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 <net.h>
13 #include <net6.h>
14 #include <vsprintf.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 
ip_to_string(struct in_addr x,char * s)155 void ip_to_string(struct in_addr x, char *s)
156 {
157 	x.s_addr = ntohl(x.s_addr);
158 	sprintf(s, "%d.%d.%d.%d",
159 		(int) ((x.s_addr >> 24) & 0xff),
160 		(int) ((x.s_addr >> 16) & 0xff),
161 		(int) ((x.s_addr >> 8) & 0xff),
162 		(int) ((x.s_addr >> 0) & 0xff)
163 	);
164 }
165 
string_to_enetaddr(const char * addr,uint8_t * enetaddr)166 void string_to_enetaddr(const char *addr, uint8_t *enetaddr)
167 {
168 	char *end;
169 	int i;
170 
171 	if (!enetaddr)
172 		return;
173 
174 	for (i = 0; i < 6; ++i) {
175 		enetaddr[i] = addr ? hextoul(addr, &end) : 0;
176 		if (addr)
177 			addr = (*end) ? end + 1 : end;
178 	}
179 }
180 
compute_ip_checksum(const void * vptr,uint nbytes)181 uint compute_ip_checksum(const void *vptr, uint nbytes)
182 {
183 	int sum, oddbyte;
184 	const unsigned short *ptr = vptr;
185 
186 	sum = 0;
187 	while (nbytes > 1) {
188 		sum += *ptr++;
189 		nbytes -= 2;
190 	}
191 	if (nbytes == 1) {
192 		oddbyte = 0;
193 		((u8 *)&oddbyte)[0] = *(u8 *)ptr;
194 		((u8 *)&oddbyte)[1] = 0;
195 		sum += oddbyte;
196 	}
197 	sum = (sum >> 16) + (sum & 0xffff);
198 	sum += (sum >> 16);
199 	sum = ~sum & 0xffff;
200 
201 	return sum;
202 }
203 
add_ip_checksums(uint offset,uint sum,uint new)204 uint add_ip_checksums(uint offset, uint sum, uint new)
205 {
206 	ulong checksum;
207 
208 	sum = ~sum & 0xffff;
209 	new = ~new & 0xffff;
210 	if (offset & 1) {
211 		/*
212 		 * byte-swap the sum if it came from an odd offset; since the
213 		 * computation is endian-independent this works.
214 		 */
215 		new = ((new >> 8) & 0xff) | ((new << 8) & 0xff00);
216 	}
217 	checksum = sum + new;
218 	if (checksum > 0xffff)
219 		checksum -= 0xffff;
220 
221 	return (~checksum) & 0xffff;
222 }
223 
ip_checksum_ok(const void * addr,uint nbytes)224 int ip_checksum_ok(const void *addr, uint nbytes)
225 {
226 	return !(compute_ip_checksum(addr, nbytes) & 0xfffe);
227 }
228