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 
string_to_ip(const char * s)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)
string_to_ip6(const char * str,size_t len,struct in6_addr * addr)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 
string_to_enetaddr(const char * addr,uint8_t * enetaddr)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 
compute_ip_checksum(const void * vptr,uint nbytes)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 
add_ip_checksums(uint offset,uint sum,uint new)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 
ip_checksum_ok(const void * addr,uint nbytes)213 int ip_checksum_ok(const void *addr, uint nbytes)
214 {
215 	return !(compute_ip_checksum(addr, nbytes) & 0xfffe);
216 }
217