18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Generic address resultion entity 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * net_random Alan Cox 78c2ecf20Sopenharmony_ci * net_ratelimit Andi Kleen 88c2ecf20Sopenharmony_ci * in{4,6}_pton YOSHIFUJI Hideaki, Copyright (C)2006 USAGI/WIDE Project 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Created by Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/ctype.h> 178c2ecf20Sopenharmony_ci#include <linux/inet.h> 188c2ecf20Sopenharmony_ci#include <linux/mm.h> 198c2ecf20Sopenharmony_ci#include <linux/net.h> 208c2ecf20Sopenharmony_ci#include <linux/string.h> 218c2ecf20Sopenharmony_ci#include <linux/types.h> 228c2ecf20Sopenharmony_ci#include <linux/percpu.h> 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci#include <linux/ratelimit.h> 258c2ecf20Sopenharmony_ci#include <linux/socket.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <net/sock.h> 288c2ecf20Sopenharmony_ci#include <net/net_ratelimit.h> 298c2ecf20Sopenharmony_ci#include <net/ipv6.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 328c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ciDEFINE_RATELIMIT_STATE(net_ratelimit_state, 5 * HZ, 10); 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * All net warning printk()s should be guarded by this function. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ciint net_ratelimit(void) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return __ratelimit(&net_ratelimit_state); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(net_ratelimit); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * Convert an ASCII string to binary IP. 468c2ecf20Sopenharmony_ci * This is outside of net/ipv4/ because various code that uses IP addresses 478c2ecf20Sopenharmony_ci * is otherwise not dependent on the TCP/IP stack. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci__be32 in_aton(const char *str) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci unsigned int l; 538c2ecf20Sopenharmony_ci unsigned int val; 548c2ecf20Sopenharmony_ci int i; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci l = 0; 578c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 588c2ecf20Sopenharmony_ci l <<= 8; 598c2ecf20Sopenharmony_ci if (*str != '\0') { 608c2ecf20Sopenharmony_ci val = 0; 618c2ecf20Sopenharmony_ci while (*str != '\0' && *str != '.' && *str != '\n') { 628c2ecf20Sopenharmony_ci val *= 10; 638c2ecf20Sopenharmony_ci val += *str - '0'; 648c2ecf20Sopenharmony_ci str++; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci l |= val; 678c2ecf20Sopenharmony_ci if (*str != '\0') 688c2ecf20Sopenharmony_ci str++; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci return htonl(l); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(in_aton); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define IN6PTON_XDIGIT 0x00010000 768c2ecf20Sopenharmony_ci#define IN6PTON_DIGIT 0x00020000 778c2ecf20Sopenharmony_ci#define IN6PTON_COLON_MASK 0x00700000 788c2ecf20Sopenharmony_ci#define IN6PTON_COLON_1 0x00100000 /* single : requested */ 798c2ecf20Sopenharmony_ci#define IN6PTON_COLON_2 0x00200000 /* second : requested */ 808c2ecf20Sopenharmony_ci#define IN6PTON_COLON_1_2 0x00400000 /* :: requested */ 818c2ecf20Sopenharmony_ci#define IN6PTON_DOT 0x00800000 /* . */ 828c2ecf20Sopenharmony_ci#define IN6PTON_DELIM 0x10000000 838c2ecf20Sopenharmony_ci#define IN6PTON_NULL 0x20000000 /* first/tail */ 848c2ecf20Sopenharmony_ci#define IN6PTON_UNKNOWN 0x40000000 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic inline int xdigit2bin(char c, int delim) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci int val; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (c == delim || c == '\0') 918c2ecf20Sopenharmony_ci return IN6PTON_DELIM; 928c2ecf20Sopenharmony_ci if (c == ':') 938c2ecf20Sopenharmony_ci return IN6PTON_COLON_MASK; 948c2ecf20Sopenharmony_ci if (c == '.') 958c2ecf20Sopenharmony_ci return IN6PTON_DOT; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci val = hex_to_bin(c); 988c2ecf20Sopenharmony_ci if (val >= 0) 998c2ecf20Sopenharmony_ci return val | IN6PTON_XDIGIT | (val < 10 ? IN6PTON_DIGIT : 0); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (delim == -1) 1028c2ecf20Sopenharmony_ci return IN6PTON_DELIM; 1038c2ecf20Sopenharmony_ci return IN6PTON_UNKNOWN; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/** 1078c2ecf20Sopenharmony_ci * in4_pton - convert an IPv4 address from literal to binary representation 1088c2ecf20Sopenharmony_ci * @src: the start of the IPv4 address string 1098c2ecf20Sopenharmony_ci * @srclen: the length of the string, -1 means strlen(src) 1108c2ecf20Sopenharmony_ci * @dst: the binary (u8[4] array) representation of the IPv4 address 1118c2ecf20Sopenharmony_ci * @delim: the delimiter of the IPv4 address in @src, -1 means no delimiter 1128c2ecf20Sopenharmony_ci * @end: A pointer to the end of the parsed string will be placed here 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * Return one on success, return zero when any error occurs 1158c2ecf20Sopenharmony_ci * and @end will point to the end of the parsed string. 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ciint in4_pton(const char *src, int srclen, 1198c2ecf20Sopenharmony_ci u8 *dst, 1208c2ecf20Sopenharmony_ci int delim, const char **end) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci const char *s; 1238c2ecf20Sopenharmony_ci u8 *d; 1248c2ecf20Sopenharmony_ci u8 dbuf[4]; 1258c2ecf20Sopenharmony_ci int ret = 0; 1268c2ecf20Sopenharmony_ci int i; 1278c2ecf20Sopenharmony_ci int w = 0; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (srclen < 0) 1308c2ecf20Sopenharmony_ci srclen = strlen(src); 1318c2ecf20Sopenharmony_ci s = src; 1328c2ecf20Sopenharmony_ci d = dbuf; 1338c2ecf20Sopenharmony_ci i = 0; 1348c2ecf20Sopenharmony_ci while (1) { 1358c2ecf20Sopenharmony_ci int c; 1368c2ecf20Sopenharmony_ci c = xdigit2bin(srclen > 0 ? *s : '\0', delim); 1378c2ecf20Sopenharmony_ci if (!(c & (IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK))) { 1388c2ecf20Sopenharmony_ci goto out; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci if (c & (IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK)) { 1418c2ecf20Sopenharmony_ci if (w == 0) 1428c2ecf20Sopenharmony_ci goto out; 1438c2ecf20Sopenharmony_ci *d++ = w & 0xff; 1448c2ecf20Sopenharmony_ci w = 0; 1458c2ecf20Sopenharmony_ci i++; 1468c2ecf20Sopenharmony_ci if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { 1478c2ecf20Sopenharmony_ci if (i != 4) 1488c2ecf20Sopenharmony_ci goto out; 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci goto cont; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci w = (w * 10) + c; 1548c2ecf20Sopenharmony_ci if ((w & 0xffff) > 255) { 1558c2ecf20Sopenharmony_ci goto out; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_cicont: 1588c2ecf20Sopenharmony_ci if (i >= 4) 1598c2ecf20Sopenharmony_ci goto out; 1608c2ecf20Sopenharmony_ci s++; 1618c2ecf20Sopenharmony_ci srclen--; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci ret = 1; 1648c2ecf20Sopenharmony_ci memcpy(dst, dbuf, sizeof(dbuf)); 1658c2ecf20Sopenharmony_ciout: 1668c2ecf20Sopenharmony_ci if (end) 1678c2ecf20Sopenharmony_ci *end = s; 1688c2ecf20Sopenharmony_ci return ret; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(in4_pton); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/** 1738c2ecf20Sopenharmony_ci * in6_pton - convert an IPv6 address from literal to binary representation 1748c2ecf20Sopenharmony_ci * @src: the start of the IPv6 address string 1758c2ecf20Sopenharmony_ci * @srclen: the length of the string, -1 means strlen(src) 1768c2ecf20Sopenharmony_ci * @dst: the binary (u8[16] array) representation of the IPv6 address 1778c2ecf20Sopenharmony_ci * @delim: the delimiter of the IPv6 address in @src, -1 means no delimiter 1788c2ecf20Sopenharmony_ci * @end: A pointer to the end of the parsed string will be placed here 1798c2ecf20Sopenharmony_ci * 1808c2ecf20Sopenharmony_ci * Return one on success, return zero when any error occurs 1818c2ecf20Sopenharmony_ci * and @end will point to the end of the parsed string. 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ciint in6_pton(const char *src, int srclen, 1858c2ecf20Sopenharmony_ci u8 *dst, 1868c2ecf20Sopenharmony_ci int delim, const char **end) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci const char *s, *tok = NULL; 1898c2ecf20Sopenharmony_ci u8 *d, *dc = NULL; 1908c2ecf20Sopenharmony_ci u8 dbuf[16]; 1918c2ecf20Sopenharmony_ci int ret = 0; 1928c2ecf20Sopenharmony_ci int i; 1938c2ecf20Sopenharmony_ci int state = IN6PTON_COLON_1_2 | IN6PTON_XDIGIT | IN6PTON_NULL; 1948c2ecf20Sopenharmony_ci int w = 0; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci memset(dbuf, 0, sizeof(dbuf)); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci s = src; 1998c2ecf20Sopenharmony_ci d = dbuf; 2008c2ecf20Sopenharmony_ci if (srclen < 0) 2018c2ecf20Sopenharmony_ci srclen = strlen(src); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci while (1) { 2048c2ecf20Sopenharmony_ci int c; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci c = xdigit2bin(srclen > 0 ? *s : '\0', delim); 2078c2ecf20Sopenharmony_ci if (!(c & state)) 2088c2ecf20Sopenharmony_ci goto out; 2098c2ecf20Sopenharmony_ci if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { 2108c2ecf20Sopenharmony_ci /* process one 16-bit word */ 2118c2ecf20Sopenharmony_ci if (!(state & IN6PTON_NULL)) { 2128c2ecf20Sopenharmony_ci *d++ = (w >> 8) & 0xff; 2138c2ecf20Sopenharmony_ci *d++ = w & 0xff; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci w = 0; 2168c2ecf20Sopenharmony_ci if (c & IN6PTON_DELIM) { 2178c2ecf20Sopenharmony_ci /* We've processed last word */ 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * COLON_1 => XDIGIT 2228c2ecf20Sopenharmony_ci * COLON_2 => XDIGIT|DELIM 2238c2ecf20Sopenharmony_ci * COLON_1_2 => COLON_2 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci switch (state & IN6PTON_COLON_MASK) { 2268c2ecf20Sopenharmony_ci case IN6PTON_COLON_2: 2278c2ecf20Sopenharmony_ci dc = d; 2288c2ecf20Sopenharmony_ci state = IN6PTON_XDIGIT | IN6PTON_DELIM; 2298c2ecf20Sopenharmony_ci if (dc - dbuf >= sizeof(dbuf)) 2308c2ecf20Sopenharmony_ci state |= IN6PTON_NULL; 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci case IN6PTON_COLON_1|IN6PTON_COLON_1_2: 2338c2ecf20Sopenharmony_ci state = IN6PTON_XDIGIT | IN6PTON_COLON_2; 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci case IN6PTON_COLON_1: 2368c2ecf20Sopenharmony_ci state = IN6PTON_XDIGIT; 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci case IN6PTON_COLON_1_2: 2398c2ecf20Sopenharmony_ci state = IN6PTON_COLON_2; 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci default: 2428c2ecf20Sopenharmony_ci state = 0; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci tok = s + 1; 2458c2ecf20Sopenharmony_ci goto cont; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (c & IN6PTON_DOT) { 2498c2ecf20Sopenharmony_ci ret = in4_pton(tok ? tok : s, srclen + (int)(s - tok), d, delim, &s); 2508c2ecf20Sopenharmony_ci if (ret > 0) { 2518c2ecf20Sopenharmony_ci d += 4; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci goto out; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci w = (w << 4) | (0xff & c); 2588c2ecf20Sopenharmony_ci state = IN6PTON_COLON_1 | IN6PTON_DELIM; 2598c2ecf20Sopenharmony_ci if (!(w & 0xf000)) { 2608c2ecf20Sopenharmony_ci state |= IN6PTON_XDIGIT; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci if (!dc && d + 2 < dbuf + sizeof(dbuf)) { 2638c2ecf20Sopenharmony_ci state |= IN6PTON_COLON_1_2; 2648c2ecf20Sopenharmony_ci state &= ~IN6PTON_DELIM; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci if (d + 2 >= dbuf + sizeof(dbuf)) { 2678c2ecf20Sopenharmony_ci state &= ~(IN6PTON_COLON_1|IN6PTON_COLON_1_2); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_cicont: 2708c2ecf20Sopenharmony_ci if ((dc && d + 4 < dbuf + sizeof(dbuf)) || 2718c2ecf20Sopenharmony_ci d + 4 == dbuf + sizeof(dbuf)) { 2728c2ecf20Sopenharmony_ci state |= IN6PTON_DOT; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci if (d >= dbuf + sizeof(dbuf)) { 2758c2ecf20Sopenharmony_ci state &= ~(IN6PTON_XDIGIT|IN6PTON_COLON_MASK); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci s++; 2788c2ecf20Sopenharmony_ci srclen--; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci i = 15; d--; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (dc) { 2848c2ecf20Sopenharmony_ci while (d >= dc) 2858c2ecf20Sopenharmony_ci dst[i--] = *d--; 2868c2ecf20Sopenharmony_ci while (i >= dc - dbuf) 2878c2ecf20Sopenharmony_ci dst[i--] = 0; 2888c2ecf20Sopenharmony_ci while (i >= 0) 2898c2ecf20Sopenharmony_ci dst[i--] = *d--; 2908c2ecf20Sopenharmony_ci } else 2918c2ecf20Sopenharmony_ci memcpy(dst, dbuf, sizeof(dbuf)); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ret = 1; 2948c2ecf20Sopenharmony_ciout: 2958c2ecf20Sopenharmony_ci if (end) 2968c2ecf20Sopenharmony_ci *end = s; 2978c2ecf20Sopenharmony_ci return ret; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(in6_pton); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int inet4_pton(const char *src, u16 port_num, 3028c2ecf20Sopenharmony_ci struct sockaddr_storage *addr) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; 3058c2ecf20Sopenharmony_ci int srclen = strlen(src); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (srclen > INET_ADDRSTRLEN) 3088c2ecf20Sopenharmony_ci return -EINVAL; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (in4_pton(src, srclen, (u8 *)&addr4->sin_addr.s_addr, 3118c2ecf20Sopenharmony_ci '\n', NULL) == 0) 3128c2ecf20Sopenharmony_ci return -EINVAL; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci addr4->sin_family = AF_INET; 3158c2ecf20Sopenharmony_ci addr4->sin_port = htons(port_num); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int inet6_pton(struct net *net, const char *src, u16 port_num, 3218c2ecf20Sopenharmony_ci struct sockaddr_storage *addr) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; 3248c2ecf20Sopenharmony_ci const char *scope_delim; 3258c2ecf20Sopenharmony_ci int srclen = strlen(src); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (srclen > INET6_ADDRSTRLEN) 3288c2ecf20Sopenharmony_ci return -EINVAL; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (in6_pton(src, srclen, (u8 *)&addr6->sin6_addr.s6_addr, 3318c2ecf20Sopenharmony_ci '%', &scope_delim) == 0) 3328c2ecf20Sopenharmony_ci return -EINVAL; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (ipv6_addr_type(&addr6->sin6_addr) & IPV6_ADDR_LINKLOCAL && 3358c2ecf20Sopenharmony_ci src + srclen != scope_delim && *scope_delim == '%') { 3368c2ecf20Sopenharmony_ci struct net_device *dev; 3378c2ecf20Sopenharmony_ci char scope_id[16]; 3388c2ecf20Sopenharmony_ci size_t scope_len = min_t(size_t, sizeof(scope_id) - 1, 3398c2ecf20Sopenharmony_ci src + srclen - scope_delim - 1); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci memcpy(scope_id, scope_delim + 1, scope_len); 3428c2ecf20Sopenharmony_ci scope_id[scope_len] = '\0'; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci dev = dev_get_by_name(net, scope_id); 3458c2ecf20Sopenharmony_ci if (dev) { 3468c2ecf20Sopenharmony_ci addr6->sin6_scope_id = dev->ifindex; 3478c2ecf20Sopenharmony_ci dev_put(dev); 3488c2ecf20Sopenharmony_ci } else if (kstrtouint(scope_id, 0, &addr6->sin6_scope_id)) { 3498c2ecf20Sopenharmony_ci return -EINVAL; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci addr6->sin6_family = AF_INET6; 3548c2ecf20Sopenharmony_ci addr6->sin6_port = htons(port_num); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/** 3608c2ecf20Sopenharmony_ci * inet_pton_with_scope - convert an IPv4/IPv6 and port to socket address 3618c2ecf20Sopenharmony_ci * @net: net namespace (used for scope handling) 3628c2ecf20Sopenharmony_ci * @af: address family, AF_INET, AF_INET6 or AF_UNSPEC for either 3638c2ecf20Sopenharmony_ci * @src: the start of the address string 3648c2ecf20Sopenharmony_ci * @port: the start of the port string (or NULL for none) 3658c2ecf20Sopenharmony_ci * @addr: output socket address 3668c2ecf20Sopenharmony_ci * 3678c2ecf20Sopenharmony_ci * Return zero on success, return errno when any error occurs. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ciint inet_pton_with_scope(struct net *net, __kernel_sa_family_t af, 3708c2ecf20Sopenharmony_ci const char *src, const char *port, struct sockaddr_storage *addr) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci u16 port_num; 3738c2ecf20Sopenharmony_ci int ret = -EINVAL; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (port) { 3768c2ecf20Sopenharmony_ci if (kstrtou16(port, 0, &port_num)) 3778c2ecf20Sopenharmony_ci return -EINVAL; 3788c2ecf20Sopenharmony_ci } else { 3798c2ecf20Sopenharmony_ci port_num = 0; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci switch (af) { 3838c2ecf20Sopenharmony_ci case AF_INET: 3848c2ecf20Sopenharmony_ci ret = inet4_pton(src, port_num, addr); 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci case AF_INET6: 3878c2ecf20Sopenharmony_ci ret = inet6_pton(net, src, port_num, addr); 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci case AF_UNSPEC: 3908c2ecf20Sopenharmony_ci ret = inet4_pton(src, port_num, addr); 3918c2ecf20Sopenharmony_ci if (ret) 3928c2ecf20Sopenharmony_ci ret = inet6_pton(net, src, port_num, addr); 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci default: 3958c2ecf20Sopenharmony_ci pr_err("unexpected address family %d\n", af); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return ret; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(inet_pton_with_scope); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cibool inet_addr_is_any(struct sockaddr *addr) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci if (addr->sa_family == AF_INET6) { 4058c2ecf20Sopenharmony_ci struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr; 4068c2ecf20Sopenharmony_ci const struct sockaddr_in6 in6_any = 4078c2ecf20Sopenharmony_ci { .sin6_addr = IN6ADDR_ANY_INIT }; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (!memcmp(in6->sin6_addr.s6_addr, 4108c2ecf20Sopenharmony_ci in6_any.sin6_addr.s6_addr, 16)) 4118c2ecf20Sopenharmony_ci return true; 4128c2ecf20Sopenharmony_ci } else if (addr->sa_family == AF_INET) { 4138c2ecf20Sopenharmony_ci struct sockaddr_in *in = (struct sockaddr_in *)addr; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (in->sin_addr.s_addr == htonl(INADDR_ANY)) 4168c2ecf20Sopenharmony_ci return true; 4178c2ecf20Sopenharmony_ci } else { 4188c2ecf20Sopenharmony_ci pr_warn("unexpected address family %u\n", addr->sa_family); 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return false; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(inet_addr_is_any); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_civoid inet_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb, 4268c2ecf20Sopenharmony_ci __be32 from, __be32 to, bool pseudohdr) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) { 4298c2ecf20Sopenharmony_ci csum_replace4(sum, from, to); 4308c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr) 4318c2ecf20Sopenharmony_ci skb->csum = ~csum_add(csum_sub(~(skb->csum), 4328c2ecf20Sopenharmony_ci (__force __wsum)from), 4338c2ecf20Sopenharmony_ci (__force __wsum)to); 4348c2ecf20Sopenharmony_ci } else if (pseudohdr) 4358c2ecf20Sopenharmony_ci *sum = ~csum_fold(csum_add(csum_sub(csum_unfold(*sum), 4368c2ecf20Sopenharmony_ci (__force __wsum)from), 4378c2ecf20Sopenharmony_ci (__force __wsum)to)); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(inet_proto_csum_replace4); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/** 4428c2ecf20Sopenharmony_ci * inet_proto_csum_replace16 - update layer 4 header checksum field 4438c2ecf20Sopenharmony_ci * @sum: Layer 4 header checksum field 4448c2ecf20Sopenharmony_ci * @skb: sk_buff for the packet 4458c2ecf20Sopenharmony_ci * @from: old IPv6 address 4468c2ecf20Sopenharmony_ci * @to: new IPv6 address 4478c2ecf20Sopenharmony_ci * @pseudohdr: True if layer 4 header checksum includes pseudoheader 4488c2ecf20Sopenharmony_ci * 4498c2ecf20Sopenharmony_ci * Update layer 4 header as per the update in IPv6 src/dst address. 4508c2ecf20Sopenharmony_ci * 4518c2ecf20Sopenharmony_ci * There is no need to update skb->csum in this function, because update in two 4528c2ecf20Sopenharmony_ci * fields a.) IPv6 src/dst address and b.) L4 header checksum cancels each other 4538c2ecf20Sopenharmony_ci * for skb->csum calculation. Whereas inet_proto_csum_replace4 function needs to 4548c2ecf20Sopenharmony_ci * update skb->csum, because update in 3 fields a.) IPv4 src/dst address, 4558c2ecf20Sopenharmony_ci * b.) IPv4 Header checksum and c.) L4 header checksum results in same diff as 4568c2ecf20Sopenharmony_ci * L4 Header checksum for skb->csum calculation. 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_civoid inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb, 4598c2ecf20Sopenharmony_ci const __be32 *from, const __be32 *to, 4608c2ecf20Sopenharmony_ci bool pseudohdr) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci __be32 diff[] = { 4638c2ecf20Sopenharmony_ci ~from[0], ~from[1], ~from[2], ~from[3], 4648c2ecf20Sopenharmony_ci to[0], to[1], to[2], to[3], 4658c2ecf20Sopenharmony_ci }; 4668c2ecf20Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) { 4678c2ecf20Sopenharmony_ci *sum = csum_fold(csum_partial(diff, sizeof(diff), 4688c2ecf20Sopenharmony_ci ~csum_unfold(*sum))); 4698c2ecf20Sopenharmony_ci } else if (pseudohdr) 4708c2ecf20Sopenharmony_ci *sum = ~csum_fold(csum_partial(diff, sizeof(diff), 4718c2ecf20Sopenharmony_ci csum_unfold(*sum))); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(inet_proto_csum_replace16); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_civoid inet_proto_csum_replace_by_diff(__sum16 *sum, struct sk_buff *skb, 4768c2ecf20Sopenharmony_ci __wsum diff, bool pseudohdr) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) { 4798c2ecf20Sopenharmony_ci *sum = csum_fold(csum_add(diff, ~csum_unfold(*sum))); 4808c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr) 4818c2ecf20Sopenharmony_ci skb->csum = ~csum_add(diff, ~skb->csum); 4828c2ecf20Sopenharmony_ci } else if (pseudohdr) { 4838c2ecf20Sopenharmony_ci *sum = ~csum_fold(csum_add(diff, csum_unfold(*sum))); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(inet_proto_csum_replace_by_diff); 487