18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Neighbour Discovery for IPv6 48c2ecf20Sopenharmony_ci * Linux INET6 implementation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 88c2ecf20Sopenharmony_ci * Mike Shaver <shaver@ingenia.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * Changes: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Alexey I. Froloff : RFC6106 (DNSSL) support 158c2ecf20Sopenharmony_ci * Pierre Ynard : export userland ND options 168c2ecf20Sopenharmony_ci * through netlink (RDNSS support) 178c2ecf20Sopenharmony_ci * Lars Fenneberg : fixed MTU setting on receipt 188c2ecf20Sopenharmony_ci * of an RA. 198c2ecf20Sopenharmony_ci * Janos Farkas : kmalloc failure checks 208c2ecf20Sopenharmony_ci * Alexey Kuznetsov : state machine reworked 218c2ecf20Sopenharmony_ci * and moved to net/core. 228c2ecf20Sopenharmony_ci * Pekka Savola : RFC2461 validation 238c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI : Verify ND options properly 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ICMPv6: " fmt 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <linux/module.h> 298c2ecf20Sopenharmony_ci#include <linux/errno.h> 308c2ecf20Sopenharmony_ci#include <linux/types.h> 318c2ecf20Sopenharmony_ci#include <linux/socket.h> 328c2ecf20Sopenharmony_ci#include <linux/sockios.h> 338c2ecf20Sopenharmony_ci#include <linux/sched.h> 348c2ecf20Sopenharmony_ci#include <linux/net.h> 358c2ecf20Sopenharmony_ci#include <linux/in6.h> 368c2ecf20Sopenharmony_ci#include <linux/route.h> 378c2ecf20Sopenharmony_ci#include <linux/init.h> 388c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 398c2ecf20Sopenharmony_ci#include <linux/slab.h> 408c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 418c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 428c2ecf20Sopenharmony_ci#endif 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#include <linux/if_addr.h> 458c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 468c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 478c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 488c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 498c2ecf20Sopenharmony_ci#include <linux/jhash.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include <net/sock.h> 528c2ecf20Sopenharmony_ci#include <net/snmp.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include <net/ipv6.h> 558c2ecf20Sopenharmony_ci#include <net/protocol.h> 568c2ecf20Sopenharmony_ci#include <net/ndisc.h> 578c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 588c2ecf20Sopenharmony_ci#include <net/addrconf.h> 598c2ecf20Sopenharmony_ci#include <net/icmp.h> 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#include <net/netlink.h> 628c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#include <net/flow.h> 658c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 668c2ecf20Sopenharmony_ci#include <net/inet_common.h> 678c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#include <linux/netfilter.h> 708c2ecf20Sopenharmony_ci#include <linux/netfilter_ipv6.h> 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic u32 ndisc_hash(const void *pkey, 738c2ecf20Sopenharmony_ci const struct net_device *dev, 748c2ecf20Sopenharmony_ci __u32 *hash_rnd); 758c2ecf20Sopenharmony_cistatic bool ndisc_key_eq(const struct neighbour *neigh, const void *pkey); 768c2ecf20Sopenharmony_cistatic bool ndisc_allow_add(const struct net_device *dev, 778c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack); 788c2ecf20Sopenharmony_cistatic int ndisc_constructor(struct neighbour *neigh); 798c2ecf20Sopenharmony_cistatic void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb); 808c2ecf20Sopenharmony_cistatic void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb); 818c2ecf20Sopenharmony_cistatic int pndisc_constructor(struct pneigh_entry *n); 828c2ecf20Sopenharmony_cistatic void pndisc_destructor(struct pneigh_entry *n); 838c2ecf20Sopenharmony_cistatic void pndisc_redo(struct sk_buff *skb); 848c2ecf20Sopenharmony_cistatic int ndisc_is_multicast(const void *pkey); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic const struct neigh_ops ndisc_generic_ops = { 878c2ecf20Sopenharmony_ci .family = AF_INET6, 888c2ecf20Sopenharmony_ci .solicit = ndisc_solicit, 898c2ecf20Sopenharmony_ci .error_report = ndisc_error_report, 908c2ecf20Sopenharmony_ci .output = neigh_resolve_output, 918c2ecf20Sopenharmony_ci .connected_output = neigh_connected_output, 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic const struct neigh_ops ndisc_hh_ops = { 958c2ecf20Sopenharmony_ci .family = AF_INET6, 968c2ecf20Sopenharmony_ci .solicit = ndisc_solicit, 978c2ecf20Sopenharmony_ci .error_report = ndisc_error_report, 988c2ecf20Sopenharmony_ci .output = neigh_resolve_output, 998c2ecf20Sopenharmony_ci .connected_output = neigh_resolve_output, 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic const struct neigh_ops ndisc_direct_ops = { 1048c2ecf20Sopenharmony_ci .family = AF_INET6, 1058c2ecf20Sopenharmony_ci .output = neigh_direct_output, 1068c2ecf20Sopenharmony_ci .connected_output = neigh_direct_output, 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistruct neigh_table nd_tbl = { 1108c2ecf20Sopenharmony_ci .family = AF_INET6, 1118c2ecf20Sopenharmony_ci .key_len = sizeof(struct in6_addr), 1128c2ecf20Sopenharmony_ci .protocol = cpu_to_be16(ETH_P_IPV6), 1138c2ecf20Sopenharmony_ci .hash = ndisc_hash, 1148c2ecf20Sopenharmony_ci .key_eq = ndisc_key_eq, 1158c2ecf20Sopenharmony_ci .constructor = ndisc_constructor, 1168c2ecf20Sopenharmony_ci .pconstructor = pndisc_constructor, 1178c2ecf20Sopenharmony_ci .pdestructor = pndisc_destructor, 1188c2ecf20Sopenharmony_ci .proxy_redo = pndisc_redo, 1198c2ecf20Sopenharmony_ci .is_multicast = ndisc_is_multicast, 1208c2ecf20Sopenharmony_ci .allow_add = ndisc_allow_add, 1218c2ecf20Sopenharmony_ci .id = "ndisc_cache", 1228c2ecf20Sopenharmony_ci .parms = { 1238c2ecf20Sopenharmony_ci .tbl = &nd_tbl, 1248c2ecf20Sopenharmony_ci .reachable_time = ND_REACHABLE_TIME, 1258c2ecf20Sopenharmony_ci .data = { 1268c2ecf20Sopenharmony_ci [NEIGH_VAR_MCAST_PROBES] = 3, 1278c2ecf20Sopenharmony_ci [NEIGH_VAR_UCAST_PROBES] = 3, 1288c2ecf20Sopenharmony_ci [NEIGH_VAR_RETRANS_TIME] = ND_RETRANS_TIMER, 1298c2ecf20Sopenharmony_ci [NEIGH_VAR_BASE_REACHABLE_TIME] = ND_REACHABLE_TIME, 1308c2ecf20Sopenharmony_ci [NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ, 1318c2ecf20Sopenharmony_ci [NEIGH_VAR_GC_STALETIME] = 60 * HZ, 1328c2ecf20Sopenharmony_ci [NEIGH_VAR_QUEUE_LEN_BYTES] = SK_WMEM_MAX, 1338c2ecf20Sopenharmony_ci [NEIGH_VAR_PROXY_QLEN] = 64, 1348c2ecf20Sopenharmony_ci [NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ, 1358c2ecf20Sopenharmony_ci [NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10, 1368c2ecf20Sopenharmony_ci }, 1378c2ecf20Sopenharmony_ci }, 1388c2ecf20Sopenharmony_ci .gc_interval = 30 * HZ, 1398c2ecf20Sopenharmony_ci .gc_thresh1 = 128, 1408c2ecf20Sopenharmony_ci .gc_thresh2 = 512, 1418c2ecf20Sopenharmony_ci .gc_thresh3 = 1024, 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nd_tbl); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_civoid __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data, 1468c2ecf20Sopenharmony_ci int data_len, int pad) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci int space = __ndisc_opt_addr_space(data_len, pad); 1498c2ecf20Sopenharmony_ci u8 *opt = skb_put(skb, space); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci opt[0] = type; 1528c2ecf20Sopenharmony_ci opt[1] = space>>3; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci memset(opt + 2, 0, pad); 1558c2ecf20Sopenharmony_ci opt += pad; 1568c2ecf20Sopenharmony_ci space -= pad; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci memcpy(opt+2, data, data_len); 1598c2ecf20Sopenharmony_ci data_len += 2; 1608c2ecf20Sopenharmony_ci opt += data_len; 1618c2ecf20Sopenharmony_ci space -= data_len; 1628c2ecf20Sopenharmony_ci if (space > 0) 1638c2ecf20Sopenharmony_ci memset(opt, 0, space); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__ndisc_fill_addr_option); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic inline void ndisc_fill_addr_option(struct sk_buff *skb, int type, 1688c2ecf20Sopenharmony_ci void *data, u8 icmp6_type) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci __ndisc_fill_addr_option(skb, type, data, skb->dev->addr_len, 1718c2ecf20Sopenharmony_ci ndisc_addr_option_pad(skb->dev->type)); 1728c2ecf20Sopenharmony_ci ndisc_ops_fill_addr_option(skb->dev, skb, icmp6_type); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic inline void ndisc_fill_redirect_addr_option(struct sk_buff *skb, 1768c2ecf20Sopenharmony_ci void *ha, 1778c2ecf20Sopenharmony_ci const u8 *ops_data) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, ha, NDISC_REDIRECT); 1808c2ecf20Sopenharmony_ci ndisc_ops_fill_redirect_addr_option(skb->dev, skb, ops_data); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, 1848c2ecf20Sopenharmony_ci struct nd_opt_hdr *end) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci int type; 1878c2ecf20Sopenharmony_ci if (!cur || !end || cur >= end) 1888c2ecf20Sopenharmony_ci return NULL; 1898c2ecf20Sopenharmony_ci type = cur->nd_opt_type; 1908c2ecf20Sopenharmony_ci do { 1918c2ecf20Sopenharmony_ci cur = ((void *)cur) + (cur->nd_opt_len << 3); 1928c2ecf20Sopenharmony_ci } while (cur < end && cur->nd_opt_type != type); 1938c2ecf20Sopenharmony_ci return cur <= end && cur->nd_opt_type == type ? cur : NULL; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic inline int ndisc_is_useropt(const struct net_device *dev, 1978c2ecf20Sopenharmony_ci struct nd_opt_hdr *opt) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci return opt->nd_opt_type == ND_OPT_PREFIX_INFO || 2008c2ecf20Sopenharmony_ci opt->nd_opt_type == ND_OPT_RDNSS || 2018c2ecf20Sopenharmony_ci opt->nd_opt_type == ND_OPT_DNSSL || 2028c2ecf20Sopenharmony_ci opt->nd_opt_type == ND_OPT_CAPTIVE_PORTAL || 2038c2ecf20Sopenharmony_ci opt->nd_opt_type == ND_OPT_PREF64 || 2048c2ecf20Sopenharmony_ci ndisc_ops_is_useropt(dev, opt->nd_opt_type); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic struct nd_opt_hdr *ndisc_next_useropt(const struct net_device *dev, 2088c2ecf20Sopenharmony_ci struct nd_opt_hdr *cur, 2098c2ecf20Sopenharmony_ci struct nd_opt_hdr *end) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci if (!cur || !end || cur >= end) 2128c2ecf20Sopenharmony_ci return NULL; 2138c2ecf20Sopenharmony_ci do { 2148c2ecf20Sopenharmony_ci cur = ((void *)cur) + (cur->nd_opt_len << 3); 2158c2ecf20Sopenharmony_ci } while (cur < end && !ndisc_is_useropt(dev, cur)); 2168c2ecf20Sopenharmony_ci return cur <= end && ndisc_is_useropt(dev, cur) ? cur : NULL; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistruct ndisc_options *ndisc_parse_options(const struct net_device *dev, 2208c2ecf20Sopenharmony_ci u8 *opt, int opt_len, 2218c2ecf20Sopenharmony_ci struct ndisc_options *ndopts) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (!nd_opt || opt_len < 0 || !ndopts) 2268c2ecf20Sopenharmony_ci return NULL; 2278c2ecf20Sopenharmony_ci memset(ndopts, 0, sizeof(*ndopts)); 2288c2ecf20Sopenharmony_ci while (opt_len) { 2298c2ecf20Sopenharmony_ci int l; 2308c2ecf20Sopenharmony_ci if (opt_len < sizeof(struct nd_opt_hdr)) 2318c2ecf20Sopenharmony_ci return NULL; 2328c2ecf20Sopenharmony_ci l = nd_opt->nd_opt_len << 3; 2338c2ecf20Sopenharmony_ci if (opt_len < l || l == 0) 2348c2ecf20Sopenharmony_ci return NULL; 2358c2ecf20Sopenharmony_ci if (ndisc_ops_parse_options(dev, nd_opt, ndopts)) 2368c2ecf20Sopenharmony_ci goto next_opt; 2378c2ecf20Sopenharmony_ci switch (nd_opt->nd_opt_type) { 2388c2ecf20Sopenharmony_ci case ND_OPT_SOURCE_LL_ADDR: 2398c2ecf20Sopenharmony_ci case ND_OPT_TARGET_LL_ADDR: 2408c2ecf20Sopenharmony_ci case ND_OPT_MTU: 2418c2ecf20Sopenharmony_ci case ND_OPT_NONCE: 2428c2ecf20Sopenharmony_ci case ND_OPT_REDIRECT_HDR: 2438c2ecf20Sopenharmony_ci if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { 2448c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, 2458c2ecf20Sopenharmony_ci "%s: duplicated ND6 option found: type=%d\n", 2468c2ecf20Sopenharmony_ci __func__, nd_opt->nd_opt_type); 2478c2ecf20Sopenharmony_ci } else { 2488c2ecf20Sopenharmony_ci ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci case ND_OPT_PREFIX_INFO: 2528c2ecf20Sopenharmony_ci ndopts->nd_opts_pi_end = nd_opt; 2538c2ecf20Sopenharmony_ci if (!ndopts->nd_opt_array[nd_opt->nd_opt_type]) 2548c2ecf20Sopenharmony_ci ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt; 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 2578c2ecf20Sopenharmony_ci case ND_OPT_ROUTE_INFO: 2588c2ecf20Sopenharmony_ci ndopts->nd_opts_ri_end = nd_opt; 2598c2ecf20Sopenharmony_ci if (!ndopts->nd_opts_ri) 2608c2ecf20Sopenharmony_ci ndopts->nd_opts_ri = nd_opt; 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci#endif 2638c2ecf20Sopenharmony_ci default: 2648c2ecf20Sopenharmony_ci if (ndisc_is_useropt(dev, nd_opt)) { 2658c2ecf20Sopenharmony_ci ndopts->nd_useropts_end = nd_opt; 2668c2ecf20Sopenharmony_ci if (!ndopts->nd_useropts) 2678c2ecf20Sopenharmony_ci ndopts->nd_useropts = nd_opt; 2688c2ecf20Sopenharmony_ci } else { 2698c2ecf20Sopenharmony_ci /* 2708c2ecf20Sopenharmony_ci * Unknown options must be silently ignored, 2718c2ecf20Sopenharmony_ci * to accommodate future extension to the 2728c2ecf20Sopenharmony_ci * protocol. 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ci ND_PRINTK(2, notice, 2758c2ecf20Sopenharmony_ci "%s: ignored unsupported option; type=%d, len=%d\n", 2768c2ecf20Sopenharmony_ci __func__, 2778c2ecf20Sopenharmony_ci nd_opt->nd_opt_type, 2788c2ecf20Sopenharmony_ci nd_opt->nd_opt_len); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_cinext_opt: 2828c2ecf20Sopenharmony_ci opt_len -= l; 2838c2ecf20Sopenharmony_ci nd_opt = ((void *)nd_opt) + l; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci return ndopts; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ciint ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci switch (dev->type) { 2918c2ecf20Sopenharmony_ci case ARPHRD_ETHER: 2928c2ecf20Sopenharmony_ci case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */ 2938c2ecf20Sopenharmony_ci case ARPHRD_FDDI: 2948c2ecf20Sopenharmony_ci ipv6_eth_mc_map(addr, buf); 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci case ARPHRD_ARCNET: 2978c2ecf20Sopenharmony_ci ipv6_arcnet_mc_map(addr, buf); 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci case ARPHRD_INFINIBAND: 3008c2ecf20Sopenharmony_ci ipv6_ib_mc_map(addr, dev->broadcast, buf); 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci case ARPHRD_IPGRE: 3038c2ecf20Sopenharmony_ci return ipv6_ipgre_mc_map(addr, dev->broadcast, buf); 3048c2ecf20Sopenharmony_ci default: 3058c2ecf20Sopenharmony_ci if (dir) { 3068c2ecf20Sopenharmony_ci memcpy(buf, dev->broadcast, dev->addr_len); 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci return -EINVAL; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ndisc_mc_map); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic u32 ndisc_hash(const void *pkey, 3158c2ecf20Sopenharmony_ci const struct net_device *dev, 3168c2ecf20Sopenharmony_ci __u32 *hash_rnd) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci return ndisc_hashfn(pkey, dev, hash_rnd); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic bool ndisc_key_eq(const struct neighbour *n, const void *pkey) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci return neigh_key_eq128(n, pkey); 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic int ndisc_constructor(struct neighbour *neigh) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct in6_addr *addr = (struct in6_addr *)&neigh->primary_key; 3298c2ecf20Sopenharmony_ci struct net_device *dev = neigh->dev; 3308c2ecf20Sopenharmony_ci struct inet6_dev *in6_dev; 3318c2ecf20Sopenharmony_ci struct neigh_parms *parms; 3328c2ecf20Sopenharmony_ci bool is_multicast = ipv6_addr_is_multicast(addr); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci in6_dev = in6_dev_get(dev); 3358c2ecf20Sopenharmony_ci if (!in6_dev) { 3368c2ecf20Sopenharmony_ci return -EINVAL; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci parms = in6_dev->nd_parms; 3408c2ecf20Sopenharmony_ci __neigh_parms_put(neigh->parms); 3418c2ecf20Sopenharmony_ci neigh->parms = neigh_parms_clone(parms); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST; 3448c2ecf20Sopenharmony_ci if (!dev->header_ops) { 3458c2ecf20Sopenharmony_ci neigh->nud_state = NUD_NOARP; 3468c2ecf20Sopenharmony_ci neigh->ops = &ndisc_direct_ops; 3478c2ecf20Sopenharmony_ci neigh->output = neigh_direct_output; 3488c2ecf20Sopenharmony_ci } else { 3498c2ecf20Sopenharmony_ci if (is_multicast) { 3508c2ecf20Sopenharmony_ci neigh->nud_state = NUD_NOARP; 3518c2ecf20Sopenharmony_ci ndisc_mc_map(addr, neigh->ha, dev, 1); 3528c2ecf20Sopenharmony_ci } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) { 3538c2ecf20Sopenharmony_ci neigh->nud_state = NUD_NOARP; 3548c2ecf20Sopenharmony_ci memcpy(neigh->ha, dev->dev_addr, dev->addr_len); 3558c2ecf20Sopenharmony_ci if (dev->flags&IFF_LOOPBACK) 3568c2ecf20Sopenharmony_ci neigh->type = RTN_LOCAL; 3578c2ecf20Sopenharmony_ci } else if (dev->flags&IFF_POINTOPOINT) { 3588c2ecf20Sopenharmony_ci neigh->nud_state = NUD_NOARP; 3598c2ecf20Sopenharmony_ci memcpy(neigh->ha, dev->broadcast, dev->addr_len); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci if (dev->header_ops->cache) 3628c2ecf20Sopenharmony_ci neigh->ops = &ndisc_hh_ops; 3638c2ecf20Sopenharmony_ci else 3648c2ecf20Sopenharmony_ci neigh->ops = &ndisc_generic_ops; 3658c2ecf20Sopenharmony_ci if (neigh->nud_state&NUD_VALID) 3668c2ecf20Sopenharmony_ci neigh->output = neigh->ops->connected_output; 3678c2ecf20Sopenharmony_ci else 3688c2ecf20Sopenharmony_ci neigh->output = neigh->ops->output; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci in6_dev_put(in6_dev); 3718c2ecf20Sopenharmony_ci return 0; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int pndisc_constructor(struct pneigh_entry *n) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct in6_addr *addr = (struct in6_addr *)&n->key; 3778c2ecf20Sopenharmony_ci struct in6_addr maddr; 3788c2ecf20Sopenharmony_ci struct net_device *dev = n->dev; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (!dev || !__in6_dev_get(dev)) 3818c2ecf20Sopenharmony_ci return -EINVAL; 3828c2ecf20Sopenharmony_ci addrconf_addr_solict_mult(addr, &maddr); 3838c2ecf20Sopenharmony_ci ipv6_dev_mc_inc(dev, &maddr); 3848c2ecf20Sopenharmony_ci return 0; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic void pndisc_destructor(struct pneigh_entry *n) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct in6_addr *addr = (struct in6_addr *)&n->key; 3908c2ecf20Sopenharmony_ci struct in6_addr maddr; 3918c2ecf20Sopenharmony_ci struct net_device *dev = n->dev; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (!dev || !__in6_dev_get(dev)) 3948c2ecf20Sopenharmony_ci return; 3958c2ecf20Sopenharmony_ci addrconf_addr_solict_mult(addr, &maddr); 3968c2ecf20Sopenharmony_ci ipv6_dev_mc_dec(dev, &maddr); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/* called with rtnl held */ 4008c2ecf20Sopenharmony_cistatic bool ndisc_allow_add(const struct net_device *dev, 4018c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (!idev || idev->cnf.disable_ipv6) { 4068c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "IPv6 is disabled on this device"); 4078c2ecf20Sopenharmony_ci return false; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return true; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic struct sk_buff *ndisc_alloc_skb(struct net_device *dev, 4148c2ecf20Sopenharmony_ci int len) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci int hlen = LL_RESERVED_SPACE(dev); 4178c2ecf20Sopenharmony_ci int tlen = dev->needed_tailroom; 4188c2ecf20Sopenharmony_ci struct sock *sk = dev_net(dev)->ipv6.ndisc_sk; 4198c2ecf20Sopenharmony_ci struct sk_buff *skb; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci skb = alloc_skb(hlen + sizeof(struct ipv6hdr) + len + tlen, GFP_ATOMIC); 4228c2ecf20Sopenharmony_ci if (!skb) { 4238c2ecf20Sopenharmony_ci ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb\n", 4248c2ecf20Sopenharmony_ci __func__); 4258c2ecf20Sopenharmony_ci return NULL; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 4298c2ecf20Sopenharmony_ci skb->dev = dev; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci skb_reserve(skb, hlen + sizeof(struct ipv6hdr)); 4328c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Manually assign socket ownership as we avoid calling 4358c2ecf20Sopenharmony_ci * sock_alloc_send_pskb() to bypass wmem buffer limits 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_ci skb_set_owner_w(skb, sk); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return skb; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic void ip6_nd_hdr(struct sk_buff *skb, 4438c2ecf20Sopenharmony_ci const struct in6_addr *saddr, 4448c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 4458c2ecf20Sopenharmony_ci int hop_limit, int len) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct ipv6hdr *hdr; 4488c2ecf20Sopenharmony_ci struct inet6_dev *idev; 4498c2ecf20Sopenharmony_ci unsigned tclass; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci rcu_read_lock(); 4528c2ecf20Sopenharmony_ci idev = __in6_dev_get(skb->dev); 4538c2ecf20Sopenharmony_ci tclass = idev ? idev->cnf.ndisc_tclass : 0; 4548c2ecf20Sopenharmony_ci rcu_read_unlock(); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci skb_push(skb, sizeof(*hdr)); 4578c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 4588c2ecf20Sopenharmony_ci hdr = ipv6_hdr(skb); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci ip6_flow_hdr(hdr, tclass, 0); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci hdr->payload_len = htons(len); 4638c2ecf20Sopenharmony_ci hdr->nexthdr = IPPROTO_ICMPV6; 4648c2ecf20Sopenharmony_ci hdr->hop_limit = hop_limit; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci hdr->saddr = *saddr; 4678c2ecf20Sopenharmony_ci hdr->daddr = *daddr; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic void ndisc_send_skb(struct sk_buff *skb, 4718c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 4728c2ecf20Sopenharmony_ci const struct in6_addr *saddr) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 4758c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 4768c2ecf20Sopenharmony_ci struct sock *sk = net->ipv6.ndisc_sk; 4778c2ecf20Sopenharmony_ci struct inet6_dev *idev; 4788c2ecf20Sopenharmony_ci int err; 4798c2ecf20Sopenharmony_ci struct icmp6hdr *icmp6h = icmp6_hdr(skb); 4808c2ecf20Sopenharmony_ci u8 type; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci type = icmp6h->icmp6_type; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (!dst) { 4858c2ecf20Sopenharmony_ci struct flowi6 fl6; 4868c2ecf20Sopenharmony_ci int oif = skb->dev->ifindex; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci icmpv6_flow_init(sk, &fl6, type, saddr, daddr, oif); 4898c2ecf20Sopenharmony_ci dst = icmp6_dst_alloc(skb->dev, &fl6); 4908c2ecf20Sopenharmony_ci if (IS_ERR(dst)) { 4918c2ecf20Sopenharmony_ci kfree_skb(skb); 4928c2ecf20Sopenharmony_ci return; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci skb_dst_set(skb, dst); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len, 4998c2ecf20Sopenharmony_ci IPPROTO_ICMPV6, 5008c2ecf20Sopenharmony_ci csum_partial(icmp6h, 5018c2ecf20Sopenharmony_ci skb->len, 0)); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci rcu_read_lock(); 5068c2ecf20Sopenharmony_ci idev = __in6_dev_get(dst->dev); 5078c2ecf20Sopenharmony_ci IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, 5108c2ecf20Sopenharmony_ci net, sk, skb, NULL, dst->dev, 5118c2ecf20Sopenharmony_ci dst_output); 5128c2ecf20Sopenharmony_ci if (!err) { 5138c2ecf20Sopenharmony_ci ICMP6MSGOUT_INC_STATS(net, idev, type); 5148c2ecf20Sopenharmony_ci ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci rcu_read_unlock(); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_civoid ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr, 5218c2ecf20Sopenharmony_ci const struct in6_addr *solicited_addr, 5228c2ecf20Sopenharmony_ci bool router, bool solicited, bool override, bool inc_opt) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct sk_buff *skb; 5258c2ecf20Sopenharmony_ci struct in6_addr tmpaddr; 5268c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 5278c2ecf20Sopenharmony_ci const struct in6_addr *src_addr; 5288c2ecf20Sopenharmony_ci struct nd_msg *msg; 5298c2ecf20Sopenharmony_ci int optlen = 0; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* for anycast or proxy, solicited_addr != src_addr */ 5328c2ecf20Sopenharmony_ci ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1); 5338c2ecf20Sopenharmony_ci if (ifp) { 5348c2ecf20Sopenharmony_ci src_addr = solicited_addr; 5358c2ecf20Sopenharmony_ci if (ifp->flags & IFA_F_OPTIMISTIC) 5368c2ecf20Sopenharmony_ci override = false; 5378c2ecf20Sopenharmony_ci inc_opt |= ifp->idev->cnf.force_tllao; 5388c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 5398c2ecf20Sopenharmony_ci } else { 5408c2ecf20Sopenharmony_ci if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr, 5418c2ecf20Sopenharmony_ci inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs, 5428c2ecf20Sopenharmony_ci &tmpaddr)) 5438c2ecf20Sopenharmony_ci return; 5448c2ecf20Sopenharmony_ci src_addr = &tmpaddr; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (!dev->addr_len) 5488c2ecf20Sopenharmony_ci inc_opt = false; 5498c2ecf20Sopenharmony_ci if (inc_opt) 5508c2ecf20Sopenharmony_ci optlen += ndisc_opt_addr_space(dev, 5518c2ecf20Sopenharmony_ci NDISC_NEIGHBOUR_ADVERTISEMENT); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); 5548c2ecf20Sopenharmony_ci if (!skb) 5558c2ecf20Sopenharmony_ci return; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci msg = skb_put(skb, sizeof(*msg)); 5588c2ecf20Sopenharmony_ci *msg = (struct nd_msg) { 5598c2ecf20Sopenharmony_ci .icmph = { 5608c2ecf20Sopenharmony_ci .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, 5618c2ecf20Sopenharmony_ci .icmp6_router = router, 5628c2ecf20Sopenharmony_ci .icmp6_solicited = solicited, 5638c2ecf20Sopenharmony_ci .icmp6_override = override, 5648c2ecf20Sopenharmony_ci }, 5658c2ecf20Sopenharmony_ci .target = *solicited_addr, 5668c2ecf20Sopenharmony_ci }; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (inc_opt) 5698c2ecf20Sopenharmony_ci ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, 5708c2ecf20Sopenharmony_ci dev->dev_addr, 5718c2ecf20Sopenharmony_ci NDISC_NEIGHBOUR_ADVERTISEMENT); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci ndisc_send_skb(skb, daddr, src_addr); 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic void ndisc_send_unsol_na(struct net_device *dev) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci struct inet6_dev *idev; 5798c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci idev = in6_dev_get(dev); 5828c2ecf20Sopenharmony_ci if (!idev) 5838c2ecf20Sopenharmony_ci return; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 5868c2ecf20Sopenharmony_ci list_for_each_entry(ifa, &idev->addr_list, if_list) { 5878c2ecf20Sopenharmony_ci /* skip tentative addresses until dad completes */ 5888c2ecf20Sopenharmony_ci if (ifa->flags & IFA_F_TENTATIVE && 5898c2ecf20Sopenharmony_ci !(ifa->flags & IFA_F_OPTIMISTIC)) 5908c2ecf20Sopenharmony_ci continue; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci ndisc_send_na(dev, &in6addr_linklocal_allnodes, &ifa->addr, 5938c2ecf20Sopenharmony_ci /*router=*/ !!idev->cnf.forwarding, 5948c2ecf20Sopenharmony_ci /*solicited=*/ false, /*override=*/ true, 5958c2ecf20Sopenharmony_ci /*inc_opt=*/ true); 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci in6_dev_put(idev); 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_civoid ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, 6038c2ecf20Sopenharmony_ci const struct in6_addr *daddr, const struct in6_addr *saddr, 6048c2ecf20Sopenharmony_ci u64 nonce) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct sk_buff *skb; 6078c2ecf20Sopenharmony_ci struct in6_addr addr_buf; 6088c2ecf20Sopenharmony_ci int inc_opt = dev->addr_len; 6098c2ecf20Sopenharmony_ci int optlen = 0; 6108c2ecf20Sopenharmony_ci struct nd_msg *msg; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (!saddr) { 6138c2ecf20Sopenharmony_ci if (ipv6_get_lladdr(dev, &addr_buf, 6148c2ecf20Sopenharmony_ci (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC))) 6158c2ecf20Sopenharmony_ci return; 6168c2ecf20Sopenharmony_ci saddr = &addr_buf; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (ipv6_addr_any(saddr)) 6208c2ecf20Sopenharmony_ci inc_opt = false; 6218c2ecf20Sopenharmony_ci if (inc_opt) 6228c2ecf20Sopenharmony_ci optlen += ndisc_opt_addr_space(dev, 6238c2ecf20Sopenharmony_ci NDISC_NEIGHBOUR_SOLICITATION); 6248c2ecf20Sopenharmony_ci if (nonce != 0) 6258c2ecf20Sopenharmony_ci optlen += 8; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); 6288c2ecf20Sopenharmony_ci if (!skb) 6298c2ecf20Sopenharmony_ci return; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci msg = skb_put(skb, sizeof(*msg)); 6328c2ecf20Sopenharmony_ci *msg = (struct nd_msg) { 6338c2ecf20Sopenharmony_ci .icmph = { 6348c2ecf20Sopenharmony_ci .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION, 6358c2ecf20Sopenharmony_ci }, 6368c2ecf20Sopenharmony_ci .target = *solicit, 6378c2ecf20Sopenharmony_ci }; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (inc_opt) 6408c2ecf20Sopenharmony_ci ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, 6418c2ecf20Sopenharmony_ci dev->dev_addr, 6428c2ecf20Sopenharmony_ci NDISC_NEIGHBOUR_SOLICITATION); 6438c2ecf20Sopenharmony_ci if (nonce != 0) { 6448c2ecf20Sopenharmony_ci u8 *opt = skb_put(skb, 8); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci opt[0] = ND_OPT_NONCE; 6478c2ecf20Sopenharmony_ci opt[1] = 8 >> 3; 6488c2ecf20Sopenharmony_ci memcpy(opt + 2, &nonce, 6); 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci ndisc_send_skb(skb, daddr, saddr); 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_civoid ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, 6558c2ecf20Sopenharmony_ci const struct in6_addr *daddr) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct sk_buff *skb; 6588c2ecf20Sopenharmony_ci struct rs_msg *msg; 6598c2ecf20Sopenharmony_ci int send_sllao = dev->addr_len; 6608c2ecf20Sopenharmony_ci int optlen = 0; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 6638c2ecf20Sopenharmony_ci /* 6648c2ecf20Sopenharmony_ci * According to section 2.2 of RFC 4429, we must not 6658c2ecf20Sopenharmony_ci * send router solicitations with a sllao from 6668c2ecf20Sopenharmony_ci * optimistic addresses, but we may send the solicitation 6678c2ecf20Sopenharmony_ci * if we don't include the sllao. So here we check 6688c2ecf20Sopenharmony_ci * if our address is optimistic, and if so, we 6698c2ecf20Sopenharmony_ci * suppress the inclusion of the sllao. 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_ci if (send_sllao) { 6728c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr, 6738c2ecf20Sopenharmony_ci dev, 1); 6748c2ecf20Sopenharmony_ci if (ifp) { 6758c2ecf20Sopenharmony_ci if (ifp->flags & IFA_F_OPTIMISTIC) { 6768c2ecf20Sopenharmony_ci send_sllao = 0; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 6798c2ecf20Sopenharmony_ci } else { 6808c2ecf20Sopenharmony_ci send_sllao = 0; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci#endif 6848c2ecf20Sopenharmony_ci if (send_sllao) 6858c2ecf20Sopenharmony_ci optlen += ndisc_opt_addr_space(dev, NDISC_ROUTER_SOLICITATION); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); 6888c2ecf20Sopenharmony_ci if (!skb) 6898c2ecf20Sopenharmony_ci return; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci msg = skb_put(skb, sizeof(*msg)); 6928c2ecf20Sopenharmony_ci *msg = (struct rs_msg) { 6938c2ecf20Sopenharmony_ci .icmph = { 6948c2ecf20Sopenharmony_ci .icmp6_type = NDISC_ROUTER_SOLICITATION, 6958c2ecf20Sopenharmony_ci }, 6968c2ecf20Sopenharmony_ci }; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (send_sllao) 6998c2ecf20Sopenharmony_ci ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, 7008c2ecf20Sopenharmony_ci dev->dev_addr, 7018c2ecf20Sopenharmony_ci NDISC_ROUTER_SOLICITATION); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci ndisc_send_skb(skb, daddr, saddr); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci /* 7108c2ecf20Sopenharmony_ci * "The sender MUST return an ICMP 7118c2ecf20Sopenharmony_ci * destination unreachable" 7128c2ecf20Sopenharmony_ci */ 7138c2ecf20Sopenharmony_ci dst_link_failure(skb); 7148c2ecf20Sopenharmony_ci kfree_skb(skb); 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci/* Called with locked neigh: either read or both */ 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci struct in6_addr *saddr = NULL; 7228c2ecf20Sopenharmony_ci struct in6_addr mcaddr; 7238c2ecf20Sopenharmony_ci struct net_device *dev = neigh->dev; 7248c2ecf20Sopenharmony_ci struct in6_addr *target = (struct in6_addr *)&neigh->primary_key; 7258c2ecf20Sopenharmony_ci int probes = atomic_read(&neigh->probes); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (skb && ipv6_chk_addr_and_flags(dev_net(dev), &ipv6_hdr(skb)->saddr, 7288c2ecf20Sopenharmony_ci dev, false, 1, 7298c2ecf20Sopenharmony_ci IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) 7308c2ecf20Sopenharmony_ci saddr = &ipv6_hdr(skb)->saddr; 7318c2ecf20Sopenharmony_ci probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES); 7328c2ecf20Sopenharmony_ci if (probes < 0) { 7338c2ecf20Sopenharmony_ci if (!(neigh->nud_state & NUD_VALID)) { 7348c2ecf20Sopenharmony_ci ND_PRINTK(1, dbg, 7358c2ecf20Sopenharmony_ci "%s: trying to ucast probe in NUD_INVALID: %pI6\n", 7368c2ecf20Sopenharmony_ci __func__, target); 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci ndisc_send_ns(dev, target, target, saddr, 0); 7398c2ecf20Sopenharmony_ci } else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) { 7408c2ecf20Sopenharmony_ci neigh_app_ns(neigh); 7418c2ecf20Sopenharmony_ci } else { 7428c2ecf20Sopenharmony_ci addrconf_addr_solict_mult(target, &mcaddr); 7438c2ecf20Sopenharmony_ci ndisc_send_ns(dev, target, &mcaddr, saddr, 0); 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic int pndisc_is_router(const void *pkey, 7488c2ecf20Sopenharmony_ci struct net_device *dev) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct pneigh_entry *n; 7518c2ecf20Sopenharmony_ci int ret = -1; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci read_lock_bh(&nd_tbl.lock); 7548c2ecf20Sopenharmony_ci n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev); 7558c2ecf20Sopenharmony_ci if (n) 7568c2ecf20Sopenharmony_ci ret = !!(n->flags & NTF_ROUTER); 7578c2ecf20Sopenharmony_ci read_unlock_bh(&nd_tbl.lock); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci return ret; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_civoid ndisc_update(const struct net_device *dev, struct neighbour *neigh, 7638c2ecf20Sopenharmony_ci const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type, 7648c2ecf20Sopenharmony_ci struct ndisc_options *ndopts) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci neigh_update(neigh, lladdr, new, flags, 0); 7678c2ecf20Sopenharmony_ci /* report ndisc ops about neighbour update */ 7688c2ecf20Sopenharmony_ci ndisc_ops_update(dev, neigh, flags, icmp6_type, ndopts); 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic void ndisc_recv_ns(struct sk_buff *skb) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); 7748c2ecf20Sopenharmony_ci const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; 7758c2ecf20Sopenharmony_ci const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; 7768c2ecf20Sopenharmony_ci u8 *lladdr = NULL; 7778c2ecf20Sopenharmony_ci u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + 7788c2ecf20Sopenharmony_ci offsetof(struct nd_msg, opt)); 7798c2ecf20Sopenharmony_ci struct ndisc_options ndopts; 7808c2ecf20Sopenharmony_ci struct net_device *dev = skb->dev; 7818c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 7828c2ecf20Sopenharmony_ci struct inet6_dev *idev = NULL; 7838c2ecf20Sopenharmony_ci struct neighbour *neigh; 7848c2ecf20Sopenharmony_ci int dad = ipv6_addr_any(saddr); 7858c2ecf20Sopenharmony_ci bool inc; 7868c2ecf20Sopenharmony_ci int is_router = -1; 7878c2ecf20Sopenharmony_ci u64 nonce = 0; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (skb->len < sizeof(struct nd_msg)) { 7908c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "NS: packet too short\n"); 7918c2ecf20Sopenharmony_ci return; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast(&msg->target)) { 7958c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "NS: multicast target address\n"); 7968c2ecf20Sopenharmony_ci return; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* 8008c2ecf20Sopenharmony_ci * RFC2461 7.1.1: 8018c2ecf20Sopenharmony_ci * DAD has to be destined for solicited node multicast address. 8028c2ecf20Sopenharmony_ci */ 8038c2ecf20Sopenharmony_ci if (dad && !ipv6_addr_is_solict_mult(daddr)) { 8048c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n"); 8058c2ecf20Sopenharmony_ci return; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) { 8098c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "NS: invalid ND options\n"); 8108c2ecf20Sopenharmony_ci return; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (ndopts.nd_opts_src_lladdr) { 8148c2ecf20Sopenharmony_ci lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev); 8158c2ecf20Sopenharmony_ci if (!lladdr) { 8168c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, 8178c2ecf20Sopenharmony_ci "NS: invalid link-layer address length\n"); 8188c2ecf20Sopenharmony_ci return; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* RFC2461 7.1.1: 8228c2ecf20Sopenharmony_ci * If the IP source address is the unspecified address, 8238c2ecf20Sopenharmony_ci * there MUST NOT be source link-layer address option 8248c2ecf20Sopenharmony_ci * in the message. 8258c2ecf20Sopenharmony_ci */ 8268c2ecf20Sopenharmony_ci if (dad) { 8278c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, 8288c2ecf20Sopenharmony_ci "NS: bad DAD packet (link-layer address option)\n"); 8298c2ecf20Sopenharmony_ci return; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci if (ndopts.nd_opts_nonce && ndopts.nd_opts_nonce->nd_opt_len == 1) 8338c2ecf20Sopenharmony_ci memcpy(&nonce, (u8 *)(ndopts.nd_opts_nonce + 1), 6); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci inc = ipv6_addr_is_multicast(daddr); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1); 8388c2ecf20Sopenharmony_ci if (ifp) { 8398c2ecf20Sopenharmony_cihave_ifp: 8408c2ecf20Sopenharmony_ci if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) { 8418c2ecf20Sopenharmony_ci if (dad) { 8428c2ecf20Sopenharmony_ci if (nonce != 0 && ifp->dad_nonce == nonce) { 8438c2ecf20Sopenharmony_ci u8 *np = (u8 *)&nonce; 8448c2ecf20Sopenharmony_ci /* Matching nonce if looped back */ 8458c2ecf20Sopenharmony_ci ND_PRINTK(2, notice, 8468c2ecf20Sopenharmony_ci "%s: IPv6 DAD loopback for address %pI6c nonce %pM ignored\n", 8478c2ecf20Sopenharmony_ci ifp->idev->dev->name, 8488c2ecf20Sopenharmony_ci &ifp->addr, np); 8498c2ecf20Sopenharmony_ci goto out; 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci /* 8528c2ecf20Sopenharmony_ci * We are colliding with another node 8538c2ecf20Sopenharmony_ci * who is doing DAD 8548c2ecf20Sopenharmony_ci * so fail our DAD process 8558c2ecf20Sopenharmony_ci */ 8568c2ecf20Sopenharmony_ci addrconf_dad_failure(skb, ifp); 8578c2ecf20Sopenharmony_ci return; 8588c2ecf20Sopenharmony_ci } else { 8598c2ecf20Sopenharmony_ci /* 8608c2ecf20Sopenharmony_ci * This is not a dad solicitation. 8618c2ecf20Sopenharmony_ci * If we are an optimistic node, 8628c2ecf20Sopenharmony_ci * we should respond. 8638c2ecf20Sopenharmony_ci * Otherwise, we should ignore it. 8648c2ecf20Sopenharmony_ci */ 8658c2ecf20Sopenharmony_ci if (!(ifp->flags & IFA_F_OPTIMISTIC)) 8668c2ecf20Sopenharmony_ci goto out; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci idev = ifp->idev; 8718c2ecf20Sopenharmony_ci } else { 8728c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* perhaps an address on the master device */ 8758c2ecf20Sopenharmony_ci if (netif_is_l3_slave(dev)) { 8768c2ecf20Sopenharmony_ci struct net_device *mdev; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci mdev = netdev_master_upper_dev_get_rcu(dev); 8798c2ecf20Sopenharmony_ci if (mdev) { 8808c2ecf20Sopenharmony_ci ifp = ipv6_get_ifaddr(net, &msg->target, mdev, 1); 8818c2ecf20Sopenharmony_ci if (ifp) 8828c2ecf20Sopenharmony_ci goto have_ifp; 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci idev = in6_dev_get(dev); 8878c2ecf20Sopenharmony_ci if (!idev) { 8888c2ecf20Sopenharmony_ci /* XXX: count this drop? */ 8898c2ecf20Sopenharmony_ci return; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (ipv6_chk_acast_addr(net, dev, &msg->target) || 8938c2ecf20Sopenharmony_ci (idev->cnf.forwarding && 8948c2ecf20Sopenharmony_ci (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) && 8958c2ecf20Sopenharmony_ci (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) { 8968c2ecf20Sopenharmony_ci if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) && 8978c2ecf20Sopenharmony_ci skb->pkt_type != PACKET_HOST && 8988c2ecf20Sopenharmony_ci inc && 8998c2ecf20Sopenharmony_ci NEIGH_VAR(idev->nd_parms, PROXY_DELAY) != 0) { 9008c2ecf20Sopenharmony_ci /* 9018c2ecf20Sopenharmony_ci * for anycast or proxy, 9028c2ecf20Sopenharmony_ci * sender should delay its response 9038c2ecf20Sopenharmony_ci * by a random time between 0 and 9048c2ecf20Sopenharmony_ci * MAX_ANYCAST_DELAY_TIME seconds. 9058c2ecf20Sopenharmony_ci * (RFC2461) -- yoshfuji 9068c2ecf20Sopenharmony_ci */ 9078c2ecf20Sopenharmony_ci struct sk_buff *n = skb_clone(skb, GFP_ATOMIC); 9088c2ecf20Sopenharmony_ci if (n) 9098c2ecf20Sopenharmony_ci pneigh_enqueue(&nd_tbl, idev->nd_parms, n); 9108c2ecf20Sopenharmony_ci goto out; 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci } else 9138c2ecf20Sopenharmony_ci goto out; 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci if (is_router < 0) 9178c2ecf20Sopenharmony_ci is_router = idev->cnf.forwarding; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci if (dad) { 9208c2ecf20Sopenharmony_ci ndisc_send_na(dev, &in6addr_linklocal_allnodes, &msg->target, 9218c2ecf20Sopenharmony_ci !!is_router, false, (ifp != NULL), true); 9228c2ecf20Sopenharmony_ci goto out; 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci if (inc) 9268c2ecf20Sopenharmony_ci NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast); 9278c2ecf20Sopenharmony_ci else 9288c2ecf20Sopenharmony_ci NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci /* 9318c2ecf20Sopenharmony_ci * update / create cache entry 9328c2ecf20Sopenharmony_ci * for the source address 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_ci neigh = __neigh_lookup(&nd_tbl, saddr, dev, 9358c2ecf20Sopenharmony_ci !inc || lladdr || !dev->addr_len); 9368c2ecf20Sopenharmony_ci if (neigh) 9378c2ecf20Sopenharmony_ci ndisc_update(dev, neigh, lladdr, NUD_STALE, 9388c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_WEAK_OVERRIDE| 9398c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE, 9408c2ecf20Sopenharmony_ci NDISC_NEIGHBOUR_SOLICITATION, &ndopts); 9418c2ecf20Sopenharmony_ci if (neigh || !dev->header_ops) { 9428c2ecf20Sopenharmony_ci ndisc_send_na(dev, saddr, &msg->target, !!is_router, 9438c2ecf20Sopenharmony_ci true, (ifp != NULL && inc), inc); 9448c2ecf20Sopenharmony_ci if (neigh) 9458c2ecf20Sopenharmony_ci neigh_release(neigh); 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ciout: 9498c2ecf20Sopenharmony_ci if (ifp) 9508c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 9518c2ecf20Sopenharmony_ci else 9528c2ecf20Sopenharmony_ci in6_dev_put(idev); 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic void ndisc_recv_na(struct sk_buff *skb) 9568c2ecf20Sopenharmony_ci{ 9578c2ecf20Sopenharmony_ci struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); 9588c2ecf20Sopenharmony_ci struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; 9598c2ecf20Sopenharmony_ci const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; 9608c2ecf20Sopenharmony_ci u8 *lladdr = NULL; 9618c2ecf20Sopenharmony_ci u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + 9628c2ecf20Sopenharmony_ci offsetof(struct nd_msg, opt)); 9638c2ecf20Sopenharmony_ci struct ndisc_options ndopts; 9648c2ecf20Sopenharmony_ci struct net_device *dev = skb->dev; 9658c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 9668c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 9678c2ecf20Sopenharmony_ci struct neighbour *neigh; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci if (skb->len < sizeof(struct nd_msg)) { 9708c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "NA: packet too short\n"); 9718c2ecf20Sopenharmony_ci return; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast(&msg->target)) { 9758c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "NA: target address is multicast\n"); 9768c2ecf20Sopenharmony_ci return; 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast(daddr) && 9808c2ecf20Sopenharmony_ci msg->icmph.icmp6_solicited) { 9818c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n"); 9828c2ecf20Sopenharmony_ci return; 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci /* For some 802.11 wireless deployments (and possibly other networks), 9868c2ecf20Sopenharmony_ci * there will be a NA proxy and unsolicitd packets are attacks 9878c2ecf20Sopenharmony_ci * and thus should not be accepted. 9888c2ecf20Sopenharmony_ci */ 9898c2ecf20Sopenharmony_ci if (!msg->icmph.icmp6_solicited && idev && 9908c2ecf20Sopenharmony_ci idev->cnf.drop_unsolicited_na) 9918c2ecf20Sopenharmony_ci return; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) { 9948c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "NS: invalid ND option\n"); 9958c2ecf20Sopenharmony_ci return; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci if (ndopts.nd_opts_tgt_lladdr) { 9988c2ecf20Sopenharmony_ci lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev); 9998c2ecf20Sopenharmony_ci if (!lladdr) { 10008c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, 10018c2ecf20Sopenharmony_ci "NA: invalid link-layer address length\n"); 10028c2ecf20Sopenharmony_ci return; 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1); 10068c2ecf20Sopenharmony_ci if (ifp) { 10078c2ecf20Sopenharmony_ci if (skb->pkt_type != PACKET_LOOPBACK 10088c2ecf20Sopenharmony_ci && (ifp->flags & IFA_F_TENTATIVE)) { 10098c2ecf20Sopenharmony_ci addrconf_dad_failure(skb, ifp); 10108c2ecf20Sopenharmony_ci return; 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci /* What should we make now? The advertisement 10138c2ecf20Sopenharmony_ci is invalid, but ndisc specs say nothing 10148c2ecf20Sopenharmony_ci about it. It could be misconfiguration, or 10158c2ecf20Sopenharmony_ci an smart proxy agent tries to help us :-) 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci We should not print the error if NA has been 10188c2ecf20Sopenharmony_ci received from loopback - it is just our own 10198c2ecf20Sopenharmony_ci unsolicited advertisement. 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_ci if (skb->pkt_type != PACKET_LOOPBACK) 10228c2ecf20Sopenharmony_ci ND_PRINTK(1, warn, 10238c2ecf20Sopenharmony_ci "NA: %pM advertised our address %pI6c on %s!\n", 10248c2ecf20Sopenharmony_ci eth_hdr(skb)->h_source, &ifp->addr, ifp->idev->dev->name); 10258c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 10268c2ecf20Sopenharmony_ci return; 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci neigh = neigh_lookup(&nd_tbl, &msg->target, dev); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if (neigh) { 10318c2ecf20Sopenharmony_ci u8 old_flags = neigh->flags; 10328c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci if (neigh->nud_state & NUD_FAILED) 10358c2ecf20Sopenharmony_ci goto out; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci /* 10388c2ecf20Sopenharmony_ci * Don't update the neighbor cache entry on a proxy NA from 10398c2ecf20Sopenharmony_ci * ourselves because either the proxied node is off link or it 10408c2ecf20Sopenharmony_ci * has already sent a NA to us. 10418c2ecf20Sopenharmony_ci */ 10428c2ecf20Sopenharmony_ci if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) && 10438c2ecf20Sopenharmony_ci net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp && 10448c2ecf20Sopenharmony_ci pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) { 10458c2ecf20Sopenharmony_ci /* XXX: idev->cnf.proxy_ndp */ 10468c2ecf20Sopenharmony_ci goto out; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci ndisc_update(dev, neigh, lladdr, 10508c2ecf20Sopenharmony_ci msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE, 10518c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_WEAK_OVERRIDE| 10528c2ecf20Sopenharmony_ci (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)| 10538c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 10548c2ecf20Sopenharmony_ci (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0), 10558c2ecf20Sopenharmony_ci NDISC_NEIGHBOUR_ADVERTISEMENT, &ndopts); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if ((old_flags & ~neigh->flags) & NTF_ROUTER) { 10588c2ecf20Sopenharmony_ci /* 10598c2ecf20Sopenharmony_ci * Change: router to host 10608c2ecf20Sopenharmony_ci */ 10618c2ecf20Sopenharmony_ci rt6_clean_tohost(dev_net(dev), saddr); 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ciout: 10658c2ecf20Sopenharmony_ci neigh_release(neigh); 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_cistatic void ndisc_recv_rs(struct sk_buff *skb) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb); 10728c2ecf20Sopenharmony_ci unsigned long ndoptlen = skb->len - sizeof(*rs_msg); 10738c2ecf20Sopenharmony_ci struct neighbour *neigh; 10748c2ecf20Sopenharmony_ci struct inet6_dev *idev; 10758c2ecf20Sopenharmony_ci const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; 10768c2ecf20Sopenharmony_ci struct ndisc_options ndopts; 10778c2ecf20Sopenharmony_ci u8 *lladdr = NULL; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (skb->len < sizeof(*rs_msg)) 10808c2ecf20Sopenharmony_ci return; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci idev = __in6_dev_get(skb->dev); 10838c2ecf20Sopenharmony_ci if (!idev) { 10848c2ecf20Sopenharmony_ci ND_PRINTK(1, err, "RS: can't find in6 device\n"); 10858c2ecf20Sopenharmony_ci return; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* Don't accept RS if we're not in router mode */ 10898c2ecf20Sopenharmony_ci if (!idev->cnf.forwarding) 10908c2ecf20Sopenharmony_ci goto out; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci /* 10938c2ecf20Sopenharmony_ci * Don't update NCE if src = ::; 10948c2ecf20Sopenharmony_ci * this implies that the source node has no ip address assigned yet. 10958c2ecf20Sopenharmony_ci */ 10968c2ecf20Sopenharmony_ci if (ipv6_addr_any(saddr)) 10978c2ecf20Sopenharmony_ci goto out; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci /* Parse ND options */ 11008c2ecf20Sopenharmony_ci if (!ndisc_parse_options(skb->dev, rs_msg->opt, ndoptlen, &ndopts)) { 11018c2ecf20Sopenharmony_ci ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n"); 11028c2ecf20Sopenharmony_ci goto out; 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (ndopts.nd_opts_src_lladdr) { 11068c2ecf20Sopenharmony_ci lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, 11078c2ecf20Sopenharmony_ci skb->dev); 11088c2ecf20Sopenharmony_ci if (!lladdr) 11098c2ecf20Sopenharmony_ci goto out; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1); 11138c2ecf20Sopenharmony_ci if (neigh) { 11148c2ecf20Sopenharmony_ci ndisc_update(skb->dev, neigh, lladdr, NUD_STALE, 11158c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_WEAK_OVERRIDE| 11168c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE| 11178c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE_ISROUTER, 11188c2ecf20Sopenharmony_ci NDISC_ROUTER_SOLICITATION, &ndopts); 11198c2ecf20Sopenharmony_ci neigh_release(neigh); 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ciout: 11228c2ecf20Sopenharmony_ci return; 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra); 11288c2ecf20Sopenharmony_ci struct sk_buff *skb; 11298c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 11308c2ecf20Sopenharmony_ci struct nduseroptmsg *ndmsg; 11318c2ecf20Sopenharmony_ci struct net *net = dev_net(ra->dev); 11328c2ecf20Sopenharmony_ci int err; 11338c2ecf20Sopenharmony_ci int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg) 11348c2ecf20Sopenharmony_ci + (opt->nd_opt_len << 3)); 11358c2ecf20Sopenharmony_ci size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr)); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci skb = nlmsg_new(msg_size, GFP_ATOMIC); 11388c2ecf20Sopenharmony_ci if (!skb) { 11398c2ecf20Sopenharmony_ci err = -ENOBUFS; 11408c2ecf20Sopenharmony_ci goto errout; 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0); 11448c2ecf20Sopenharmony_ci if (!nlh) { 11458c2ecf20Sopenharmony_ci goto nla_put_failure; 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci ndmsg = nlmsg_data(nlh); 11498c2ecf20Sopenharmony_ci ndmsg->nduseropt_family = AF_INET6; 11508c2ecf20Sopenharmony_ci ndmsg->nduseropt_ifindex = ra->dev->ifindex; 11518c2ecf20Sopenharmony_ci ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type; 11528c2ecf20Sopenharmony_ci ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code; 11538c2ecf20Sopenharmony_ci ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci if (nla_put_in6_addr(skb, NDUSEROPT_SRCADDR, &ipv6_hdr(ra)->saddr)) 11588c2ecf20Sopenharmony_ci goto nla_put_failure; 11598c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC); 11628c2ecf20Sopenharmony_ci return; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cinla_put_failure: 11658c2ecf20Sopenharmony_ci nlmsg_free(skb); 11668c2ecf20Sopenharmony_ci err = -EMSGSIZE; 11678c2ecf20Sopenharmony_cierrout: 11688c2ecf20Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err); 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_cistatic void ndisc_router_discovery(struct sk_buff *skb) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb); 11748c2ecf20Sopenharmony_ci struct neighbour *neigh = NULL; 11758c2ecf20Sopenharmony_ci struct inet6_dev *in6_dev; 11768c2ecf20Sopenharmony_ci struct fib6_info *rt = NULL; 11778c2ecf20Sopenharmony_ci struct net *net; 11788c2ecf20Sopenharmony_ci int lifetime; 11798c2ecf20Sopenharmony_ci struct ndisc_options ndopts; 11808c2ecf20Sopenharmony_ci int optlen; 11818c2ecf20Sopenharmony_ci unsigned int pref = 0; 11828c2ecf20Sopenharmony_ci __u32 old_if_flags; 11838c2ecf20Sopenharmony_ci bool send_ifinfo_notify = false; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci __u8 *opt = (__u8 *)(ra_msg + 1); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci optlen = (skb_tail_pointer(skb) - skb_transport_header(skb)) - 11888c2ecf20Sopenharmony_ci sizeof(struct ra_msg); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci ND_PRINTK(2, info, 11918c2ecf20Sopenharmony_ci "RA: %s, dev: %s\n", 11928c2ecf20Sopenharmony_ci __func__, skb->dev->name); 11938c2ecf20Sopenharmony_ci if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) { 11948c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "RA: source address is not link-local\n"); 11958c2ecf20Sopenharmony_ci return; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci if (optlen < 0) { 11988c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "RA: packet too short\n"); 11998c2ecf20Sopenharmony_ci return; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE 12038c2ecf20Sopenharmony_ci if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) { 12048c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "RA: from host or unauthorized router\n"); 12058c2ecf20Sopenharmony_ci return; 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci#endif 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci /* 12108c2ecf20Sopenharmony_ci * set the RA_RECV flag in the interface 12118c2ecf20Sopenharmony_ci */ 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci in6_dev = __in6_dev_get(skb->dev); 12148c2ecf20Sopenharmony_ci if (!in6_dev) { 12158c2ecf20Sopenharmony_ci ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n", 12168c2ecf20Sopenharmony_ci skb->dev->name); 12178c2ecf20Sopenharmony_ci return; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (!ndisc_parse_options(skb->dev, opt, optlen, &ndopts)) { 12218c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "RA: invalid ND options\n"); 12228c2ecf20Sopenharmony_ci return; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (!ipv6_accept_ra(in6_dev)) { 12268c2ecf20Sopenharmony_ci ND_PRINTK(2, info, 12278c2ecf20Sopenharmony_ci "RA: %s, did not accept ra for dev: %s\n", 12288c2ecf20Sopenharmony_ci __func__, skb->dev->name); 12298c2ecf20Sopenharmony_ci goto skip_linkparms; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE 12338c2ecf20Sopenharmony_ci /* skip link-specific parameters from interior routers */ 12348c2ecf20Sopenharmony_ci if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) { 12358c2ecf20Sopenharmony_ci ND_PRINTK(2, info, 12368c2ecf20Sopenharmony_ci "RA: %s, nodetype is NODEFAULT, dev: %s\n", 12378c2ecf20Sopenharmony_ci __func__, skb->dev->name); 12388c2ecf20Sopenharmony_ci goto skip_linkparms; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci#endif 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci if (in6_dev->if_flags & IF_RS_SENT) { 12438c2ecf20Sopenharmony_ci /* 12448c2ecf20Sopenharmony_ci * flag that an RA was received after an RS was sent 12458c2ecf20Sopenharmony_ci * out on this interface. 12468c2ecf20Sopenharmony_ci */ 12478c2ecf20Sopenharmony_ci in6_dev->if_flags |= IF_RA_RCVD; 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* 12518c2ecf20Sopenharmony_ci * Remember the managed/otherconf flags from most recently 12528c2ecf20Sopenharmony_ci * received RA message (RFC 2462) -- yoshfuji 12538c2ecf20Sopenharmony_ci */ 12548c2ecf20Sopenharmony_ci old_if_flags = in6_dev->if_flags; 12558c2ecf20Sopenharmony_ci in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED | 12568c2ecf20Sopenharmony_ci IF_RA_OTHERCONF)) | 12578c2ecf20Sopenharmony_ci (ra_msg->icmph.icmp6_addrconf_managed ? 12588c2ecf20Sopenharmony_ci IF_RA_MANAGED : 0) | 12598c2ecf20Sopenharmony_ci (ra_msg->icmph.icmp6_addrconf_other ? 12608c2ecf20Sopenharmony_ci IF_RA_OTHERCONF : 0); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci if (old_if_flags != in6_dev->if_flags) 12638c2ecf20Sopenharmony_ci send_ifinfo_notify = true; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci if (!in6_dev->cnf.accept_ra_defrtr) { 12668c2ecf20Sopenharmony_ci ND_PRINTK(2, info, 12678c2ecf20Sopenharmony_ci "RA: %s, defrtr is false for dev: %s\n", 12688c2ecf20Sopenharmony_ci __func__, skb->dev->name); 12698c2ecf20Sopenharmony_ci goto skip_defrtr; 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime); 12738c2ecf20Sopenharmony_ci if (lifetime != 0 && lifetime < in6_dev->cnf.accept_ra_min_lft) { 12748c2ecf20Sopenharmony_ci ND_PRINTK(2, info, 12758c2ecf20Sopenharmony_ci "RA: router lifetime (%ds) is too short: %s\n", 12768c2ecf20Sopenharmony_ci lifetime, skb->dev->name); 12778c2ecf20Sopenharmony_ci goto skip_defrtr; 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci /* Do not accept RA with source-addr found on local machine unless 12818c2ecf20Sopenharmony_ci * accept_ra_from_local is set to true. 12828c2ecf20Sopenharmony_ci */ 12838c2ecf20Sopenharmony_ci net = dev_net(in6_dev->dev); 12848c2ecf20Sopenharmony_ci if (!in6_dev->cnf.accept_ra_from_local && 12858c2ecf20Sopenharmony_ci ipv6_chk_addr(net, &ipv6_hdr(skb)->saddr, in6_dev->dev, 0)) { 12868c2ecf20Sopenharmony_ci ND_PRINTK(2, info, 12878c2ecf20Sopenharmony_ci "RA from local address detected on dev: %s: default router ignored\n", 12888c2ecf20Sopenharmony_ci skb->dev->name); 12898c2ecf20Sopenharmony_ci goto skip_defrtr; 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 12938c2ecf20Sopenharmony_ci pref = ra_msg->icmph.icmp6_router_pref; 12948c2ecf20Sopenharmony_ci /* 10b is handled as if it were 00b (medium) */ 12958c2ecf20Sopenharmony_ci if (pref == ICMPV6_ROUTER_PREF_INVALID || 12968c2ecf20Sopenharmony_ci !in6_dev->cnf.accept_ra_rtr_pref) 12978c2ecf20Sopenharmony_ci pref = ICMPV6_ROUTER_PREF_MEDIUM; 12988c2ecf20Sopenharmony_ci#endif 12998c2ecf20Sopenharmony_ci /* routes added from RAs do not use nexthop objects */ 13008c2ecf20Sopenharmony_ci rt = rt6_get_dflt_router(net, &ipv6_hdr(skb)->saddr, skb->dev); 13018c2ecf20Sopenharmony_ci if (rt) { 13028c2ecf20Sopenharmony_ci neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6, 13038c2ecf20Sopenharmony_ci rt->fib6_nh->fib_nh_dev, NULL, 13048c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->saddr); 13058c2ecf20Sopenharmony_ci if (!neigh) { 13068c2ecf20Sopenharmony_ci ND_PRINTK(0, err, 13078c2ecf20Sopenharmony_ci "RA: %s got default router without neighbour\n", 13088c2ecf20Sopenharmony_ci __func__); 13098c2ecf20Sopenharmony_ci fib6_info_release(rt); 13108c2ecf20Sopenharmony_ci return; 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci if (rt && lifetime == 0) { 13148c2ecf20Sopenharmony_ci ip6_del_rt(net, rt, false); 13158c2ecf20Sopenharmony_ci rt = NULL; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci ND_PRINTK(3, info, "RA: rt: %p lifetime: %d, for dev: %s\n", 13198c2ecf20Sopenharmony_ci rt, lifetime, skb->dev->name); 13208c2ecf20Sopenharmony_ci if (!rt && lifetime) { 13218c2ecf20Sopenharmony_ci ND_PRINTK(3, info, "RA: adding default router\n"); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci rt = rt6_add_dflt_router(net, &ipv6_hdr(skb)->saddr, 13248c2ecf20Sopenharmony_ci skb->dev, pref); 13258c2ecf20Sopenharmony_ci if (!rt) { 13268c2ecf20Sopenharmony_ci ND_PRINTK(0, err, 13278c2ecf20Sopenharmony_ci "RA: %s failed to add default route\n", 13288c2ecf20Sopenharmony_ci __func__); 13298c2ecf20Sopenharmony_ci return; 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6, 13338c2ecf20Sopenharmony_ci rt->fib6_nh->fib_nh_dev, NULL, 13348c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->saddr); 13358c2ecf20Sopenharmony_ci if (!neigh) { 13368c2ecf20Sopenharmony_ci ND_PRINTK(0, err, 13378c2ecf20Sopenharmony_ci "RA: %s got default router without neighbour\n", 13388c2ecf20Sopenharmony_ci __func__); 13398c2ecf20Sopenharmony_ci fib6_info_release(rt); 13408c2ecf20Sopenharmony_ci return; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci neigh->flags |= NTF_ROUTER; 13438c2ecf20Sopenharmony_ci } else if (rt) { 13448c2ecf20Sopenharmony_ci rt->fib6_flags = (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci if (rt) 13488c2ecf20Sopenharmony_ci fib6_set_expires(rt, jiffies + (HZ * lifetime)); 13498c2ecf20Sopenharmony_ci if (in6_dev->cnf.accept_ra_min_hop_limit < 256 && 13508c2ecf20Sopenharmony_ci ra_msg->icmph.icmp6_hop_limit) { 13518c2ecf20Sopenharmony_ci if (in6_dev->cnf.accept_ra_min_hop_limit <= ra_msg->icmph.icmp6_hop_limit) { 13528c2ecf20Sopenharmony_ci in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit; 13538c2ecf20Sopenharmony_ci fib6_metric_set(rt, RTAX_HOPLIMIT, 13548c2ecf20Sopenharmony_ci ra_msg->icmph.icmp6_hop_limit); 13558c2ecf20Sopenharmony_ci } else { 13568c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "RA: Got route advertisement with lower hop_limit than minimum\n"); 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ciskip_defrtr: 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci /* 13638c2ecf20Sopenharmony_ci * Update Reachable Time and Retrans Timer 13648c2ecf20Sopenharmony_ci */ 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci if (in6_dev->nd_parms) { 13678c2ecf20Sopenharmony_ci unsigned long rtime = ntohl(ra_msg->retrans_timer); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) { 13708c2ecf20Sopenharmony_ci rtime = (rtime*HZ)/1000; 13718c2ecf20Sopenharmony_ci if (rtime < HZ/100) 13728c2ecf20Sopenharmony_ci rtime = HZ/100; 13738c2ecf20Sopenharmony_ci NEIGH_VAR_SET(in6_dev->nd_parms, RETRANS_TIME, rtime); 13748c2ecf20Sopenharmony_ci in6_dev->tstamp = jiffies; 13758c2ecf20Sopenharmony_ci send_ifinfo_notify = true; 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci rtime = ntohl(ra_msg->reachable_time); 13798c2ecf20Sopenharmony_ci if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) { 13808c2ecf20Sopenharmony_ci rtime = (rtime*HZ)/1000; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci if (rtime < HZ/10) 13838c2ecf20Sopenharmony_ci rtime = HZ/10; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci if (rtime != NEIGH_VAR(in6_dev->nd_parms, BASE_REACHABLE_TIME)) { 13868c2ecf20Sopenharmony_ci NEIGH_VAR_SET(in6_dev->nd_parms, 13878c2ecf20Sopenharmony_ci BASE_REACHABLE_TIME, rtime); 13888c2ecf20Sopenharmony_ci NEIGH_VAR_SET(in6_dev->nd_parms, 13898c2ecf20Sopenharmony_ci GC_STALETIME, 3 * rtime); 13908c2ecf20Sopenharmony_ci in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime); 13918c2ecf20Sopenharmony_ci in6_dev->tstamp = jiffies; 13928c2ecf20Sopenharmony_ci send_ifinfo_notify = true; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci } 13958c2ecf20Sopenharmony_ci } 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci /* 13988c2ecf20Sopenharmony_ci * Send a notify if RA changed managed/otherconf flags or timer settings 13998c2ecf20Sopenharmony_ci */ 14008c2ecf20Sopenharmony_ci if (send_ifinfo_notify) 14018c2ecf20Sopenharmony_ci inet6_ifinfo_notify(RTM_NEWLINK, in6_dev); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ciskip_linkparms: 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci /* 14068c2ecf20Sopenharmony_ci * Process options. 14078c2ecf20Sopenharmony_ci */ 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci if (!neigh) 14108c2ecf20Sopenharmony_ci neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr, 14118c2ecf20Sopenharmony_ci skb->dev, 1); 14128c2ecf20Sopenharmony_ci if (neigh) { 14138c2ecf20Sopenharmony_ci u8 *lladdr = NULL; 14148c2ecf20Sopenharmony_ci if (ndopts.nd_opts_src_lladdr) { 14158c2ecf20Sopenharmony_ci lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, 14168c2ecf20Sopenharmony_ci skb->dev); 14178c2ecf20Sopenharmony_ci if (!lladdr) { 14188c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, 14198c2ecf20Sopenharmony_ci "RA: invalid link-layer address length\n"); 14208c2ecf20Sopenharmony_ci goto out; 14218c2ecf20Sopenharmony_ci } 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ci ndisc_update(skb->dev, neigh, lladdr, NUD_STALE, 14248c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_WEAK_OVERRIDE| 14258c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE| 14268c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 14278c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_ISROUTER, 14288c2ecf20Sopenharmony_ci NDISC_ROUTER_ADVERTISEMENT, &ndopts); 14298c2ecf20Sopenharmony_ci } 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (!ipv6_accept_ra(in6_dev)) { 14328c2ecf20Sopenharmony_ci ND_PRINTK(2, info, 14338c2ecf20Sopenharmony_ci "RA: %s, accept_ra is false for dev: %s\n", 14348c2ecf20Sopenharmony_ci __func__, skb->dev->name); 14358c2ecf20Sopenharmony_ci goto out; 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 14398c2ecf20Sopenharmony_ci if (!in6_dev->cnf.accept_ra_from_local && 14408c2ecf20Sopenharmony_ci ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, 14418c2ecf20Sopenharmony_ci in6_dev->dev, 0)) { 14428c2ecf20Sopenharmony_ci ND_PRINTK(2, info, 14438c2ecf20Sopenharmony_ci "RA from local address detected on dev: %s: router info ignored.\n", 14448c2ecf20Sopenharmony_ci skb->dev->name); 14458c2ecf20Sopenharmony_ci goto skip_routeinfo; 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) { 14498c2ecf20Sopenharmony_ci struct nd_opt_hdr *p; 14508c2ecf20Sopenharmony_ci for (p = ndopts.nd_opts_ri; 14518c2ecf20Sopenharmony_ci p; 14528c2ecf20Sopenharmony_ci p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) { 14538c2ecf20Sopenharmony_ci struct route_info *ri = (struct route_info *)p; 14548c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE 14558c2ecf20Sopenharmony_ci if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT && 14568c2ecf20Sopenharmony_ci ri->prefix_len == 0) 14578c2ecf20Sopenharmony_ci continue; 14588c2ecf20Sopenharmony_ci#endif 14598c2ecf20Sopenharmony_ci if (ri->prefix_len == 0 && 14608c2ecf20Sopenharmony_ci !in6_dev->cnf.accept_ra_defrtr) 14618c2ecf20Sopenharmony_ci continue; 14628c2ecf20Sopenharmony_ci if (ri->lifetime != 0 && 14638c2ecf20Sopenharmony_ci ntohl(ri->lifetime) < in6_dev->cnf.accept_ra_min_lft) 14648c2ecf20Sopenharmony_ci continue; 14658c2ecf20Sopenharmony_ci if (ri->prefix_len < in6_dev->cnf.accept_ra_rt_info_min_plen) 14668c2ecf20Sopenharmony_ci continue; 14678c2ecf20Sopenharmony_ci if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen) 14688c2ecf20Sopenharmony_ci continue; 14698c2ecf20Sopenharmony_ci rt6_route_rcv(skb->dev, (u8 *)p, (p->nd_opt_len) << 3, 14708c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->saddr); 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci } 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ciskip_routeinfo: 14758c2ecf20Sopenharmony_ci#endif 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE 14788c2ecf20Sopenharmony_ci /* skip link-specific ndopts from interior routers */ 14798c2ecf20Sopenharmony_ci if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) { 14808c2ecf20Sopenharmony_ci ND_PRINTK(2, info, 14818c2ecf20Sopenharmony_ci "RA: %s, nodetype is NODEFAULT (interior routes), dev: %s\n", 14828c2ecf20Sopenharmony_ci __func__, skb->dev->name); 14838c2ecf20Sopenharmony_ci goto out; 14848c2ecf20Sopenharmony_ci } 14858c2ecf20Sopenharmony_ci#endif 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) { 14888c2ecf20Sopenharmony_ci struct nd_opt_hdr *p; 14898c2ecf20Sopenharmony_ci for (p = ndopts.nd_opts_pi; 14908c2ecf20Sopenharmony_ci p; 14918c2ecf20Sopenharmony_ci p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) { 14928c2ecf20Sopenharmony_ci addrconf_prefix_rcv(skb->dev, (u8 *)p, 14938c2ecf20Sopenharmony_ci (p->nd_opt_len) << 3, 14948c2ecf20Sopenharmony_ci ndopts.nd_opts_src_lladdr != NULL); 14958c2ecf20Sopenharmony_ci } 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci if (ndopts.nd_opts_mtu && in6_dev->cnf.accept_ra_mtu) { 14998c2ecf20Sopenharmony_ci __be32 n; 15008c2ecf20Sopenharmony_ci u32 mtu; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci memcpy(&n, ((u8 *)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu)); 15038c2ecf20Sopenharmony_ci mtu = ntohl(n); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) { 15068c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu); 15078c2ecf20Sopenharmony_ci } else if (in6_dev->cnf.mtu6 != mtu) { 15088c2ecf20Sopenharmony_ci in6_dev->cnf.mtu6 = mtu; 15098c2ecf20Sopenharmony_ci fib6_metric_set(rt, RTAX_MTU, mtu); 15108c2ecf20Sopenharmony_ci rt6_mtu_change(skb->dev, mtu); 15118c2ecf20Sopenharmony_ci } 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci if (ndopts.nd_useropts) { 15158c2ecf20Sopenharmony_ci struct nd_opt_hdr *p; 15168c2ecf20Sopenharmony_ci for (p = ndopts.nd_useropts; 15178c2ecf20Sopenharmony_ci p; 15188c2ecf20Sopenharmony_ci p = ndisc_next_useropt(skb->dev, p, 15198c2ecf20Sopenharmony_ci ndopts.nd_useropts_end)) { 15208c2ecf20Sopenharmony_ci ndisc_ra_useropt(skb, p); 15218c2ecf20Sopenharmony_ci } 15228c2ecf20Sopenharmony_ci } 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) { 15258c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "RA: invalid RA options\n"); 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ciout: 15288c2ecf20Sopenharmony_ci fib6_info_release(rt); 15298c2ecf20Sopenharmony_ci if (neigh) 15308c2ecf20Sopenharmony_ci neigh_release(neigh); 15318c2ecf20Sopenharmony_ci} 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_cistatic void ndisc_redirect_rcv(struct sk_buff *skb) 15348c2ecf20Sopenharmony_ci{ 15358c2ecf20Sopenharmony_ci u8 *hdr; 15368c2ecf20Sopenharmony_ci struct ndisc_options ndopts; 15378c2ecf20Sopenharmony_ci struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb); 15388c2ecf20Sopenharmony_ci u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + 15398c2ecf20Sopenharmony_ci offsetof(struct rd_msg, opt)); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE 15428c2ecf20Sopenharmony_ci switch (skb->ndisc_nodetype) { 15438c2ecf20Sopenharmony_ci case NDISC_NODETYPE_HOST: 15448c2ecf20Sopenharmony_ci case NDISC_NODETYPE_NODEFAULT: 15458c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, 15468c2ecf20Sopenharmony_ci "Redirect: from host or unauthorized router\n"); 15478c2ecf20Sopenharmony_ci return; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci#endif 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) { 15528c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, 15538c2ecf20Sopenharmony_ci "Redirect: source address is not link-local\n"); 15548c2ecf20Sopenharmony_ci return; 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci if (!ndisc_parse_options(skb->dev, msg->opt, ndoptlen, &ndopts)) 15588c2ecf20Sopenharmony_ci return; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci if (!ndopts.nd_opts_rh) { 15618c2ecf20Sopenharmony_ci ip6_redirect_no_header(skb, dev_net(skb->dev), 15628c2ecf20Sopenharmony_ci skb->dev->ifindex); 15638c2ecf20Sopenharmony_ci return; 15648c2ecf20Sopenharmony_ci } 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci hdr = (u8 *)ndopts.nd_opts_rh; 15678c2ecf20Sopenharmony_ci hdr += 8; 15688c2ecf20Sopenharmony_ci if (!pskb_pull(skb, hdr - skb_transport_header(skb))) 15698c2ecf20Sopenharmony_ci return; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci icmpv6_notify(skb, NDISC_REDIRECT, 0, 0); 15728c2ecf20Sopenharmony_ci} 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_cistatic void ndisc_fill_redirect_hdr_option(struct sk_buff *skb, 15758c2ecf20Sopenharmony_ci struct sk_buff *orig_skb, 15768c2ecf20Sopenharmony_ci int rd_len) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci u8 *opt = skb_put(skb, rd_len); 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci memset(opt, 0, 8); 15818c2ecf20Sopenharmony_ci *(opt++) = ND_OPT_REDIRECT_HDR; 15828c2ecf20Sopenharmony_ci *(opt++) = (rd_len >> 3); 15838c2ecf20Sopenharmony_ci opt += 6; 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci skb_copy_bits(orig_skb, skb_network_offset(orig_skb), opt, 15868c2ecf20Sopenharmony_ci rd_len - 8); 15878c2ecf20Sopenharmony_ci} 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_civoid ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) 15908c2ecf20Sopenharmony_ci{ 15918c2ecf20Sopenharmony_ci struct net_device *dev = skb->dev; 15928c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 15938c2ecf20Sopenharmony_ci struct sock *sk = net->ipv6.ndisc_sk; 15948c2ecf20Sopenharmony_ci int optlen = 0; 15958c2ecf20Sopenharmony_ci struct inet_peer *peer; 15968c2ecf20Sopenharmony_ci struct sk_buff *buff; 15978c2ecf20Sopenharmony_ci struct rd_msg *msg; 15988c2ecf20Sopenharmony_ci struct in6_addr saddr_buf; 15998c2ecf20Sopenharmony_ci struct rt6_info *rt; 16008c2ecf20Sopenharmony_ci struct dst_entry *dst; 16018c2ecf20Sopenharmony_ci struct flowi6 fl6; 16028c2ecf20Sopenharmony_ci int rd_len; 16038c2ecf20Sopenharmony_ci u8 ha_buf[MAX_ADDR_LEN], *ha = NULL, 16048c2ecf20Sopenharmony_ci ops_data_buf[NDISC_OPS_REDIRECT_DATA_SPACE], *ops_data = NULL; 16058c2ecf20Sopenharmony_ci bool ret; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci if (netif_is_l3_master(skb->dev)) { 16088c2ecf20Sopenharmony_ci dev = __dev_get_by_index(dev_net(skb->dev), IPCB(skb)->iif); 16098c2ecf20Sopenharmony_ci if (!dev) 16108c2ecf20Sopenharmony_ci return; 16118c2ecf20Sopenharmony_ci } 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) { 16148c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n", 16158c2ecf20Sopenharmony_ci dev->name); 16168c2ecf20Sopenharmony_ci return; 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) && 16208c2ecf20Sopenharmony_ci ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 16218c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, 16228c2ecf20Sopenharmony_ci "Redirect: target address is not link-local unicast\n"); 16238c2ecf20Sopenharmony_ci return; 16248c2ecf20Sopenharmony_ci } 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT, 16278c2ecf20Sopenharmony_ci &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex); 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci dst = ip6_route_output(net, NULL, &fl6); 16308c2ecf20Sopenharmony_ci if (dst->error) { 16318c2ecf20Sopenharmony_ci dst_release(dst); 16328c2ecf20Sopenharmony_ci return; 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); 16358c2ecf20Sopenharmony_ci if (IS_ERR(dst)) 16368c2ecf20Sopenharmony_ci return; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci rt = (struct rt6_info *) dst; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci if (rt->rt6i_flags & RTF_GATEWAY) { 16418c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, 16428c2ecf20Sopenharmony_ci "Redirect: destination is not a neighbour\n"); 16438c2ecf20Sopenharmony_ci goto release; 16448c2ecf20Sopenharmony_ci } 16458c2ecf20Sopenharmony_ci peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr, 1); 16468c2ecf20Sopenharmony_ci ret = inet_peer_xrlim_allow(peer, 1*HZ); 16478c2ecf20Sopenharmony_ci if (peer) 16488c2ecf20Sopenharmony_ci inet_putpeer(peer); 16498c2ecf20Sopenharmony_ci if (!ret) 16508c2ecf20Sopenharmony_ci goto release; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci if (dev->addr_len) { 16538c2ecf20Sopenharmony_ci struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target); 16548c2ecf20Sopenharmony_ci if (!neigh) { 16558c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, 16568c2ecf20Sopenharmony_ci "Redirect: no neigh for target address\n"); 16578c2ecf20Sopenharmony_ci goto release; 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci read_lock_bh(&neigh->lock); 16618c2ecf20Sopenharmony_ci if (neigh->nud_state & NUD_VALID) { 16628c2ecf20Sopenharmony_ci memcpy(ha_buf, neigh->ha, dev->addr_len); 16638c2ecf20Sopenharmony_ci read_unlock_bh(&neigh->lock); 16648c2ecf20Sopenharmony_ci ha = ha_buf; 16658c2ecf20Sopenharmony_ci optlen += ndisc_redirect_opt_addr_space(dev, neigh, 16668c2ecf20Sopenharmony_ci ops_data_buf, 16678c2ecf20Sopenharmony_ci &ops_data); 16688c2ecf20Sopenharmony_ci } else 16698c2ecf20Sopenharmony_ci read_unlock_bh(&neigh->lock); 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci neigh_release(neigh); 16728c2ecf20Sopenharmony_ci } 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci rd_len = min_t(unsigned int, 16758c2ecf20Sopenharmony_ci IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(*msg) - optlen, 16768c2ecf20Sopenharmony_ci skb->len + 8); 16778c2ecf20Sopenharmony_ci rd_len &= ~0x7; 16788c2ecf20Sopenharmony_ci optlen += rd_len; 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci buff = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); 16818c2ecf20Sopenharmony_ci if (!buff) 16828c2ecf20Sopenharmony_ci goto release; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci msg = skb_put(buff, sizeof(*msg)); 16858c2ecf20Sopenharmony_ci *msg = (struct rd_msg) { 16868c2ecf20Sopenharmony_ci .icmph = { 16878c2ecf20Sopenharmony_ci .icmp6_type = NDISC_REDIRECT, 16888c2ecf20Sopenharmony_ci }, 16898c2ecf20Sopenharmony_ci .target = *target, 16908c2ecf20Sopenharmony_ci .dest = ipv6_hdr(skb)->daddr, 16918c2ecf20Sopenharmony_ci }; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci /* 16948c2ecf20Sopenharmony_ci * include target_address option 16958c2ecf20Sopenharmony_ci */ 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci if (ha) 16988c2ecf20Sopenharmony_ci ndisc_fill_redirect_addr_option(buff, ha, ops_data); 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci /* 17018c2ecf20Sopenharmony_ci * build redirect option and copy skb over to the new packet. 17028c2ecf20Sopenharmony_ci */ 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci if (rd_len) 17058c2ecf20Sopenharmony_ci ndisc_fill_redirect_hdr_option(buff, skb, rd_len); 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci skb_dst_set(buff, dst); 17088c2ecf20Sopenharmony_ci ndisc_send_skb(buff, &ipv6_hdr(skb)->saddr, &saddr_buf); 17098c2ecf20Sopenharmony_ci return; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_cirelease: 17128c2ecf20Sopenharmony_ci dst_release(dst); 17138c2ecf20Sopenharmony_ci} 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_cistatic void pndisc_redo(struct sk_buff *skb) 17168c2ecf20Sopenharmony_ci{ 17178c2ecf20Sopenharmony_ci ndisc_recv_ns(skb); 17188c2ecf20Sopenharmony_ci kfree_skb(skb); 17198c2ecf20Sopenharmony_ci} 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_cistatic int ndisc_is_multicast(const void *pkey) 17228c2ecf20Sopenharmony_ci{ 17238c2ecf20Sopenharmony_ci return ipv6_addr_is_multicast((struct in6_addr *)pkey); 17248c2ecf20Sopenharmony_ci} 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_cistatic bool ndisc_suppress_frag_ndisc(struct sk_buff *skb) 17278c2ecf20Sopenharmony_ci{ 17288c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(skb->dev); 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci if (!idev) 17318c2ecf20Sopenharmony_ci return true; 17328c2ecf20Sopenharmony_ci if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED && 17338c2ecf20Sopenharmony_ci idev->cnf.suppress_frag_ndisc) { 17348c2ecf20Sopenharmony_ci net_warn_ratelimited("Received fragmented ndisc packet. Carefully consider disabling suppress_frag_ndisc.\n"); 17358c2ecf20Sopenharmony_ci return true; 17368c2ecf20Sopenharmony_ci } 17378c2ecf20Sopenharmony_ci return false; 17388c2ecf20Sopenharmony_ci} 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ciint ndisc_rcv(struct sk_buff *skb) 17418c2ecf20Sopenharmony_ci{ 17428c2ecf20Sopenharmony_ci struct nd_msg *msg; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci if (ndisc_suppress_frag_ndisc(skb)) 17458c2ecf20Sopenharmony_ci return 0; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci if (skb_linearize(skb)) 17488c2ecf20Sopenharmony_ci return 0; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci msg = (struct nd_msg *)skb_transport_header(skb); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci __skb_push(skb, skb->data - skb_transport_header(skb)); 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci if (ipv6_hdr(skb)->hop_limit != 255) { 17558c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n", 17568c2ecf20Sopenharmony_ci ipv6_hdr(skb)->hop_limit); 17578c2ecf20Sopenharmony_ci return 0; 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci if (msg->icmph.icmp6_code != 0) { 17618c2ecf20Sopenharmony_ci ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n", 17628c2ecf20Sopenharmony_ci msg->icmph.icmp6_code); 17638c2ecf20Sopenharmony_ci return 0; 17648c2ecf20Sopenharmony_ci } 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci switch (msg->icmph.icmp6_type) { 17678c2ecf20Sopenharmony_ci case NDISC_NEIGHBOUR_SOLICITATION: 17688c2ecf20Sopenharmony_ci memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb)); 17698c2ecf20Sopenharmony_ci ndisc_recv_ns(skb); 17708c2ecf20Sopenharmony_ci break; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci case NDISC_NEIGHBOUR_ADVERTISEMENT: 17738c2ecf20Sopenharmony_ci ndisc_recv_na(skb); 17748c2ecf20Sopenharmony_ci break; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci case NDISC_ROUTER_SOLICITATION: 17778c2ecf20Sopenharmony_ci ndisc_recv_rs(skb); 17788c2ecf20Sopenharmony_ci break; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci case NDISC_ROUTER_ADVERTISEMENT: 17818c2ecf20Sopenharmony_ci ndisc_router_discovery(skb); 17828c2ecf20Sopenharmony_ci break; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci case NDISC_REDIRECT: 17858c2ecf20Sopenharmony_ci ndisc_redirect_rcv(skb); 17868c2ecf20Sopenharmony_ci break; 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci return 0; 17908c2ecf20Sopenharmony_ci} 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_cistatic int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) 17938c2ecf20Sopenharmony_ci{ 17948c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 17958c2ecf20Sopenharmony_ci struct netdev_notifier_change_info *change_info; 17968c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 17978c2ecf20Sopenharmony_ci struct inet6_dev *idev; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci switch (event) { 18008c2ecf20Sopenharmony_ci case NETDEV_CHANGEADDR: 18018c2ecf20Sopenharmony_ci neigh_changeaddr(&nd_tbl, dev); 18028c2ecf20Sopenharmony_ci fib6_run_gc(0, net, false); 18038c2ecf20Sopenharmony_ci fallthrough; 18048c2ecf20Sopenharmony_ci case NETDEV_UP: 18058c2ecf20Sopenharmony_ci idev = in6_dev_get(dev); 18068c2ecf20Sopenharmony_ci if (!idev) 18078c2ecf20Sopenharmony_ci break; 18088c2ecf20Sopenharmony_ci if (idev->cnf.ndisc_notify || 18098c2ecf20Sopenharmony_ci net->ipv6.devconf_all->ndisc_notify) 18108c2ecf20Sopenharmony_ci ndisc_send_unsol_na(dev); 18118c2ecf20Sopenharmony_ci in6_dev_put(idev); 18128c2ecf20Sopenharmony_ci break; 18138c2ecf20Sopenharmony_ci case NETDEV_CHANGE: 18148c2ecf20Sopenharmony_ci change_info = ptr; 18158c2ecf20Sopenharmony_ci if (change_info->flags_changed & IFF_NOARP) 18168c2ecf20Sopenharmony_ci neigh_changeaddr(&nd_tbl, dev); 18178c2ecf20Sopenharmony_ci if (!netif_carrier_ok(dev)) 18188c2ecf20Sopenharmony_ci neigh_carrier_down(&nd_tbl, dev); 18198c2ecf20Sopenharmony_ci break; 18208c2ecf20Sopenharmony_ci case NETDEV_DOWN: 18218c2ecf20Sopenharmony_ci neigh_ifdown(&nd_tbl, dev); 18228c2ecf20Sopenharmony_ci fib6_run_gc(0, net, false); 18238c2ecf20Sopenharmony_ci break; 18248c2ecf20Sopenharmony_ci case NETDEV_NOTIFY_PEERS: 18258c2ecf20Sopenharmony_ci ndisc_send_unsol_na(dev); 18268c2ecf20Sopenharmony_ci break; 18278c2ecf20Sopenharmony_ci default: 18288c2ecf20Sopenharmony_ci break; 18298c2ecf20Sopenharmony_ci } 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci return NOTIFY_DONE; 18328c2ecf20Sopenharmony_ci} 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_cistatic struct notifier_block ndisc_netdev_notifier = { 18358c2ecf20Sopenharmony_ci .notifier_call = ndisc_netdev_event, 18368c2ecf20Sopenharmony_ci .priority = ADDRCONF_NOTIFY_PRIORITY - 5, 18378c2ecf20Sopenharmony_ci}; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 18408c2ecf20Sopenharmony_cistatic void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl, 18418c2ecf20Sopenharmony_ci const char *func, const char *dev_name) 18428c2ecf20Sopenharmony_ci{ 18438c2ecf20Sopenharmony_ci static char warncomm[TASK_COMM_LEN]; 18448c2ecf20Sopenharmony_ci static int warned; 18458c2ecf20Sopenharmony_ci if (strcmp(warncomm, current->comm) && warned < 5) { 18468c2ecf20Sopenharmony_ci strcpy(warncomm, current->comm); 18478c2ecf20Sopenharmony_ci pr_warn("process `%s' is using deprecated sysctl (%s) net.ipv6.neigh.%s.%s - use net.ipv6.neigh.%s.%s_ms instead\n", 18488c2ecf20Sopenharmony_ci warncomm, func, 18498c2ecf20Sopenharmony_ci dev_name, ctl->procname, 18508c2ecf20Sopenharmony_ci dev_name, ctl->procname); 18518c2ecf20Sopenharmony_ci warned++; 18528c2ecf20Sopenharmony_ci } 18538c2ecf20Sopenharmony_ci} 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ciint ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void *buffer, 18568c2ecf20Sopenharmony_ci size_t *lenp, loff_t *ppos) 18578c2ecf20Sopenharmony_ci{ 18588c2ecf20Sopenharmony_ci struct net_device *dev = ctl->extra1; 18598c2ecf20Sopenharmony_ci struct inet6_dev *idev; 18608c2ecf20Sopenharmony_ci int ret; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci if ((strcmp(ctl->procname, "retrans_time") == 0) || 18638c2ecf20Sopenharmony_ci (strcmp(ctl->procname, "base_reachable_time") == 0)) 18648c2ecf20Sopenharmony_ci ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default"); 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci if (strcmp(ctl->procname, "retrans_time") == 0) 18678c2ecf20Sopenharmony_ci ret = neigh_proc_dointvec(ctl, write, buffer, lenp, ppos); 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci else if (strcmp(ctl->procname, "base_reachable_time") == 0) 18708c2ecf20Sopenharmony_ci ret = neigh_proc_dointvec_jiffies(ctl, write, 18718c2ecf20Sopenharmony_ci buffer, lenp, ppos); 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) || 18748c2ecf20Sopenharmony_ci (strcmp(ctl->procname, "base_reachable_time_ms") == 0)) 18758c2ecf20Sopenharmony_ci ret = neigh_proc_dointvec_ms_jiffies(ctl, write, 18768c2ecf20Sopenharmony_ci buffer, lenp, ppos); 18778c2ecf20Sopenharmony_ci else 18788c2ecf20Sopenharmony_ci ret = -1; 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) { 18818c2ecf20Sopenharmony_ci if (ctl->data == &NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME)) 18828c2ecf20Sopenharmony_ci idev->nd_parms->reachable_time = 18838c2ecf20Sopenharmony_ci neigh_rand_reach_time(NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME)); 18848c2ecf20Sopenharmony_ci idev->tstamp = jiffies; 18858c2ecf20Sopenharmony_ci inet6_ifinfo_notify(RTM_NEWLINK, idev); 18868c2ecf20Sopenharmony_ci in6_dev_put(idev); 18878c2ecf20Sopenharmony_ci } 18888c2ecf20Sopenharmony_ci return ret; 18898c2ecf20Sopenharmony_ci} 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci#endif 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_cistatic int __net_init ndisc_net_init(struct net *net) 18958c2ecf20Sopenharmony_ci{ 18968c2ecf20Sopenharmony_ci struct ipv6_pinfo *np; 18978c2ecf20Sopenharmony_ci struct sock *sk; 18988c2ecf20Sopenharmony_ci int err; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci err = inet_ctl_sock_create(&sk, PF_INET6, 19018c2ecf20Sopenharmony_ci SOCK_RAW, IPPROTO_ICMPV6, net); 19028c2ecf20Sopenharmony_ci if (err < 0) { 19038c2ecf20Sopenharmony_ci ND_PRINTK(0, err, 19048c2ecf20Sopenharmony_ci "NDISC: Failed to initialize the control socket (err %d)\n", 19058c2ecf20Sopenharmony_ci err); 19068c2ecf20Sopenharmony_ci return err; 19078c2ecf20Sopenharmony_ci } 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci net->ipv6.ndisc_sk = sk; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci np = inet6_sk(sk); 19128c2ecf20Sopenharmony_ci np->hop_limit = 255; 19138c2ecf20Sopenharmony_ci /* Do not loopback ndisc messages */ 19148c2ecf20Sopenharmony_ci np->mc_loop = 0; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci return 0; 19178c2ecf20Sopenharmony_ci} 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_cistatic void __net_exit ndisc_net_exit(struct net *net) 19208c2ecf20Sopenharmony_ci{ 19218c2ecf20Sopenharmony_ci inet_ctl_sock_destroy(net->ipv6.ndisc_sk); 19228c2ecf20Sopenharmony_ci} 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_cistatic struct pernet_operations ndisc_net_ops = { 19258c2ecf20Sopenharmony_ci .init = ndisc_net_init, 19268c2ecf20Sopenharmony_ci .exit = ndisc_net_exit, 19278c2ecf20Sopenharmony_ci}; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ciint __init ndisc_init(void) 19308c2ecf20Sopenharmony_ci{ 19318c2ecf20Sopenharmony_ci int err; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci err = register_pernet_subsys(&ndisc_net_ops); 19348c2ecf20Sopenharmony_ci if (err) 19358c2ecf20Sopenharmony_ci return err; 19368c2ecf20Sopenharmony_ci /* 19378c2ecf20Sopenharmony_ci * Initialize the neighbour table 19388c2ecf20Sopenharmony_ci */ 19398c2ecf20Sopenharmony_ci neigh_table_init(NEIGH_ND_TABLE, &nd_tbl); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 19428c2ecf20Sopenharmony_ci err = neigh_sysctl_register(NULL, &nd_tbl.parms, 19438c2ecf20Sopenharmony_ci ndisc_ifinfo_sysctl_change); 19448c2ecf20Sopenharmony_ci if (err) 19458c2ecf20Sopenharmony_ci goto out_unregister_pernet; 19468c2ecf20Sopenharmony_ciout: 19478c2ecf20Sopenharmony_ci#endif 19488c2ecf20Sopenharmony_ci return err; 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 19518c2ecf20Sopenharmony_ciout_unregister_pernet: 19528c2ecf20Sopenharmony_ci unregister_pernet_subsys(&ndisc_net_ops); 19538c2ecf20Sopenharmony_ci goto out; 19548c2ecf20Sopenharmony_ci#endif 19558c2ecf20Sopenharmony_ci} 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ciint __init ndisc_late_init(void) 19588c2ecf20Sopenharmony_ci{ 19598c2ecf20Sopenharmony_ci return register_netdevice_notifier(&ndisc_netdev_notifier); 19608c2ecf20Sopenharmony_ci} 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_civoid ndisc_late_cleanup(void) 19638c2ecf20Sopenharmony_ci{ 19648c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&ndisc_netdev_notifier); 19658c2ecf20Sopenharmony_ci} 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_civoid ndisc_cleanup(void) 19688c2ecf20Sopenharmony_ci{ 19698c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 19708c2ecf20Sopenharmony_ci neigh_sysctl_unregister(&nd_tbl.parms); 19718c2ecf20Sopenharmony_ci#endif 19728c2ecf20Sopenharmony_ci neigh_table_clear(NEIGH_ND_TABLE, &nd_tbl); 19738c2ecf20Sopenharmony_ci unregister_pernet_subsys(&ndisc_net_ops); 19748c2ecf20Sopenharmony_ci} 1975