Merge tag 'doc-2023-10-rc5-3' of https://source.denx.de/u-boot/custodians/u-boot-efi
[platform/kernel/u-boot.git] / lib / net_utils.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Generic network code. Moved from net.c
4  *
5  * Copyright 1994 - 2000 Neil Russell.
6  * Copyright 2000 Roland Borde
7  * Copyright 2000 Paolo Scaffardi
8  * Copyright 2000-2002 Wolfgang Denk, wd@denx.de
9  * Copyright 2009 Dirk Behme, dirk.behme@googlemail.com
10  */
11
12 #include <common.h>
13 #include <net.h>
14 #include <net6.h>
15
16 struct in_addr string_to_ip(const char *s)
17 {
18         struct in_addr addr;
19         char *e;
20         int i;
21
22         addr.s_addr = 0;
23         if (s == NULL)
24                 return addr;
25
26         for (addr.s_addr = 0, i = 0; i < 4; ++i) {
27                 ulong val = s ? dectoul(s, &e) : 0;
28                 if (val > 255) {
29                         addr.s_addr = 0;
30                         return addr;
31                 }
32                 if (i != 3 && *e != '.') {
33                         addr.s_addr = 0;
34                         return addr;
35                 }
36                 addr.s_addr <<= 8;
37                 addr.s_addr |= (val & 0xFF);
38                 if (s) {
39                         s = (*e) ? e+1 : e;
40                 }
41         }
42
43         addr.s_addr = htonl(addr.s_addr);
44         return addr;
45 }
46
47 #if IS_ENABLED(CONFIG_IPV6)
48 int string_to_ip6(const char *str, size_t len, struct in6_addr *addr)
49 {
50         int colon_count = 0;
51         int found_double_colon = 0;
52         int xstart = 0;         /* first zero (double colon) */
53         int section_num = 7;    /* num words the double colon represents */
54         int i;
55         const char *s = str;
56         const char *const e = s + len;
57         struct in_addr zero_ip = {.s_addr = 0};
58
59         if (!str)
60                 return -1;
61
62         /* First pass, verify the syntax and locate the double colon */
63         while (s < e) {
64                 while (s < e && isxdigit((int)*s))
65                         s++;
66                 if (*s == '\0')
67                         break;
68                 if (*s != ':') {
69                         if (*s == '.' && section_num >= 2) {
70                                 struct in_addr v4;
71
72                                 while (s != str && *(s - 1) != ':')
73                                         --s;
74                                 v4 = string_to_ip(s);
75                                 if (memcmp(&zero_ip, &v4,
76                                            sizeof(struct in_addr)) != 0) {
77                                         section_num -= 2;
78                                         break;
79                                 }
80                         }
81                         /* This could be a valid address */
82                         break;
83                 }
84                 if (s == str) {
85                         /* The address begins with a colon */
86                         if (*++s != ':')
87                                 /* Must start with a double colon or a number */
88                                 goto out_err;
89                 } else {
90                         s++;
91                         if (found_double_colon)
92                                 section_num--;
93                         else
94                                 xstart++;
95                 }
96
97                 if (*s == ':') {
98                         if (found_double_colon)
99                                 /* Two double colons are not allowed */
100                                 goto out_err;
101                         found_double_colon = 1;
102                         section_num -= xstart;
103                         s++;
104                 }
105
106                 if (++colon_count == 7)
107                         /* Found all colons */
108                         break;
109                 ++s;
110         }
111
112         if (colon_count == 0)
113                 goto out_err;
114         if (*--s == ':')
115                 section_num++;
116
117         /* Second pass, read the address */
118         s = str;
119         for (i = 0; i < 8; i++) {
120                 int val = 0;
121                 char *end;
122
123                 if (found_double_colon &&
124                     i >= xstart && i < xstart + section_num) {
125                         addr->s6_addr16[i] = 0;
126                         continue;
127                 }
128                 while (*s == ':')
129                         s++;
130
131                 if (i == 6 && isdigit((int)*s)) {
132                         struct in_addr v4 = string_to_ip(s);
133
134                         if (memcmp(&zero_ip, &v4,
135                                    sizeof(struct in_addr)) != 0) {
136                                 /* Ending with :IPv4-address */
137                                 addr->s6_addr32[3] = v4.s_addr;
138                                 break;
139                         }
140                 }
141
142                 val = simple_strtoul(s, &end, 16);
143                 if (end != e && *end != '\0' && *end != ':')
144                         goto out_err;
145                 addr->s6_addr16[i] = htons(val);
146                 s = end;
147         }
148         return 0;
149
150 out_err:
151         return -1;
152 }
153 #endif
154
155 void string_to_enetaddr(const char *addr, uint8_t *enetaddr)
156 {
157         char *end;
158         int i;
159
160         if (!enetaddr)
161                 return;
162
163         for (i = 0; i < 6; ++i) {
164                 enetaddr[i] = addr ? hextoul(addr, &end) : 0;
165                 if (addr)
166                         addr = (*end) ? end + 1 : end;
167         }
168 }
169
170 uint compute_ip_checksum(const void *vptr, uint nbytes)
171 {
172         int sum, oddbyte;
173         const unsigned short *ptr = vptr;
174
175         sum = 0;
176         while (nbytes > 1) {
177                 sum += *ptr++;
178                 nbytes -= 2;
179         }
180         if (nbytes == 1) {
181                 oddbyte = 0;
182                 ((u8 *)&oddbyte)[0] = *(u8 *)ptr;
183                 ((u8 *)&oddbyte)[1] = 0;
184                 sum += oddbyte;
185         }
186         sum = (sum >> 16) + (sum & 0xffff);
187         sum += (sum >> 16);
188         sum = ~sum & 0xffff;
189
190         return sum;
191 }
192
193 uint add_ip_checksums(uint offset, uint sum, uint new)
194 {
195         ulong checksum;
196
197         sum = ~sum & 0xffff;
198         new = ~new & 0xffff;
199         if (offset & 1) {
200                 /*
201                  * byte-swap the sum if it came from an odd offset; since the
202                  * computation is endian-independent this works.
203                  */
204                 new = ((new >> 8) & 0xff) | ((new << 8) & 0xff00);
205         }
206         checksum = sum + new;
207         if (checksum > 0xffff)
208                 checksum -= 0xffff;
209
210         return (~checksum) & 0xffff;
211 }
212
213 int ip_checksum_ok(const void *addr, uint nbytes)
214 {
215         return !(compute_ip_checksum(addr, nbytes) & 0xfffe);
216 }