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