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
16struct 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)
48int 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
150out_err:
151	return -1;
152}
153#endif
154
155void 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
170uint 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
193uint 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
213int ip_checksum_ok(const void *addr, uint nbytes)
214{
215	return !(compute_ip_checksum(addr, nbytes) & 0xfffe);
216}
217