162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Neighbour Discovery for IPv6 462306a36Sopenharmony_ci * Linux INET6 implementation 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 862306a36Sopenharmony_ci * Mike Shaver <shaver@ingenia.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * Changes: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Alexey I. Froloff : RFC6106 (DNSSL) support 1562306a36Sopenharmony_ci * Pierre Ynard : export userland ND options 1662306a36Sopenharmony_ci * through netlink (RDNSS support) 1762306a36Sopenharmony_ci * Lars Fenneberg : fixed MTU setting on receipt 1862306a36Sopenharmony_ci * of an RA. 1962306a36Sopenharmony_ci * Janos Farkas : kmalloc failure checks 2062306a36Sopenharmony_ci * Alexey Kuznetsov : state machine reworked 2162306a36Sopenharmony_ci * and moved to net/core. 2262306a36Sopenharmony_ci * Pekka Savola : RFC2461 validation 2362306a36Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI : Verify ND options properly 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define pr_fmt(fmt) "ICMPv6: " fmt 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/errno.h> 3062306a36Sopenharmony_ci#include <linux/types.h> 3162306a36Sopenharmony_ci#include <linux/socket.h> 3262306a36Sopenharmony_ci#include <linux/sockios.h> 3362306a36Sopenharmony_ci#include <linux/sched.h> 3462306a36Sopenharmony_ci#include <linux/net.h> 3562306a36Sopenharmony_ci#include <linux/in6.h> 3662306a36Sopenharmony_ci#include <linux/route.h> 3762306a36Sopenharmony_ci#include <linux/init.h> 3862306a36Sopenharmony_ci#include <linux/rcupdate.h> 3962306a36Sopenharmony_ci#include <linux/slab.h> 4062306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 4162306a36Sopenharmony_ci#include <linux/sysctl.h> 4262306a36Sopenharmony_ci#endif 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include <linux/if_addr.h> 4562306a36Sopenharmony_ci#include <linux/if_ether.h> 4662306a36Sopenharmony_ci#include <linux/if_arp.h> 4762306a36Sopenharmony_ci#include <linux/ipv6.h> 4862306a36Sopenharmony_ci#include <linux/icmpv6.h> 4962306a36Sopenharmony_ci#include <linux/jhash.h> 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#include <net/sock.h> 5262306a36Sopenharmony_ci#include <net/snmp.h> 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#include <net/ipv6.h> 5562306a36Sopenharmony_ci#include <net/protocol.h> 5662306a36Sopenharmony_ci#include <net/ndisc.h> 5762306a36Sopenharmony_ci#include <net/ip6_route.h> 5862306a36Sopenharmony_ci#include <net/addrconf.h> 5962306a36Sopenharmony_ci#include <net/icmp.h> 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#include <net/netlink.h> 6262306a36Sopenharmony_ci#include <linux/rtnetlink.h> 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#include <net/flow.h> 6562306a36Sopenharmony_ci#include <net/ip6_checksum.h> 6662306a36Sopenharmony_ci#include <net/inet_common.h> 6762306a36Sopenharmony_ci#include <linux/proc_fs.h> 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#include <linux/netfilter.h> 7062306a36Sopenharmony_ci#include <linux/netfilter_ipv6.h> 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic u32 ndisc_hash(const void *pkey, 7362306a36Sopenharmony_ci const struct net_device *dev, 7462306a36Sopenharmony_ci __u32 *hash_rnd); 7562306a36Sopenharmony_cistatic bool ndisc_key_eq(const struct neighbour *neigh, const void *pkey); 7662306a36Sopenharmony_cistatic bool ndisc_allow_add(const struct net_device *dev, 7762306a36Sopenharmony_ci struct netlink_ext_ack *extack); 7862306a36Sopenharmony_cistatic int ndisc_constructor(struct neighbour *neigh); 7962306a36Sopenharmony_cistatic void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb); 8062306a36Sopenharmony_cistatic void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb); 8162306a36Sopenharmony_cistatic int pndisc_constructor(struct pneigh_entry *n); 8262306a36Sopenharmony_cistatic void pndisc_destructor(struct pneigh_entry *n); 8362306a36Sopenharmony_cistatic void pndisc_redo(struct sk_buff *skb); 8462306a36Sopenharmony_cistatic int ndisc_is_multicast(const void *pkey); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic const struct neigh_ops ndisc_generic_ops = { 8762306a36Sopenharmony_ci .family = AF_INET6, 8862306a36Sopenharmony_ci .solicit = ndisc_solicit, 8962306a36Sopenharmony_ci .error_report = ndisc_error_report, 9062306a36Sopenharmony_ci .output = neigh_resolve_output, 9162306a36Sopenharmony_ci .connected_output = neigh_connected_output, 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic const struct neigh_ops ndisc_hh_ops = { 9562306a36Sopenharmony_ci .family = AF_INET6, 9662306a36Sopenharmony_ci .solicit = ndisc_solicit, 9762306a36Sopenharmony_ci .error_report = ndisc_error_report, 9862306a36Sopenharmony_ci .output = neigh_resolve_output, 9962306a36Sopenharmony_ci .connected_output = neigh_resolve_output, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic const struct neigh_ops ndisc_direct_ops = { 10462306a36Sopenharmony_ci .family = AF_INET6, 10562306a36Sopenharmony_ci .output = neigh_direct_output, 10662306a36Sopenharmony_ci .connected_output = neigh_direct_output, 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistruct neigh_table nd_tbl = { 11062306a36Sopenharmony_ci .family = AF_INET6, 11162306a36Sopenharmony_ci .key_len = sizeof(struct in6_addr), 11262306a36Sopenharmony_ci .protocol = cpu_to_be16(ETH_P_IPV6), 11362306a36Sopenharmony_ci .hash = ndisc_hash, 11462306a36Sopenharmony_ci .key_eq = ndisc_key_eq, 11562306a36Sopenharmony_ci .constructor = ndisc_constructor, 11662306a36Sopenharmony_ci .pconstructor = pndisc_constructor, 11762306a36Sopenharmony_ci .pdestructor = pndisc_destructor, 11862306a36Sopenharmony_ci .proxy_redo = pndisc_redo, 11962306a36Sopenharmony_ci .is_multicast = ndisc_is_multicast, 12062306a36Sopenharmony_ci .allow_add = ndisc_allow_add, 12162306a36Sopenharmony_ci .id = "ndisc_cache", 12262306a36Sopenharmony_ci .parms = { 12362306a36Sopenharmony_ci .tbl = &nd_tbl, 12462306a36Sopenharmony_ci .reachable_time = ND_REACHABLE_TIME, 12562306a36Sopenharmony_ci .data = { 12662306a36Sopenharmony_ci [NEIGH_VAR_MCAST_PROBES] = 3, 12762306a36Sopenharmony_ci [NEIGH_VAR_UCAST_PROBES] = 3, 12862306a36Sopenharmony_ci [NEIGH_VAR_RETRANS_TIME] = ND_RETRANS_TIMER, 12962306a36Sopenharmony_ci [NEIGH_VAR_BASE_REACHABLE_TIME] = ND_REACHABLE_TIME, 13062306a36Sopenharmony_ci [NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ, 13162306a36Sopenharmony_ci [NEIGH_VAR_INTERVAL_PROBE_TIME_MS] = 5 * HZ, 13262306a36Sopenharmony_ci [NEIGH_VAR_GC_STALETIME] = 60 * HZ, 13362306a36Sopenharmony_ci [NEIGH_VAR_QUEUE_LEN_BYTES] = SK_WMEM_MAX, 13462306a36Sopenharmony_ci [NEIGH_VAR_PROXY_QLEN] = 64, 13562306a36Sopenharmony_ci [NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ, 13662306a36Sopenharmony_ci [NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10, 13762306a36Sopenharmony_ci }, 13862306a36Sopenharmony_ci }, 13962306a36Sopenharmony_ci .gc_interval = 30 * HZ, 14062306a36Sopenharmony_ci .gc_thresh1 = 128, 14162306a36Sopenharmony_ci .gc_thresh2 = 512, 14262306a36Sopenharmony_ci .gc_thresh3 = 1024, 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nd_tbl); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_civoid __ndisc_fill_addr_option(struct sk_buff *skb, int type, const void *data, 14762306a36Sopenharmony_ci int data_len, int pad) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci int space = __ndisc_opt_addr_space(data_len, pad); 15062306a36Sopenharmony_ci u8 *opt = skb_put(skb, space); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci opt[0] = type; 15362306a36Sopenharmony_ci opt[1] = space>>3; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci memset(opt + 2, 0, pad); 15662306a36Sopenharmony_ci opt += pad; 15762306a36Sopenharmony_ci space -= pad; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci memcpy(opt+2, data, data_len); 16062306a36Sopenharmony_ci data_len += 2; 16162306a36Sopenharmony_ci opt += data_len; 16262306a36Sopenharmony_ci space -= data_len; 16362306a36Sopenharmony_ci if (space > 0) 16462306a36Sopenharmony_ci memset(opt, 0, space); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__ndisc_fill_addr_option); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic inline void ndisc_fill_addr_option(struct sk_buff *skb, int type, 16962306a36Sopenharmony_ci const void *data, u8 icmp6_type) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci __ndisc_fill_addr_option(skb, type, data, skb->dev->addr_len, 17262306a36Sopenharmony_ci ndisc_addr_option_pad(skb->dev->type)); 17362306a36Sopenharmony_ci ndisc_ops_fill_addr_option(skb->dev, skb, icmp6_type); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic inline void ndisc_fill_redirect_addr_option(struct sk_buff *skb, 17762306a36Sopenharmony_ci void *ha, 17862306a36Sopenharmony_ci const u8 *ops_data) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, ha, NDISC_REDIRECT); 18162306a36Sopenharmony_ci ndisc_ops_fill_redirect_addr_option(skb->dev, skb, ops_data); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, 18562306a36Sopenharmony_ci struct nd_opt_hdr *end) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci int type; 18862306a36Sopenharmony_ci if (!cur || !end || cur >= end) 18962306a36Sopenharmony_ci return NULL; 19062306a36Sopenharmony_ci type = cur->nd_opt_type; 19162306a36Sopenharmony_ci do { 19262306a36Sopenharmony_ci cur = ((void *)cur) + (cur->nd_opt_len << 3); 19362306a36Sopenharmony_ci } while (cur < end && cur->nd_opt_type != type); 19462306a36Sopenharmony_ci return cur <= end && cur->nd_opt_type == type ? cur : NULL; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic inline int ndisc_is_useropt(const struct net_device *dev, 19862306a36Sopenharmony_ci struct nd_opt_hdr *opt) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci return opt->nd_opt_type == ND_OPT_PREFIX_INFO || 20162306a36Sopenharmony_ci opt->nd_opt_type == ND_OPT_RDNSS || 20262306a36Sopenharmony_ci opt->nd_opt_type == ND_OPT_DNSSL || 20362306a36Sopenharmony_ci opt->nd_opt_type == ND_OPT_CAPTIVE_PORTAL || 20462306a36Sopenharmony_ci opt->nd_opt_type == ND_OPT_PREF64 || 20562306a36Sopenharmony_ci ndisc_ops_is_useropt(dev, opt->nd_opt_type); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic struct nd_opt_hdr *ndisc_next_useropt(const struct net_device *dev, 20962306a36Sopenharmony_ci struct nd_opt_hdr *cur, 21062306a36Sopenharmony_ci struct nd_opt_hdr *end) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci if (!cur || !end || cur >= end) 21362306a36Sopenharmony_ci return NULL; 21462306a36Sopenharmony_ci do { 21562306a36Sopenharmony_ci cur = ((void *)cur) + (cur->nd_opt_len << 3); 21662306a36Sopenharmony_ci } while (cur < end && !ndisc_is_useropt(dev, cur)); 21762306a36Sopenharmony_ci return cur <= end && ndisc_is_useropt(dev, cur) ? cur : NULL; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistruct ndisc_options *ndisc_parse_options(const struct net_device *dev, 22162306a36Sopenharmony_ci u8 *opt, int opt_len, 22262306a36Sopenharmony_ci struct ndisc_options *ndopts) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (!nd_opt || opt_len < 0 || !ndopts) 22762306a36Sopenharmony_ci return NULL; 22862306a36Sopenharmony_ci memset(ndopts, 0, sizeof(*ndopts)); 22962306a36Sopenharmony_ci while (opt_len) { 23062306a36Sopenharmony_ci int l; 23162306a36Sopenharmony_ci if (opt_len < sizeof(struct nd_opt_hdr)) 23262306a36Sopenharmony_ci return NULL; 23362306a36Sopenharmony_ci l = nd_opt->nd_opt_len << 3; 23462306a36Sopenharmony_ci if (opt_len < l || l == 0) 23562306a36Sopenharmony_ci return NULL; 23662306a36Sopenharmony_ci if (ndisc_ops_parse_options(dev, nd_opt, ndopts)) 23762306a36Sopenharmony_ci goto next_opt; 23862306a36Sopenharmony_ci switch (nd_opt->nd_opt_type) { 23962306a36Sopenharmony_ci case ND_OPT_SOURCE_LL_ADDR: 24062306a36Sopenharmony_ci case ND_OPT_TARGET_LL_ADDR: 24162306a36Sopenharmony_ci case ND_OPT_MTU: 24262306a36Sopenharmony_ci case ND_OPT_NONCE: 24362306a36Sopenharmony_ci case ND_OPT_REDIRECT_HDR: 24462306a36Sopenharmony_ci if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { 24562306a36Sopenharmony_ci ND_PRINTK(2, warn, 24662306a36Sopenharmony_ci "%s: duplicated ND6 option found: type=%d\n", 24762306a36Sopenharmony_ci __func__, nd_opt->nd_opt_type); 24862306a36Sopenharmony_ci } else { 24962306a36Sopenharmony_ci ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci case ND_OPT_PREFIX_INFO: 25362306a36Sopenharmony_ci ndopts->nd_opts_pi_end = nd_opt; 25462306a36Sopenharmony_ci if (!ndopts->nd_opt_array[nd_opt->nd_opt_type]) 25562306a36Sopenharmony_ci ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt; 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 25862306a36Sopenharmony_ci case ND_OPT_ROUTE_INFO: 25962306a36Sopenharmony_ci ndopts->nd_opts_ri_end = nd_opt; 26062306a36Sopenharmony_ci if (!ndopts->nd_opts_ri) 26162306a36Sopenharmony_ci ndopts->nd_opts_ri = nd_opt; 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci#endif 26462306a36Sopenharmony_ci default: 26562306a36Sopenharmony_ci if (ndisc_is_useropt(dev, nd_opt)) { 26662306a36Sopenharmony_ci ndopts->nd_useropts_end = nd_opt; 26762306a36Sopenharmony_ci if (!ndopts->nd_useropts) 26862306a36Sopenharmony_ci ndopts->nd_useropts = nd_opt; 26962306a36Sopenharmony_ci } else { 27062306a36Sopenharmony_ci /* 27162306a36Sopenharmony_ci * Unknown options must be silently ignored, 27262306a36Sopenharmony_ci * to accommodate future extension to the 27362306a36Sopenharmony_ci * protocol. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci ND_PRINTK(2, notice, 27662306a36Sopenharmony_ci "%s: ignored unsupported option; type=%d, len=%d\n", 27762306a36Sopenharmony_ci __func__, 27862306a36Sopenharmony_ci nd_opt->nd_opt_type, 27962306a36Sopenharmony_ci nd_opt->nd_opt_len); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_cinext_opt: 28362306a36Sopenharmony_ci opt_len -= l; 28462306a36Sopenharmony_ci nd_opt = ((void *)nd_opt) + l; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci return ndopts; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ciint ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci switch (dev->type) { 29262306a36Sopenharmony_ci case ARPHRD_ETHER: 29362306a36Sopenharmony_ci case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */ 29462306a36Sopenharmony_ci case ARPHRD_FDDI: 29562306a36Sopenharmony_ci ipv6_eth_mc_map(addr, buf); 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci case ARPHRD_ARCNET: 29862306a36Sopenharmony_ci ipv6_arcnet_mc_map(addr, buf); 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci case ARPHRD_INFINIBAND: 30162306a36Sopenharmony_ci ipv6_ib_mc_map(addr, dev->broadcast, buf); 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci case ARPHRD_IPGRE: 30462306a36Sopenharmony_ci return ipv6_ipgre_mc_map(addr, dev->broadcast, buf); 30562306a36Sopenharmony_ci default: 30662306a36Sopenharmony_ci if (dir) { 30762306a36Sopenharmony_ci memcpy(buf, dev->broadcast, dev->addr_len); 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci return -EINVAL; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ciEXPORT_SYMBOL(ndisc_mc_map); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic u32 ndisc_hash(const void *pkey, 31662306a36Sopenharmony_ci const struct net_device *dev, 31762306a36Sopenharmony_ci __u32 *hash_rnd) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci return ndisc_hashfn(pkey, dev, hash_rnd); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic bool ndisc_key_eq(const struct neighbour *n, const void *pkey) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci return neigh_key_eq128(n, pkey); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int ndisc_constructor(struct neighbour *neigh) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct in6_addr *addr = (struct in6_addr *)&neigh->primary_key; 33062306a36Sopenharmony_ci struct net_device *dev = neigh->dev; 33162306a36Sopenharmony_ci struct inet6_dev *in6_dev; 33262306a36Sopenharmony_ci struct neigh_parms *parms; 33362306a36Sopenharmony_ci bool is_multicast = ipv6_addr_is_multicast(addr); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci in6_dev = in6_dev_get(dev); 33662306a36Sopenharmony_ci if (!in6_dev) { 33762306a36Sopenharmony_ci return -EINVAL; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci parms = in6_dev->nd_parms; 34162306a36Sopenharmony_ci __neigh_parms_put(neigh->parms); 34262306a36Sopenharmony_ci neigh->parms = neigh_parms_clone(parms); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST; 34562306a36Sopenharmony_ci if (!dev->header_ops) { 34662306a36Sopenharmony_ci neigh->nud_state = NUD_NOARP; 34762306a36Sopenharmony_ci neigh->ops = &ndisc_direct_ops; 34862306a36Sopenharmony_ci neigh->output = neigh_direct_output; 34962306a36Sopenharmony_ci } else { 35062306a36Sopenharmony_ci if (is_multicast) { 35162306a36Sopenharmony_ci neigh->nud_state = NUD_NOARP; 35262306a36Sopenharmony_ci ndisc_mc_map(addr, neigh->ha, dev, 1); 35362306a36Sopenharmony_ci } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) { 35462306a36Sopenharmony_ci neigh->nud_state = NUD_NOARP; 35562306a36Sopenharmony_ci memcpy(neigh->ha, dev->dev_addr, dev->addr_len); 35662306a36Sopenharmony_ci if (dev->flags&IFF_LOOPBACK) 35762306a36Sopenharmony_ci neigh->type = RTN_LOCAL; 35862306a36Sopenharmony_ci } else if (dev->flags&IFF_POINTOPOINT) { 35962306a36Sopenharmony_ci neigh->nud_state = NUD_NOARP; 36062306a36Sopenharmony_ci memcpy(neigh->ha, dev->broadcast, dev->addr_len); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci if (dev->header_ops->cache) 36362306a36Sopenharmony_ci neigh->ops = &ndisc_hh_ops; 36462306a36Sopenharmony_ci else 36562306a36Sopenharmony_ci neigh->ops = &ndisc_generic_ops; 36662306a36Sopenharmony_ci if (neigh->nud_state&NUD_VALID) 36762306a36Sopenharmony_ci neigh->output = neigh->ops->connected_output; 36862306a36Sopenharmony_ci else 36962306a36Sopenharmony_ci neigh->output = neigh->ops->output; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci in6_dev_put(in6_dev); 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int pndisc_constructor(struct pneigh_entry *n) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct in6_addr *addr = (struct in6_addr *)&n->key; 37862306a36Sopenharmony_ci struct in6_addr maddr; 37962306a36Sopenharmony_ci struct net_device *dev = n->dev; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (!dev || !__in6_dev_get(dev)) 38262306a36Sopenharmony_ci return -EINVAL; 38362306a36Sopenharmony_ci addrconf_addr_solict_mult(addr, &maddr); 38462306a36Sopenharmony_ci ipv6_dev_mc_inc(dev, &maddr); 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic void pndisc_destructor(struct pneigh_entry *n) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct in6_addr *addr = (struct in6_addr *)&n->key; 39162306a36Sopenharmony_ci struct in6_addr maddr; 39262306a36Sopenharmony_ci struct net_device *dev = n->dev; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (!dev || !__in6_dev_get(dev)) 39562306a36Sopenharmony_ci return; 39662306a36Sopenharmony_ci addrconf_addr_solict_mult(addr, &maddr); 39762306a36Sopenharmony_ci ipv6_dev_mc_dec(dev, &maddr); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* called with rtnl held */ 40162306a36Sopenharmony_cistatic bool ndisc_allow_add(const struct net_device *dev, 40262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (!idev || idev->cnf.disable_ipv6) { 40762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "IPv6 is disabled on this device"); 40862306a36Sopenharmony_ci return false; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return true; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic struct sk_buff *ndisc_alloc_skb(struct net_device *dev, 41562306a36Sopenharmony_ci int len) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci int hlen = LL_RESERVED_SPACE(dev); 41862306a36Sopenharmony_ci int tlen = dev->needed_tailroom; 41962306a36Sopenharmony_ci struct sock *sk = dev_net(dev)->ipv6.ndisc_sk; 42062306a36Sopenharmony_ci struct sk_buff *skb; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci skb = alloc_skb(hlen + sizeof(struct ipv6hdr) + len + tlen, GFP_ATOMIC); 42362306a36Sopenharmony_ci if (!skb) { 42462306a36Sopenharmony_ci ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb\n", 42562306a36Sopenharmony_ci __func__); 42662306a36Sopenharmony_ci return NULL; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 43062306a36Sopenharmony_ci skb->dev = dev; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci skb_reserve(skb, hlen + sizeof(struct ipv6hdr)); 43362306a36Sopenharmony_ci skb_reset_transport_header(skb); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* Manually assign socket ownership as we avoid calling 43662306a36Sopenharmony_ci * sock_alloc_send_pskb() to bypass wmem buffer limits 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci skb_set_owner_w(skb, sk); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return skb; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic void ip6_nd_hdr(struct sk_buff *skb, 44462306a36Sopenharmony_ci const struct in6_addr *saddr, 44562306a36Sopenharmony_ci const struct in6_addr *daddr, 44662306a36Sopenharmony_ci int hop_limit, int len) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct ipv6hdr *hdr; 44962306a36Sopenharmony_ci struct inet6_dev *idev; 45062306a36Sopenharmony_ci unsigned tclass; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci rcu_read_lock(); 45362306a36Sopenharmony_ci idev = __in6_dev_get(skb->dev); 45462306a36Sopenharmony_ci tclass = idev ? idev->cnf.ndisc_tclass : 0; 45562306a36Sopenharmony_ci rcu_read_unlock(); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci skb_push(skb, sizeof(*hdr)); 45862306a36Sopenharmony_ci skb_reset_network_header(skb); 45962306a36Sopenharmony_ci hdr = ipv6_hdr(skb); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ip6_flow_hdr(hdr, tclass, 0); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci hdr->payload_len = htons(len); 46462306a36Sopenharmony_ci hdr->nexthdr = IPPROTO_ICMPV6; 46562306a36Sopenharmony_ci hdr->hop_limit = hop_limit; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci hdr->saddr = *saddr; 46862306a36Sopenharmony_ci hdr->daddr = *daddr; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_civoid ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, 47262306a36Sopenharmony_ci const struct in6_addr *saddr) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 47562306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 47662306a36Sopenharmony_ci struct sock *sk = net->ipv6.ndisc_sk; 47762306a36Sopenharmony_ci struct inet6_dev *idev; 47862306a36Sopenharmony_ci int err; 47962306a36Sopenharmony_ci struct icmp6hdr *icmp6h = icmp6_hdr(skb); 48062306a36Sopenharmony_ci u8 type; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci type = icmp6h->icmp6_type; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (!dst) { 48562306a36Sopenharmony_ci struct flowi6 fl6; 48662306a36Sopenharmony_ci int oif = skb->dev->ifindex; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci icmpv6_flow_init(sk, &fl6, type, saddr, daddr, oif); 48962306a36Sopenharmony_ci dst = icmp6_dst_alloc(skb->dev, &fl6); 49062306a36Sopenharmony_ci if (IS_ERR(dst)) { 49162306a36Sopenharmony_ci kfree_skb(skb); 49262306a36Sopenharmony_ci return; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci skb_dst_set(skb, dst); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len, 49962306a36Sopenharmony_ci IPPROTO_ICMPV6, 50062306a36Sopenharmony_ci csum_partial(icmp6h, 50162306a36Sopenharmony_ci skb->len, 0)); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci rcu_read_lock(); 50662306a36Sopenharmony_ci idev = __in6_dev_get(dst->dev); 50762306a36Sopenharmony_ci IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, 51062306a36Sopenharmony_ci net, sk, skb, NULL, dst->dev, 51162306a36Sopenharmony_ci dst_output); 51262306a36Sopenharmony_ci if (!err) { 51362306a36Sopenharmony_ci ICMP6MSGOUT_INC_STATS(net, idev, type); 51462306a36Sopenharmony_ci ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci rcu_read_unlock(); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ciEXPORT_SYMBOL(ndisc_send_skb); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_civoid ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr, 52262306a36Sopenharmony_ci const struct in6_addr *solicited_addr, 52362306a36Sopenharmony_ci bool router, bool solicited, bool override, bool inc_opt) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct sk_buff *skb; 52662306a36Sopenharmony_ci struct in6_addr tmpaddr; 52762306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 52862306a36Sopenharmony_ci const struct in6_addr *src_addr; 52962306a36Sopenharmony_ci struct nd_msg *msg; 53062306a36Sopenharmony_ci int optlen = 0; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* for anycast or proxy, solicited_addr != src_addr */ 53362306a36Sopenharmony_ci ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1); 53462306a36Sopenharmony_ci if (ifp) { 53562306a36Sopenharmony_ci src_addr = solicited_addr; 53662306a36Sopenharmony_ci if (ifp->flags & IFA_F_OPTIMISTIC) 53762306a36Sopenharmony_ci override = false; 53862306a36Sopenharmony_ci inc_opt |= ifp->idev->cnf.force_tllao; 53962306a36Sopenharmony_ci in6_ifa_put(ifp); 54062306a36Sopenharmony_ci } else { 54162306a36Sopenharmony_ci if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr, 54262306a36Sopenharmony_ci inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs, 54362306a36Sopenharmony_ci &tmpaddr)) 54462306a36Sopenharmony_ci return; 54562306a36Sopenharmony_ci src_addr = &tmpaddr; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (!dev->addr_len) 54962306a36Sopenharmony_ci inc_opt = false; 55062306a36Sopenharmony_ci if (inc_opt) 55162306a36Sopenharmony_ci optlen += ndisc_opt_addr_space(dev, 55262306a36Sopenharmony_ci NDISC_NEIGHBOUR_ADVERTISEMENT); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); 55562306a36Sopenharmony_ci if (!skb) 55662306a36Sopenharmony_ci return; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci msg = skb_put(skb, sizeof(*msg)); 55962306a36Sopenharmony_ci *msg = (struct nd_msg) { 56062306a36Sopenharmony_ci .icmph = { 56162306a36Sopenharmony_ci .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, 56262306a36Sopenharmony_ci .icmp6_router = router, 56362306a36Sopenharmony_ci .icmp6_solicited = solicited, 56462306a36Sopenharmony_ci .icmp6_override = override, 56562306a36Sopenharmony_ci }, 56662306a36Sopenharmony_ci .target = *solicited_addr, 56762306a36Sopenharmony_ci }; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (inc_opt) 57062306a36Sopenharmony_ci ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, 57162306a36Sopenharmony_ci dev->dev_addr, 57262306a36Sopenharmony_ci NDISC_NEIGHBOUR_ADVERTISEMENT); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci ndisc_send_skb(skb, daddr, src_addr); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic void ndisc_send_unsol_na(struct net_device *dev) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct inet6_dev *idev; 58062306a36Sopenharmony_ci struct inet6_ifaddr *ifa; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci idev = in6_dev_get(dev); 58362306a36Sopenharmony_ci if (!idev) 58462306a36Sopenharmony_ci return; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci read_lock_bh(&idev->lock); 58762306a36Sopenharmony_ci list_for_each_entry(ifa, &idev->addr_list, if_list) { 58862306a36Sopenharmony_ci /* skip tentative addresses until dad completes */ 58962306a36Sopenharmony_ci if (ifa->flags & IFA_F_TENTATIVE && 59062306a36Sopenharmony_ci !(ifa->flags & IFA_F_OPTIMISTIC)) 59162306a36Sopenharmony_ci continue; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci ndisc_send_na(dev, &in6addr_linklocal_allnodes, &ifa->addr, 59462306a36Sopenharmony_ci /*router=*/ !!idev->cnf.forwarding, 59562306a36Sopenharmony_ci /*solicited=*/ false, /*override=*/ true, 59662306a36Sopenharmony_ci /*inc_opt=*/ true); 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci in6_dev_put(idev); 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistruct sk_buff *ndisc_ns_create(struct net_device *dev, const struct in6_addr *solicit, 60462306a36Sopenharmony_ci const struct in6_addr *saddr, u64 nonce) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci int inc_opt = dev->addr_len; 60762306a36Sopenharmony_ci struct sk_buff *skb; 60862306a36Sopenharmony_ci struct nd_msg *msg; 60962306a36Sopenharmony_ci int optlen = 0; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (!saddr) 61262306a36Sopenharmony_ci return NULL; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (ipv6_addr_any(saddr)) 61562306a36Sopenharmony_ci inc_opt = false; 61662306a36Sopenharmony_ci if (inc_opt) 61762306a36Sopenharmony_ci optlen += ndisc_opt_addr_space(dev, 61862306a36Sopenharmony_ci NDISC_NEIGHBOUR_SOLICITATION); 61962306a36Sopenharmony_ci if (nonce != 0) 62062306a36Sopenharmony_ci optlen += 8; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); 62362306a36Sopenharmony_ci if (!skb) 62462306a36Sopenharmony_ci return NULL; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci msg = skb_put(skb, sizeof(*msg)); 62762306a36Sopenharmony_ci *msg = (struct nd_msg) { 62862306a36Sopenharmony_ci .icmph = { 62962306a36Sopenharmony_ci .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION, 63062306a36Sopenharmony_ci }, 63162306a36Sopenharmony_ci .target = *solicit, 63262306a36Sopenharmony_ci }; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (inc_opt) 63562306a36Sopenharmony_ci ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, 63662306a36Sopenharmony_ci dev->dev_addr, 63762306a36Sopenharmony_ci NDISC_NEIGHBOUR_SOLICITATION); 63862306a36Sopenharmony_ci if (nonce != 0) { 63962306a36Sopenharmony_ci u8 *opt = skb_put(skb, 8); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci opt[0] = ND_OPT_NONCE; 64262306a36Sopenharmony_ci opt[1] = 8 >> 3; 64362306a36Sopenharmony_ci memcpy(opt + 2, &nonce, 6); 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci return skb; 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ciEXPORT_SYMBOL(ndisc_ns_create); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_civoid ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, 65162306a36Sopenharmony_ci const struct in6_addr *daddr, const struct in6_addr *saddr, 65262306a36Sopenharmony_ci u64 nonce) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct in6_addr addr_buf; 65562306a36Sopenharmony_ci struct sk_buff *skb; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (!saddr) { 65862306a36Sopenharmony_ci if (ipv6_get_lladdr(dev, &addr_buf, 65962306a36Sopenharmony_ci (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC))) 66062306a36Sopenharmony_ci return; 66162306a36Sopenharmony_ci saddr = &addr_buf; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci skb = ndisc_ns_create(dev, solicit, saddr, nonce); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (skb) 66762306a36Sopenharmony_ci ndisc_send_skb(skb, daddr, saddr); 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_civoid ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, 67162306a36Sopenharmony_ci const struct in6_addr *daddr) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct sk_buff *skb; 67462306a36Sopenharmony_ci struct rs_msg *msg; 67562306a36Sopenharmony_ci int send_sllao = dev->addr_len; 67662306a36Sopenharmony_ci int optlen = 0; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 67962306a36Sopenharmony_ci /* 68062306a36Sopenharmony_ci * According to section 2.2 of RFC 4429, we must not 68162306a36Sopenharmony_ci * send router solicitations with a sllao from 68262306a36Sopenharmony_ci * optimistic addresses, but we may send the solicitation 68362306a36Sopenharmony_ci * if we don't include the sllao. So here we check 68462306a36Sopenharmony_ci * if our address is optimistic, and if so, we 68562306a36Sopenharmony_ci * suppress the inclusion of the sllao. 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_ci if (send_sllao) { 68862306a36Sopenharmony_ci struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr, 68962306a36Sopenharmony_ci dev, 1); 69062306a36Sopenharmony_ci if (ifp) { 69162306a36Sopenharmony_ci if (ifp->flags & IFA_F_OPTIMISTIC) { 69262306a36Sopenharmony_ci send_sllao = 0; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci in6_ifa_put(ifp); 69562306a36Sopenharmony_ci } else { 69662306a36Sopenharmony_ci send_sllao = 0; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci#endif 70062306a36Sopenharmony_ci if (send_sllao) 70162306a36Sopenharmony_ci optlen += ndisc_opt_addr_space(dev, NDISC_ROUTER_SOLICITATION); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); 70462306a36Sopenharmony_ci if (!skb) 70562306a36Sopenharmony_ci return; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci msg = skb_put(skb, sizeof(*msg)); 70862306a36Sopenharmony_ci *msg = (struct rs_msg) { 70962306a36Sopenharmony_ci .icmph = { 71062306a36Sopenharmony_ci .icmp6_type = NDISC_ROUTER_SOLICITATION, 71162306a36Sopenharmony_ci }, 71262306a36Sopenharmony_ci }; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (send_sllao) 71562306a36Sopenharmony_ci ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, 71662306a36Sopenharmony_ci dev->dev_addr, 71762306a36Sopenharmony_ci NDISC_ROUTER_SOLICITATION); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci ndisc_send_skb(skb, daddr, saddr); 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci /* 72662306a36Sopenharmony_ci * "The sender MUST return an ICMP 72762306a36Sopenharmony_ci * destination unreachable" 72862306a36Sopenharmony_ci */ 72962306a36Sopenharmony_ci dst_link_failure(skb); 73062306a36Sopenharmony_ci kfree_skb(skb); 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci/* Called with locked neigh: either read or both */ 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct in6_addr *saddr = NULL; 73862306a36Sopenharmony_ci struct in6_addr mcaddr; 73962306a36Sopenharmony_ci struct net_device *dev = neigh->dev; 74062306a36Sopenharmony_ci struct in6_addr *target = (struct in6_addr *)&neigh->primary_key; 74162306a36Sopenharmony_ci int probes = atomic_read(&neigh->probes); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (skb && ipv6_chk_addr_and_flags(dev_net(dev), &ipv6_hdr(skb)->saddr, 74462306a36Sopenharmony_ci dev, false, 1, 74562306a36Sopenharmony_ci IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) 74662306a36Sopenharmony_ci saddr = &ipv6_hdr(skb)->saddr; 74762306a36Sopenharmony_ci probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES); 74862306a36Sopenharmony_ci if (probes < 0) { 74962306a36Sopenharmony_ci if (!(READ_ONCE(neigh->nud_state) & NUD_VALID)) { 75062306a36Sopenharmony_ci ND_PRINTK(1, dbg, 75162306a36Sopenharmony_ci "%s: trying to ucast probe in NUD_INVALID: %pI6\n", 75262306a36Sopenharmony_ci __func__, target); 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci ndisc_send_ns(dev, target, target, saddr, 0); 75562306a36Sopenharmony_ci } else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) { 75662306a36Sopenharmony_ci neigh_app_ns(neigh); 75762306a36Sopenharmony_ci } else { 75862306a36Sopenharmony_ci addrconf_addr_solict_mult(target, &mcaddr); 75962306a36Sopenharmony_ci ndisc_send_ns(dev, target, &mcaddr, saddr, 0); 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic int pndisc_is_router(const void *pkey, 76462306a36Sopenharmony_ci struct net_device *dev) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci struct pneigh_entry *n; 76762306a36Sopenharmony_ci int ret = -1; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci read_lock_bh(&nd_tbl.lock); 77062306a36Sopenharmony_ci n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev); 77162306a36Sopenharmony_ci if (n) 77262306a36Sopenharmony_ci ret = !!(n->flags & NTF_ROUTER); 77362306a36Sopenharmony_ci read_unlock_bh(&nd_tbl.lock); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci return ret; 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_civoid ndisc_update(const struct net_device *dev, struct neighbour *neigh, 77962306a36Sopenharmony_ci const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type, 78062306a36Sopenharmony_ci struct ndisc_options *ndopts) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci neigh_update(neigh, lladdr, new, flags, 0); 78362306a36Sopenharmony_ci /* report ndisc ops about neighbour update */ 78462306a36Sopenharmony_ci ndisc_ops_update(dev, neigh, flags, icmp6_type, ndopts); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); 79062306a36Sopenharmony_ci const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; 79162306a36Sopenharmony_ci const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; 79262306a36Sopenharmony_ci u8 *lladdr = NULL; 79362306a36Sopenharmony_ci u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + 79462306a36Sopenharmony_ci offsetof(struct nd_msg, opt)); 79562306a36Sopenharmony_ci struct ndisc_options ndopts; 79662306a36Sopenharmony_ci struct net_device *dev = skb->dev; 79762306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 79862306a36Sopenharmony_ci struct inet6_dev *idev = NULL; 79962306a36Sopenharmony_ci struct neighbour *neigh; 80062306a36Sopenharmony_ci int dad = ipv6_addr_any(saddr); 80162306a36Sopenharmony_ci int is_router = -1; 80262306a36Sopenharmony_ci SKB_DR(reason); 80362306a36Sopenharmony_ci u64 nonce = 0; 80462306a36Sopenharmony_ci bool inc; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (skb->len < sizeof(struct nd_msg)) 80762306a36Sopenharmony_ci return SKB_DROP_REASON_PKT_TOO_SMALL; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (ipv6_addr_is_multicast(&msg->target)) { 81062306a36Sopenharmony_ci ND_PRINTK(2, warn, "NS: multicast target address\n"); 81162306a36Sopenharmony_ci return reason; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* 81562306a36Sopenharmony_ci * RFC2461 7.1.1: 81662306a36Sopenharmony_ci * DAD has to be destined for solicited node multicast address. 81762306a36Sopenharmony_ci */ 81862306a36Sopenharmony_ci if (dad && !ipv6_addr_is_solict_mult(daddr)) { 81962306a36Sopenharmony_ci ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n"); 82062306a36Sopenharmony_ci return reason; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) 82462306a36Sopenharmony_ci return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (ndopts.nd_opts_src_lladdr) { 82762306a36Sopenharmony_ci lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev); 82862306a36Sopenharmony_ci if (!lladdr) { 82962306a36Sopenharmony_ci ND_PRINTK(2, warn, 83062306a36Sopenharmony_ci "NS: invalid link-layer address length\n"); 83162306a36Sopenharmony_ci return reason; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci /* RFC2461 7.1.1: 83562306a36Sopenharmony_ci * If the IP source address is the unspecified address, 83662306a36Sopenharmony_ci * there MUST NOT be source link-layer address option 83762306a36Sopenharmony_ci * in the message. 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_ci if (dad) { 84062306a36Sopenharmony_ci ND_PRINTK(2, warn, 84162306a36Sopenharmony_ci "NS: bad DAD packet (link-layer address option)\n"); 84262306a36Sopenharmony_ci return reason; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci if (ndopts.nd_opts_nonce && ndopts.nd_opts_nonce->nd_opt_len == 1) 84662306a36Sopenharmony_ci memcpy(&nonce, (u8 *)(ndopts.nd_opts_nonce + 1), 6); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci inc = ipv6_addr_is_multicast(daddr); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1); 85162306a36Sopenharmony_ci if (ifp) { 85262306a36Sopenharmony_cihave_ifp: 85362306a36Sopenharmony_ci if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) { 85462306a36Sopenharmony_ci if (dad) { 85562306a36Sopenharmony_ci if (nonce != 0 && ifp->dad_nonce == nonce) { 85662306a36Sopenharmony_ci u8 *np = (u8 *)&nonce; 85762306a36Sopenharmony_ci /* Matching nonce if looped back */ 85862306a36Sopenharmony_ci ND_PRINTK(2, notice, 85962306a36Sopenharmony_ci "%s: IPv6 DAD loopback for address %pI6c nonce %pM ignored\n", 86062306a36Sopenharmony_ci ifp->idev->dev->name, 86162306a36Sopenharmony_ci &ifp->addr, np); 86262306a36Sopenharmony_ci goto out; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci /* 86562306a36Sopenharmony_ci * We are colliding with another node 86662306a36Sopenharmony_ci * who is doing DAD 86762306a36Sopenharmony_ci * so fail our DAD process 86862306a36Sopenharmony_ci */ 86962306a36Sopenharmony_ci addrconf_dad_failure(skb, ifp); 87062306a36Sopenharmony_ci return reason; 87162306a36Sopenharmony_ci } else { 87262306a36Sopenharmony_ci /* 87362306a36Sopenharmony_ci * This is not a dad solicitation. 87462306a36Sopenharmony_ci * If we are an optimistic node, 87562306a36Sopenharmony_ci * we should respond. 87662306a36Sopenharmony_ci * Otherwise, we should ignore it. 87762306a36Sopenharmony_ci */ 87862306a36Sopenharmony_ci if (!(ifp->flags & IFA_F_OPTIMISTIC)) 87962306a36Sopenharmony_ci goto out; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci idev = ifp->idev; 88462306a36Sopenharmony_ci } else { 88562306a36Sopenharmony_ci struct net *net = dev_net(dev); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci /* perhaps an address on the master device */ 88862306a36Sopenharmony_ci if (netif_is_l3_slave(dev)) { 88962306a36Sopenharmony_ci struct net_device *mdev; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci mdev = netdev_master_upper_dev_get_rcu(dev); 89262306a36Sopenharmony_ci if (mdev) { 89362306a36Sopenharmony_ci ifp = ipv6_get_ifaddr(net, &msg->target, mdev, 1); 89462306a36Sopenharmony_ci if (ifp) 89562306a36Sopenharmony_ci goto have_ifp; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci idev = in6_dev_get(dev); 90062306a36Sopenharmony_ci if (!idev) { 90162306a36Sopenharmony_ci /* XXX: count this drop? */ 90262306a36Sopenharmony_ci return reason; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (ipv6_chk_acast_addr(net, dev, &msg->target) || 90662306a36Sopenharmony_ci (idev->cnf.forwarding && 90762306a36Sopenharmony_ci (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) && 90862306a36Sopenharmony_ci (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) { 90962306a36Sopenharmony_ci if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) && 91062306a36Sopenharmony_ci skb->pkt_type != PACKET_HOST && 91162306a36Sopenharmony_ci inc && 91262306a36Sopenharmony_ci NEIGH_VAR(idev->nd_parms, PROXY_DELAY) != 0) { 91362306a36Sopenharmony_ci /* 91462306a36Sopenharmony_ci * for anycast or proxy, 91562306a36Sopenharmony_ci * sender should delay its response 91662306a36Sopenharmony_ci * by a random time between 0 and 91762306a36Sopenharmony_ci * MAX_ANYCAST_DELAY_TIME seconds. 91862306a36Sopenharmony_ci * (RFC2461) -- yoshfuji 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_ci struct sk_buff *n = skb_clone(skb, GFP_ATOMIC); 92162306a36Sopenharmony_ci if (n) 92262306a36Sopenharmony_ci pneigh_enqueue(&nd_tbl, idev->nd_parms, n); 92362306a36Sopenharmony_ci goto out; 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci } else { 92662306a36Sopenharmony_ci SKB_DR_SET(reason, IPV6_NDISC_NS_OTHERHOST); 92762306a36Sopenharmony_ci goto out; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (is_router < 0) 93262306a36Sopenharmony_ci is_router = idev->cnf.forwarding; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (dad) { 93562306a36Sopenharmony_ci ndisc_send_na(dev, &in6addr_linklocal_allnodes, &msg->target, 93662306a36Sopenharmony_ci !!is_router, false, (ifp != NULL), true); 93762306a36Sopenharmony_ci goto out; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (inc) 94162306a36Sopenharmony_ci NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast); 94262306a36Sopenharmony_ci else 94362306a36Sopenharmony_ci NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci /* 94662306a36Sopenharmony_ci * update / create cache entry 94762306a36Sopenharmony_ci * for the source address 94862306a36Sopenharmony_ci */ 94962306a36Sopenharmony_ci neigh = __neigh_lookup(&nd_tbl, saddr, dev, 95062306a36Sopenharmony_ci !inc || lladdr || !dev->addr_len); 95162306a36Sopenharmony_ci if (neigh) 95262306a36Sopenharmony_ci ndisc_update(dev, neigh, lladdr, NUD_STALE, 95362306a36Sopenharmony_ci NEIGH_UPDATE_F_WEAK_OVERRIDE| 95462306a36Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE, 95562306a36Sopenharmony_ci NDISC_NEIGHBOUR_SOLICITATION, &ndopts); 95662306a36Sopenharmony_ci if (neigh || !dev->header_ops) { 95762306a36Sopenharmony_ci ndisc_send_na(dev, saddr, &msg->target, !!is_router, 95862306a36Sopenharmony_ci true, (ifp != NULL && inc), inc); 95962306a36Sopenharmony_ci if (neigh) 96062306a36Sopenharmony_ci neigh_release(neigh); 96162306a36Sopenharmony_ci reason = SKB_CONSUMED; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ciout: 96562306a36Sopenharmony_ci if (ifp) 96662306a36Sopenharmony_ci in6_ifa_put(ifp); 96762306a36Sopenharmony_ci else 96862306a36Sopenharmony_ci in6_dev_put(idev); 96962306a36Sopenharmony_ci return reason; 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci switch (idev->cnf.accept_untracked_na) { 97762306a36Sopenharmony_ci case 0: /* Don't accept untracked na (absent in neighbor cache) */ 97862306a36Sopenharmony_ci return 0; 97962306a36Sopenharmony_ci case 1: /* Create new entries from na if currently untracked */ 98062306a36Sopenharmony_ci return 1; 98162306a36Sopenharmony_ci case 2: /* Create new entries from untracked na only if saddr is in the 98262306a36Sopenharmony_ci * same subnet as an address configured on the interface that 98362306a36Sopenharmony_ci * received the na 98462306a36Sopenharmony_ci */ 98562306a36Sopenharmony_ci return !!ipv6_chk_prefix(saddr, dev); 98662306a36Sopenharmony_ci default: 98762306a36Sopenharmony_ci return 0; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); 99462306a36Sopenharmony_ci struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; 99562306a36Sopenharmony_ci const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; 99662306a36Sopenharmony_ci u8 *lladdr = NULL; 99762306a36Sopenharmony_ci u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + 99862306a36Sopenharmony_ci offsetof(struct nd_msg, opt)); 99962306a36Sopenharmony_ci struct ndisc_options ndopts; 100062306a36Sopenharmony_ci struct net_device *dev = skb->dev; 100162306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 100262306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 100362306a36Sopenharmony_ci struct neighbour *neigh; 100462306a36Sopenharmony_ci SKB_DR(reason); 100562306a36Sopenharmony_ci u8 new_state; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (skb->len < sizeof(struct nd_msg)) 100862306a36Sopenharmony_ci return SKB_DROP_REASON_PKT_TOO_SMALL; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci if (ipv6_addr_is_multicast(&msg->target)) { 101162306a36Sopenharmony_ci ND_PRINTK(2, warn, "NA: target address is multicast\n"); 101262306a36Sopenharmony_ci return reason; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (ipv6_addr_is_multicast(daddr) && 101662306a36Sopenharmony_ci msg->icmph.icmp6_solicited) { 101762306a36Sopenharmony_ci ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n"); 101862306a36Sopenharmony_ci return reason; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* For some 802.11 wireless deployments (and possibly other networks), 102262306a36Sopenharmony_ci * there will be a NA proxy and unsolicitd packets are attacks 102362306a36Sopenharmony_ci * and thus should not be accepted. 102462306a36Sopenharmony_ci * drop_unsolicited_na takes precedence over accept_untracked_na 102562306a36Sopenharmony_ci */ 102662306a36Sopenharmony_ci if (!msg->icmph.icmp6_solicited && idev && 102762306a36Sopenharmony_ci idev->cnf.drop_unsolicited_na) 102862306a36Sopenharmony_ci return reason; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) 103162306a36Sopenharmony_ci return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (ndopts.nd_opts_tgt_lladdr) { 103462306a36Sopenharmony_ci lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev); 103562306a36Sopenharmony_ci if (!lladdr) { 103662306a36Sopenharmony_ci ND_PRINTK(2, warn, 103762306a36Sopenharmony_ci "NA: invalid link-layer address length\n"); 103862306a36Sopenharmony_ci return reason; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1); 104262306a36Sopenharmony_ci if (ifp) { 104362306a36Sopenharmony_ci if (skb->pkt_type != PACKET_LOOPBACK 104462306a36Sopenharmony_ci && (ifp->flags & IFA_F_TENTATIVE)) { 104562306a36Sopenharmony_ci addrconf_dad_failure(skb, ifp); 104662306a36Sopenharmony_ci return reason; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci /* What should we make now? The advertisement 104962306a36Sopenharmony_ci is invalid, but ndisc specs say nothing 105062306a36Sopenharmony_ci about it. It could be misconfiguration, or 105162306a36Sopenharmony_ci an smart proxy agent tries to help us :-) 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci We should not print the error if NA has been 105462306a36Sopenharmony_ci received from loopback - it is just our own 105562306a36Sopenharmony_ci unsolicited advertisement. 105662306a36Sopenharmony_ci */ 105762306a36Sopenharmony_ci if (skb->pkt_type != PACKET_LOOPBACK) 105862306a36Sopenharmony_ci ND_PRINTK(1, warn, 105962306a36Sopenharmony_ci "NA: %pM advertised our address %pI6c on %s!\n", 106062306a36Sopenharmony_ci eth_hdr(skb)->h_source, &ifp->addr, ifp->idev->dev->name); 106162306a36Sopenharmony_ci in6_ifa_put(ifp); 106262306a36Sopenharmony_ci return reason; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci neigh = neigh_lookup(&nd_tbl, &msg->target, dev); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci /* RFC 9131 updates original Neighbour Discovery RFC 4861. 106862306a36Sopenharmony_ci * NAs with Target LL Address option without a corresponding 106962306a36Sopenharmony_ci * entry in the neighbour cache can now create a STALE neighbour 107062306a36Sopenharmony_ci * cache entry on routers. 107162306a36Sopenharmony_ci * 107262306a36Sopenharmony_ci * entry accept fwding solicited behaviour 107362306a36Sopenharmony_ci * ------- ------ ------ --------- ---------------------- 107462306a36Sopenharmony_ci * present X X 0 Set state to STALE 107562306a36Sopenharmony_ci * present X X 1 Set state to REACHABLE 107662306a36Sopenharmony_ci * absent 0 X X Do nothing 107762306a36Sopenharmony_ci * absent 1 0 X Do nothing 107862306a36Sopenharmony_ci * absent 1 1 X Add a new STALE entry 107962306a36Sopenharmony_ci * 108062306a36Sopenharmony_ci * Note that we don't do a (daddr == all-routers-mcast) check. 108162306a36Sopenharmony_ci */ 108262306a36Sopenharmony_ci new_state = msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE; 108362306a36Sopenharmony_ci if (!neigh && lladdr && idev && idev->cnf.forwarding) { 108462306a36Sopenharmony_ci if (accept_untracked_na(dev, saddr)) { 108562306a36Sopenharmony_ci neigh = neigh_create(&nd_tbl, &msg->target, dev); 108662306a36Sopenharmony_ci new_state = NUD_STALE; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (neigh && !IS_ERR(neigh)) { 109162306a36Sopenharmony_ci u8 old_flags = neigh->flags; 109262306a36Sopenharmony_ci struct net *net = dev_net(dev); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci if (READ_ONCE(neigh->nud_state) & NUD_FAILED) 109562306a36Sopenharmony_ci goto out; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* 109862306a36Sopenharmony_ci * Don't update the neighbor cache entry on a proxy NA from 109962306a36Sopenharmony_ci * ourselves because either the proxied node is off link or it 110062306a36Sopenharmony_ci * has already sent a NA to us. 110162306a36Sopenharmony_ci */ 110262306a36Sopenharmony_ci if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) && 110362306a36Sopenharmony_ci net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp && 110462306a36Sopenharmony_ci pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) { 110562306a36Sopenharmony_ci /* XXX: idev->cnf.proxy_ndp */ 110662306a36Sopenharmony_ci goto out; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci ndisc_update(dev, neigh, lladdr, 111062306a36Sopenharmony_ci new_state, 111162306a36Sopenharmony_ci NEIGH_UPDATE_F_WEAK_OVERRIDE| 111262306a36Sopenharmony_ci (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)| 111362306a36Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 111462306a36Sopenharmony_ci (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0), 111562306a36Sopenharmony_ci NDISC_NEIGHBOUR_ADVERTISEMENT, &ndopts); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci if ((old_flags & ~neigh->flags) & NTF_ROUTER) { 111862306a36Sopenharmony_ci /* 111962306a36Sopenharmony_ci * Change: router to host 112062306a36Sopenharmony_ci */ 112162306a36Sopenharmony_ci rt6_clean_tohost(dev_net(dev), saddr); 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci reason = SKB_CONSUMED; 112462306a36Sopenharmony_ciout: 112562306a36Sopenharmony_ci neigh_release(neigh); 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci return reason; 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb); 113362306a36Sopenharmony_ci unsigned long ndoptlen = skb->len - sizeof(*rs_msg); 113462306a36Sopenharmony_ci struct neighbour *neigh; 113562306a36Sopenharmony_ci struct inet6_dev *idev; 113662306a36Sopenharmony_ci const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; 113762306a36Sopenharmony_ci struct ndisc_options ndopts; 113862306a36Sopenharmony_ci u8 *lladdr = NULL; 113962306a36Sopenharmony_ci SKB_DR(reason); 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci if (skb->len < sizeof(*rs_msg)) 114262306a36Sopenharmony_ci return SKB_DROP_REASON_PKT_TOO_SMALL; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci idev = __in6_dev_get(skb->dev); 114562306a36Sopenharmony_ci if (!idev) { 114662306a36Sopenharmony_ci ND_PRINTK(1, err, "RS: can't find in6 device\n"); 114762306a36Sopenharmony_ci return reason; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci /* Don't accept RS if we're not in router mode */ 115162306a36Sopenharmony_ci if (!idev->cnf.forwarding) 115262306a36Sopenharmony_ci goto out; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci /* 115562306a36Sopenharmony_ci * Don't update NCE if src = ::; 115662306a36Sopenharmony_ci * this implies that the source node has no ip address assigned yet. 115762306a36Sopenharmony_ci */ 115862306a36Sopenharmony_ci if (ipv6_addr_any(saddr)) 115962306a36Sopenharmony_ci goto out; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci /* Parse ND options */ 116262306a36Sopenharmony_ci if (!ndisc_parse_options(skb->dev, rs_msg->opt, ndoptlen, &ndopts)) 116362306a36Sopenharmony_ci return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (ndopts.nd_opts_src_lladdr) { 116662306a36Sopenharmony_ci lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, 116762306a36Sopenharmony_ci skb->dev); 116862306a36Sopenharmony_ci if (!lladdr) 116962306a36Sopenharmony_ci goto out; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1); 117362306a36Sopenharmony_ci if (neigh) { 117462306a36Sopenharmony_ci ndisc_update(skb->dev, neigh, lladdr, NUD_STALE, 117562306a36Sopenharmony_ci NEIGH_UPDATE_F_WEAK_OVERRIDE| 117662306a36Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE| 117762306a36Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE_ISROUTER, 117862306a36Sopenharmony_ci NDISC_ROUTER_SOLICITATION, &ndopts); 117962306a36Sopenharmony_ci neigh_release(neigh); 118062306a36Sopenharmony_ci reason = SKB_CONSUMED; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ciout: 118362306a36Sopenharmony_ci return reason; 118462306a36Sopenharmony_ci} 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistatic void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra); 118962306a36Sopenharmony_ci struct sk_buff *skb; 119062306a36Sopenharmony_ci struct nlmsghdr *nlh; 119162306a36Sopenharmony_ci struct nduseroptmsg *ndmsg; 119262306a36Sopenharmony_ci struct net *net = dev_net(ra->dev); 119362306a36Sopenharmony_ci int err; 119462306a36Sopenharmony_ci int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg) 119562306a36Sopenharmony_ci + (opt->nd_opt_len << 3)); 119662306a36Sopenharmony_ci size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr)); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci skb = nlmsg_new(msg_size, GFP_ATOMIC); 119962306a36Sopenharmony_ci if (!skb) { 120062306a36Sopenharmony_ci err = -ENOBUFS; 120162306a36Sopenharmony_ci goto errout; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0); 120562306a36Sopenharmony_ci if (!nlh) { 120662306a36Sopenharmony_ci goto nla_put_failure; 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci ndmsg = nlmsg_data(nlh); 121062306a36Sopenharmony_ci ndmsg->nduseropt_family = AF_INET6; 121162306a36Sopenharmony_ci ndmsg->nduseropt_ifindex = ra->dev->ifindex; 121262306a36Sopenharmony_ci ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type; 121362306a36Sopenharmony_ci ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code; 121462306a36Sopenharmony_ci ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (nla_put_in6_addr(skb, NDUSEROPT_SRCADDR, &ipv6_hdr(ra)->saddr)) 121962306a36Sopenharmony_ci goto nla_put_failure; 122062306a36Sopenharmony_ci nlmsg_end(skb, nlh); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC); 122362306a36Sopenharmony_ci return; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_cinla_put_failure: 122662306a36Sopenharmony_ci nlmsg_free(skb); 122762306a36Sopenharmony_ci err = -EMSGSIZE; 122862306a36Sopenharmony_cierrout: 122962306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err); 123062306a36Sopenharmony_ci} 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_cistatic enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb); 123562306a36Sopenharmony_ci bool send_ifinfo_notify = false; 123662306a36Sopenharmony_ci struct neighbour *neigh = NULL; 123762306a36Sopenharmony_ci struct ndisc_options ndopts; 123862306a36Sopenharmony_ci struct fib6_info *rt = NULL; 123962306a36Sopenharmony_ci struct inet6_dev *in6_dev; 124062306a36Sopenharmony_ci u32 defrtr_usr_metric; 124162306a36Sopenharmony_ci unsigned int pref = 0; 124262306a36Sopenharmony_ci __u32 old_if_flags; 124362306a36Sopenharmony_ci struct net *net; 124462306a36Sopenharmony_ci SKB_DR(reason); 124562306a36Sopenharmony_ci int lifetime; 124662306a36Sopenharmony_ci int optlen; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci __u8 *opt = (__u8 *)(ra_msg + 1); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci optlen = (skb_tail_pointer(skb) - skb_transport_header(skb)) - 125162306a36Sopenharmony_ci sizeof(struct ra_msg); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci ND_PRINTK(2, info, 125462306a36Sopenharmony_ci "RA: %s, dev: %s\n", 125562306a36Sopenharmony_ci __func__, skb->dev->name); 125662306a36Sopenharmony_ci if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) { 125762306a36Sopenharmony_ci ND_PRINTK(2, warn, "RA: source address is not link-local\n"); 125862306a36Sopenharmony_ci return reason; 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci if (optlen < 0) 126162306a36Sopenharmony_ci return SKB_DROP_REASON_PKT_TOO_SMALL; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE 126462306a36Sopenharmony_ci if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) { 126562306a36Sopenharmony_ci ND_PRINTK(2, warn, "RA: from host or unauthorized router\n"); 126662306a36Sopenharmony_ci return reason; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci#endif 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci in6_dev = __in6_dev_get(skb->dev); 127162306a36Sopenharmony_ci if (!in6_dev) { 127262306a36Sopenharmony_ci ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n", 127362306a36Sopenharmony_ci skb->dev->name); 127462306a36Sopenharmony_ci return reason; 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci if (!ndisc_parse_options(skb->dev, opt, optlen, &ndopts)) 127862306a36Sopenharmony_ci return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (!ipv6_accept_ra(in6_dev)) { 128162306a36Sopenharmony_ci ND_PRINTK(2, info, 128262306a36Sopenharmony_ci "RA: %s, did not accept ra for dev: %s\n", 128362306a36Sopenharmony_ci __func__, skb->dev->name); 128462306a36Sopenharmony_ci goto skip_linkparms; 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE 128862306a36Sopenharmony_ci /* skip link-specific parameters from interior routers */ 128962306a36Sopenharmony_ci if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) { 129062306a36Sopenharmony_ci ND_PRINTK(2, info, 129162306a36Sopenharmony_ci "RA: %s, nodetype is NODEFAULT, dev: %s\n", 129262306a36Sopenharmony_ci __func__, skb->dev->name); 129362306a36Sopenharmony_ci goto skip_linkparms; 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci#endif 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (in6_dev->if_flags & IF_RS_SENT) { 129862306a36Sopenharmony_ci /* 129962306a36Sopenharmony_ci * flag that an RA was received after an RS was sent 130062306a36Sopenharmony_ci * out on this interface. 130162306a36Sopenharmony_ci */ 130262306a36Sopenharmony_ci in6_dev->if_flags |= IF_RA_RCVD; 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* 130662306a36Sopenharmony_ci * Remember the managed/otherconf flags from most recently 130762306a36Sopenharmony_ci * received RA message (RFC 2462) -- yoshfuji 130862306a36Sopenharmony_ci */ 130962306a36Sopenharmony_ci old_if_flags = in6_dev->if_flags; 131062306a36Sopenharmony_ci in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED | 131162306a36Sopenharmony_ci IF_RA_OTHERCONF)) | 131262306a36Sopenharmony_ci (ra_msg->icmph.icmp6_addrconf_managed ? 131362306a36Sopenharmony_ci IF_RA_MANAGED : 0) | 131462306a36Sopenharmony_ci (ra_msg->icmph.icmp6_addrconf_other ? 131562306a36Sopenharmony_ci IF_RA_OTHERCONF : 0); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if (old_if_flags != in6_dev->if_flags) 131862306a36Sopenharmony_ci send_ifinfo_notify = true; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci if (!in6_dev->cnf.accept_ra_defrtr) { 132162306a36Sopenharmony_ci ND_PRINTK(2, info, 132262306a36Sopenharmony_ci "RA: %s, defrtr is false for dev: %s\n", 132362306a36Sopenharmony_ci __func__, skb->dev->name); 132462306a36Sopenharmony_ci goto skip_defrtr; 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime); 132862306a36Sopenharmony_ci if (lifetime != 0 && lifetime < in6_dev->cnf.accept_ra_min_lft) { 132962306a36Sopenharmony_ci ND_PRINTK(2, info, 133062306a36Sopenharmony_ci "RA: router lifetime (%ds) is too short: %s\n", 133162306a36Sopenharmony_ci lifetime, skb->dev->name); 133262306a36Sopenharmony_ci goto skip_defrtr; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci /* Do not accept RA with source-addr found on local machine unless 133662306a36Sopenharmony_ci * accept_ra_from_local is set to true. 133762306a36Sopenharmony_ci */ 133862306a36Sopenharmony_ci net = dev_net(in6_dev->dev); 133962306a36Sopenharmony_ci if (!in6_dev->cnf.accept_ra_from_local && 134062306a36Sopenharmony_ci ipv6_chk_addr(net, &ipv6_hdr(skb)->saddr, in6_dev->dev, 0)) { 134162306a36Sopenharmony_ci ND_PRINTK(2, info, 134262306a36Sopenharmony_ci "RA from local address detected on dev: %s: default router ignored\n", 134362306a36Sopenharmony_ci skb->dev->name); 134462306a36Sopenharmony_ci goto skip_defrtr; 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 134862306a36Sopenharmony_ci pref = ra_msg->icmph.icmp6_router_pref; 134962306a36Sopenharmony_ci /* 10b is handled as if it were 00b (medium) */ 135062306a36Sopenharmony_ci if (pref == ICMPV6_ROUTER_PREF_INVALID || 135162306a36Sopenharmony_ci !in6_dev->cnf.accept_ra_rtr_pref) 135262306a36Sopenharmony_ci pref = ICMPV6_ROUTER_PREF_MEDIUM; 135362306a36Sopenharmony_ci#endif 135462306a36Sopenharmony_ci /* routes added from RAs do not use nexthop objects */ 135562306a36Sopenharmony_ci rt = rt6_get_dflt_router(net, &ipv6_hdr(skb)->saddr, skb->dev); 135662306a36Sopenharmony_ci if (rt) { 135762306a36Sopenharmony_ci neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6, 135862306a36Sopenharmony_ci rt->fib6_nh->fib_nh_dev, NULL, 135962306a36Sopenharmony_ci &ipv6_hdr(skb)->saddr); 136062306a36Sopenharmony_ci if (!neigh) { 136162306a36Sopenharmony_ci ND_PRINTK(0, err, 136262306a36Sopenharmony_ci "RA: %s got default router without neighbour\n", 136362306a36Sopenharmony_ci __func__); 136462306a36Sopenharmony_ci fib6_info_release(rt); 136562306a36Sopenharmony_ci return reason; 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci /* Set default route metric as specified by user */ 136962306a36Sopenharmony_ci defrtr_usr_metric = in6_dev->cnf.ra_defrtr_metric; 137062306a36Sopenharmony_ci /* delete the route if lifetime is 0 or if metric needs change */ 137162306a36Sopenharmony_ci if (rt && (lifetime == 0 || rt->fib6_metric != defrtr_usr_metric)) { 137262306a36Sopenharmony_ci ip6_del_rt(net, rt, false); 137362306a36Sopenharmony_ci rt = NULL; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci ND_PRINTK(3, info, "RA: rt: %p lifetime: %d, metric: %d, for dev: %s\n", 137762306a36Sopenharmony_ci rt, lifetime, defrtr_usr_metric, skb->dev->name); 137862306a36Sopenharmony_ci if (!rt && lifetime) { 137962306a36Sopenharmony_ci ND_PRINTK(3, info, "RA: adding default router\n"); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci if (neigh) 138262306a36Sopenharmony_ci neigh_release(neigh); 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci rt = rt6_add_dflt_router(net, &ipv6_hdr(skb)->saddr, 138562306a36Sopenharmony_ci skb->dev, pref, defrtr_usr_metric); 138662306a36Sopenharmony_ci if (!rt) { 138762306a36Sopenharmony_ci ND_PRINTK(0, err, 138862306a36Sopenharmony_ci "RA: %s failed to add default route\n", 138962306a36Sopenharmony_ci __func__); 139062306a36Sopenharmony_ci return reason; 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6, 139462306a36Sopenharmony_ci rt->fib6_nh->fib_nh_dev, NULL, 139562306a36Sopenharmony_ci &ipv6_hdr(skb)->saddr); 139662306a36Sopenharmony_ci if (!neigh) { 139762306a36Sopenharmony_ci ND_PRINTK(0, err, 139862306a36Sopenharmony_ci "RA: %s got default router without neighbour\n", 139962306a36Sopenharmony_ci __func__); 140062306a36Sopenharmony_ci fib6_info_release(rt); 140162306a36Sopenharmony_ci return reason; 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci neigh->flags |= NTF_ROUTER; 140462306a36Sopenharmony_ci } else if (rt && IPV6_EXTRACT_PREF(rt->fib6_flags) != pref) { 140562306a36Sopenharmony_ci struct nl_info nlinfo = { 140662306a36Sopenharmony_ci .nl_net = net, 140762306a36Sopenharmony_ci }; 140862306a36Sopenharmony_ci rt->fib6_flags = (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 140962306a36Sopenharmony_ci inet6_rt_notify(RTM_NEWROUTE, rt, &nlinfo, NLM_F_REPLACE); 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci if (rt) 141362306a36Sopenharmony_ci fib6_set_expires(rt, jiffies + (HZ * lifetime)); 141462306a36Sopenharmony_ci if (in6_dev->cnf.accept_ra_min_hop_limit < 256 && 141562306a36Sopenharmony_ci ra_msg->icmph.icmp6_hop_limit) { 141662306a36Sopenharmony_ci if (in6_dev->cnf.accept_ra_min_hop_limit <= ra_msg->icmph.icmp6_hop_limit) { 141762306a36Sopenharmony_ci in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit; 141862306a36Sopenharmony_ci fib6_metric_set(rt, RTAX_HOPLIMIT, 141962306a36Sopenharmony_ci ra_msg->icmph.icmp6_hop_limit); 142062306a36Sopenharmony_ci } else { 142162306a36Sopenharmony_ci ND_PRINTK(2, warn, "RA: Got route advertisement with lower hop_limit than minimum\n"); 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci } 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ciskip_defrtr: 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci /* 142862306a36Sopenharmony_ci * Update Reachable Time and Retrans Timer 142962306a36Sopenharmony_ci */ 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci if (in6_dev->nd_parms) { 143262306a36Sopenharmony_ci unsigned long rtime = ntohl(ra_msg->retrans_timer); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) { 143562306a36Sopenharmony_ci rtime = (rtime*HZ)/1000; 143662306a36Sopenharmony_ci if (rtime < HZ/100) 143762306a36Sopenharmony_ci rtime = HZ/100; 143862306a36Sopenharmony_ci NEIGH_VAR_SET(in6_dev->nd_parms, RETRANS_TIME, rtime); 143962306a36Sopenharmony_ci in6_dev->tstamp = jiffies; 144062306a36Sopenharmony_ci send_ifinfo_notify = true; 144162306a36Sopenharmony_ci } 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci rtime = ntohl(ra_msg->reachable_time); 144462306a36Sopenharmony_ci if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) { 144562306a36Sopenharmony_ci rtime = (rtime*HZ)/1000; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci if (rtime < HZ/10) 144862306a36Sopenharmony_ci rtime = HZ/10; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci if (rtime != NEIGH_VAR(in6_dev->nd_parms, BASE_REACHABLE_TIME)) { 145162306a36Sopenharmony_ci NEIGH_VAR_SET(in6_dev->nd_parms, 145262306a36Sopenharmony_ci BASE_REACHABLE_TIME, rtime); 145362306a36Sopenharmony_ci NEIGH_VAR_SET(in6_dev->nd_parms, 145462306a36Sopenharmony_ci GC_STALETIME, 3 * rtime); 145562306a36Sopenharmony_ci in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime); 145662306a36Sopenharmony_ci in6_dev->tstamp = jiffies; 145762306a36Sopenharmony_ci send_ifinfo_notify = true; 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ciskip_linkparms: 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci /* 146562306a36Sopenharmony_ci * Process options. 146662306a36Sopenharmony_ci */ 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci if (!neigh) 146962306a36Sopenharmony_ci neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr, 147062306a36Sopenharmony_ci skb->dev, 1); 147162306a36Sopenharmony_ci if (neigh) { 147262306a36Sopenharmony_ci u8 *lladdr = NULL; 147362306a36Sopenharmony_ci if (ndopts.nd_opts_src_lladdr) { 147462306a36Sopenharmony_ci lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, 147562306a36Sopenharmony_ci skb->dev); 147662306a36Sopenharmony_ci if (!lladdr) { 147762306a36Sopenharmony_ci ND_PRINTK(2, warn, 147862306a36Sopenharmony_ci "RA: invalid link-layer address length\n"); 147962306a36Sopenharmony_ci goto out; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci } 148262306a36Sopenharmony_ci ndisc_update(skb->dev, neigh, lladdr, NUD_STALE, 148362306a36Sopenharmony_ci NEIGH_UPDATE_F_WEAK_OVERRIDE| 148462306a36Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE| 148562306a36Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 148662306a36Sopenharmony_ci NEIGH_UPDATE_F_ISROUTER, 148762306a36Sopenharmony_ci NDISC_ROUTER_ADVERTISEMENT, &ndopts); 148862306a36Sopenharmony_ci reason = SKB_CONSUMED; 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci if (!ipv6_accept_ra(in6_dev)) { 149262306a36Sopenharmony_ci ND_PRINTK(2, info, 149362306a36Sopenharmony_ci "RA: %s, accept_ra is false for dev: %s\n", 149462306a36Sopenharmony_ci __func__, skb->dev->name); 149562306a36Sopenharmony_ci goto out; 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 149962306a36Sopenharmony_ci if (!in6_dev->cnf.accept_ra_from_local && 150062306a36Sopenharmony_ci ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, 150162306a36Sopenharmony_ci in6_dev->dev, 0)) { 150262306a36Sopenharmony_ci ND_PRINTK(2, info, 150362306a36Sopenharmony_ci "RA from local address detected on dev: %s: router info ignored.\n", 150462306a36Sopenharmony_ci skb->dev->name); 150562306a36Sopenharmony_ci goto skip_routeinfo; 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) { 150962306a36Sopenharmony_ci struct nd_opt_hdr *p; 151062306a36Sopenharmony_ci for (p = ndopts.nd_opts_ri; 151162306a36Sopenharmony_ci p; 151262306a36Sopenharmony_ci p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) { 151362306a36Sopenharmony_ci struct route_info *ri = (struct route_info *)p; 151462306a36Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE 151562306a36Sopenharmony_ci if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT && 151662306a36Sopenharmony_ci ri->prefix_len == 0) 151762306a36Sopenharmony_ci continue; 151862306a36Sopenharmony_ci#endif 151962306a36Sopenharmony_ci if (ri->prefix_len == 0 && 152062306a36Sopenharmony_ci !in6_dev->cnf.accept_ra_defrtr) 152162306a36Sopenharmony_ci continue; 152262306a36Sopenharmony_ci if (ri->lifetime != 0 && 152362306a36Sopenharmony_ci ntohl(ri->lifetime) < in6_dev->cnf.accept_ra_min_lft) 152462306a36Sopenharmony_ci continue; 152562306a36Sopenharmony_ci if (ri->prefix_len < in6_dev->cnf.accept_ra_rt_info_min_plen) 152662306a36Sopenharmony_ci continue; 152762306a36Sopenharmony_ci if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen) 152862306a36Sopenharmony_ci continue; 152962306a36Sopenharmony_ci rt6_route_rcv(skb->dev, (u8 *)p, (p->nd_opt_len) << 3, 153062306a36Sopenharmony_ci &ipv6_hdr(skb)->saddr); 153162306a36Sopenharmony_ci } 153262306a36Sopenharmony_ci } 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ciskip_routeinfo: 153562306a36Sopenharmony_ci#endif 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE 153862306a36Sopenharmony_ci /* skip link-specific ndopts from interior routers */ 153962306a36Sopenharmony_ci if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) { 154062306a36Sopenharmony_ci ND_PRINTK(2, info, 154162306a36Sopenharmony_ci "RA: %s, nodetype is NODEFAULT (interior routes), dev: %s\n", 154262306a36Sopenharmony_ci __func__, skb->dev->name); 154362306a36Sopenharmony_ci goto out; 154462306a36Sopenharmony_ci } 154562306a36Sopenharmony_ci#endif 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) { 154862306a36Sopenharmony_ci struct nd_opt_hdr *p; 154962306a36Sopenharmony_ci for (p = ndopts.nd_opts_pi; 155062306a36Sopenharmony_ci p; 155162306a36Sopenharmony_ci p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) { 155262306a36Sopenharmony_ci addrconf_prefix_rcv(skb->dev, (u8 *)p, 155362306a36Sopenharmony_ci (p->nd_opt_len) << 3, 155462306a36Sopenharmony_ci ndopts.nd_opts_src_lladdr != NULL); 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci if (ndopts.nd_opts_mtu && in6_dev->cnf.accept_ra_mtu) { 155962306a36Sopenharmony_ci __be32 n; 156062306a36Sopenharmony_ci u32 mtu; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci memcpy(&n, ((u8 *)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu)); 156362306a36Sopenharmony_ci mtu = ntohl(n); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (in6_dev->ra_mtu != mtu) { 156662306a36Sopenharmony_ci in6_dev->ra_mtu = mtu; 156762306a36Sopenharmony_ci send_ifinfo_notify = true; 156862306a36Sopenharmony_ci } 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) { 157162306a36Sopenharmony_ci ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu); 157262306a36Sopenharmony_ci } else if (in6_dev->cnf.mtu6 != mtu) { 157362306a36Sopenharmony_ci in6_dev->cnf.mtu6 = mtu; 157462306a36Sopenharmony_ci fib6_metric_set(rt, RTAX_MTU, mtu); 157562306a36Sopenharmony_ci rt6_mtu_change(skb->dev, mtu); 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci } 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci if (ndopts.nd_useropts) { 158062306a36Sopenharmony_ci struct nd_opt_hdr *p; 158162306a36Sopenharmony_ci for (p = ndopts.nd_useropts; 158262306a36Sopenharmony_ci p; 158362306a36Sopenharmony_ci p = ndisc_next_useropt(skb->dev, p, 158462306a36Sopenharmony_ci ndopts.nd_useropts_end)) { 158562306a36Sopenharmony_ci ndisc_ra_useropt(skb, p); 158662306a36Sopenharmony_ci } 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) { 159062306a36Sopenharmony_ci ND_PRINTK(2, warn, "RA: invalid RA options\n"); 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ciout: 159362306a36Sopenharmony_ci /* Send a notify if RA changed managed/otherconf flags or 159462306a36Sopenharmony_ci * timer settings or ra_mtu value 159562306a36Sopenharmony_ci */ 159662306a36Sopenharmony_ci if (send_ifinfo_notify) 159762306a36Sopenharmony_ci inet6_ifinfo_notify(RTM_NEWLINK, in6_dev); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci fib6_info_release(rt); 160062306a36Sopenharmony_ci if (neigh) 160162306a36Sopenharmony_ci neigh_release(neigh); 160262306a36Sopenharmony_ci return reason; 160362306a36Sopenharmony_ci} 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_cistatic enum skb_drop_reason ndisc_redirect_rcv(struct sk_buff *skb) 160662306a36Sopenharmony_ci{ 160762306a36Sopenharmony_ci struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb); 160862306a36Sopenharmony_ci u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + 160962306a36Sopenharmony_ci offsetof(struct rd_msg, opt)); 161062306a36Sopenharmony_ci struct ndisc_options ndopts; 161162306a36Sopenharmony_ci SKB_DR(reason); 161262306a36Sopenharmony_ci u8 *hdr; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE 161562306a36Sopenharmony_ci switch (skb->ndisc_nodetype) { 161662306a36Sopenharmony_ci case NDISC_NODETYPE_HOST: 161762306a36Sopenharmony_ci case NDISC_NODETYPE_NODEFAULT: 161862306a36Sopenharmony_ci ND_PRINTK(2, warn, 161962306a36Sopenharmony_ci "Redirect: from host or unauthorized router\n"); 162062306a36Sopenharmony_ci return reason; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci#endif 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) { 162562306a36Sopenharmony_ci ND_PRINTK(2, warn, 162662306a36Sopenharmony_ci "Redirect: source address is not link-local\n"); 162762306a36Sopenharmony_ci return reason; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci if (!ndisc_parse_options(skb->dev, msg->opt, ndoptlen, &ndopts)) 163162306a36Sopenharmony_ci return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci if (!ndopts.nd_opts_rh) { 163462306a36Sopenharmony_ci ip6_redirect_no_header(skb, dev_net(skb->dev), 163562306a36Sopenharmony_ci skb->dev->ifindex); 163662306a36Sopenharmony_ci return reason; 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci hdr = (u8 *)ndopts.nd_opts_rh; 164062306a36Sopenharmony_ci hdr += 8; 164162306a36Sopenharmony_ci if (!pskb_pull(skb, hdr - skb_transport_header(skb))) 164262306a36Sopenharmony_ci return SKB_DROP_REASON_PKT_TOO_SMALL; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci return icmpv6_notify(skb, NDISC_REDIRECT, 0, 0); 164562306a36Sopenharmony_ci} 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_cistatic void ndisc_fill_redirect_hdr_option(struct sk_buff *skb, 164862306a36Sopenharmony_ci struct sk_buff *orig_skb, 164962306a36Sopenharmony_ci int rd_len) 165062306a36Sopenharmony_ci{ 165162306a36Sopenharmony_ci u8 *opt = skb_put(skb, rd_len); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci memset(opt, 0, 8); 165462306a36Sopenharmony_ci *(opt++) = ND_OPT_REDIRECT_HDR; 165562306a36Sopenharmony_ci *(opt++) = (rd_len >> 3); 165662306a36Sopenharmony_ci opt += 6; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci skb_copy_bits(orig_skb, skb_network_offset(orig_skb), opt, 165962306a36Sopenharmony_ci rd_len - 8); 166062306a36Sopenharmony_ci} 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_civoid ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) 166362306a36Sopenharmony_ci{ 166462306a36Sopenharmony_ci struct net_device *dev = skb->dev; 166562306a36Sopenharmony_ci struct net *net = dev_net(dev); 166662306a36Sopenharmony_ci struct sock *sk = net->ipv6.ndisc_sk; 166762306a36Sopenharmony_ci int optlen = 0; 166862306a36Sopenharmony_ci struct inet_peer *peer; 166962306a36Sopenharmony_ci struct sk_buff *buff; 167062306a36Sopenharmony_ci struct rd_msg *msg; 167162306a36Sopenharmony_ci struct in6_addr saddr_buf; 167262306a36Sopenharmony_ci struct rt6_info *rt; 167362306a36Sopenharmony_ci struct dst_entry *dst; 167462306a36Sopenharmony_ci struct flowi6 fl6; 167562306a36Sopenharmony_ci int rd_len; 167662306a36Sopenharmony_ci u8 ha_buf[MAX_ADDR_LEN], *ha = NULL, 167762306a36Sopenharmony_ci ops_data_buf[NDISC_OPS_REDIRECT_DATA_SPACE], *ops_data = NULL; 167862306a36Sopenharmony_ci bool ret; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci if (netif_is_l3_master(skb->dev)) { 168162306a36Sopenharmony_ci dev = __dev_get_by_index(dev_net(skb->dev), IPCB(skb)->iif); 168262306a36Sopenharmony_ci if (!dev) 168362306a36Sopenharmony_ci return; 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) { 168762306a36Sopenharmony_ci ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n", 168862306a36Sopenharmony_ci dev->name); 168962306a36Sopenharmony_ci return; 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) && 169362306a36Sopenharmony_ci ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 169462306a36Sopenharmony_ci ND_PRINTK(2, warn, 169562306a36Sopenharmony_ci "Redirect: target address is not link-local unicast\n"); 169662306a36Sopenharmony_ci return; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT, 170062306a36Sopenharmony_ci &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex); 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci dst = ip6_route_output(net, NULL, &fl6); 170362306a36Sopenharmony_ci if (dst->error) { 170462306a36Sopenharmony_ci dst_release(dst); 170562306a36Sopenharmony_ci return; 170662306a36Sopenharmony_ci } 170762306a36Sopenharmony_ci dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); 170862306a36Sopenharmony_ci if (IS_ERR(dst)) 170962306a36Sopenharmony_ci return; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci rt = (struct rt6_info *) dst; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci if (rt->rt6i_flags & RTF_GATEWAY) { 171462306a36Sopenharmony_ci ND_PRINTK(2, warn, 171562306a36Sopenharmony_ci "Redirect: destination is not a neighbour\n"); 171662306a36Sopenharmony_ci goto release; 171762306a36Sopenharmony_ci } 171862306a36Sopenharmony_ci peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr, 1); 171962306a36Sopenharmony_ci ret = inet_peer_xrlim_allow(peer, 1*HZ); 172062306a36Sopenharmony_ci if (peer) 172162306a36Sopenharmony_ci inet_putpeer(peer); 172262306a36Sopenharmony_ci if (!ret) 172362306a36Sopenharmony_ci goto release; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci if (dev->addr_len) { 172662306a36Sopenharmony_ci struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target); 172762306a36Sopenharmony_ci if (!neigh) { 172862306a36Sopenharmony_ci ND_PRINTK(2, warn, 172962306a36Sopenharmony_ci "Redirect: no neigh for target address\n"); 173062306a36Sopenharmony_ci goto release; 173162306a36Sopenharmony_ci } 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci read_lock_bh(&neigh->lock); 173462306a36Sopenharmony_ci if (neigh->nud_state & NUD_VALID) { 173562306a36Sopenharmony_ci memcpy(ha_buf, neigh->ha, dev->addr_len); 173662306a36Sopenharmony_ci read_unlock_bh(&neigh->lock); 173762306a36Sopenharmony_ci ha = ha_buf; 173862306a36Sopenharmony_ci optlen += ndisc_redirect_opt_addr_space(dev, neigh, 173962306a36Sopenharmony_ci ops_data_buf, 174062306a36Sopenharmony_ci &ops_data); 174162306a36Sopenharmony_ci } else 174262306a36Sopenharmony_ci read_unlock_bh(&neigh->lock); 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci neigh_release(neigh); 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci rd_len = min_t(unsigned int, 174862306a36Sopenharmony_ci IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(*msg) - optlen, 174962306a36Sopenharmony_ci skb->len + 8); 175062306a36Sopenharmony_ci rd_len &= ~0x7; 175162306a36Sopenharmony_ci optlen += rd_len; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci buff = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); 175462306a36Sopenharmony_ci if (!buff) 175562306a36Sopenharmony_ci goto release; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci msg = skb_put(buff, sizeof(*msg)); 175862306a36Sopenharmony_ci *msg = (struct rd_msg) { 175962306a36Sopenharmony_ci .icmph = { 176062306a36Sopenharmony_ci .icmp6_type = NDISC_REDIRECT, 176162306a36Sopenharmony_ci }, 176262306a36Sopenharmony_ci .target = *target, 176362306a36Sopenharmony_ci .dest = ipv6_hdr(skb)->daddr, 176462306a36Sopenharmony_ci }; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci /* 176762306a36Sopenharmony_ci * include target_address option 176862306a36Sopenharmony_ci */ 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci if (ha) 177162306a36Sopenharmony_ci ndisc_fill_redirect_addr_option(buff, ha, ops_data); 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci /* 177462306a36Sopenharmony_ci * build redirect option and copy skb over to the new packet. 177562306a36Sopenharmony_ci */ 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci if (rd_len) 177862306a36Sopenharmony_ci ndisc_fill_redirect_hdr_option(buff, skb, rd_len); 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci skb_dst_set(buff, dst); 178162306a36Sopenharmony_ci ndisc_send_skb(buff, &ipv6_hdr(skb)->saddr, &saddr_buf); 178262306a36Sopenharmony_ci return; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_cirelease: 178562306a36Sopenharmony_ci dst_release(dst); 178662306a36Sopenharmony_ci} 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_cistatic void pndisc_redo(struct sk_buff *skb) 178962306a36Sopenharmony_ci{ 179062306a36Sopenharmony_ci enum skb_drop_reason reason = ndisc_recv_ns(skb); 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci kfree_skb_reason(skb, reason); 179362306a36Sopenharmony_ci} 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_cistatic int ndisc_is_multicast(const void *pkey) 179662306a36Sopenharmony_ci{ 179762306a36Sopenharmony_ci return ipv6_addr_is_multicast((struct in6_addr *)pkey); 179862306a36Sopenharmony_ci} 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_cistatic bool ndisc_suppress_frag_ndisc(struct sk_buff *skb) 180162306a36Sopenharmony_ci{ 180262306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(skb->dev); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci if (!idev) 180562306a36Sopenharmony_ci return true; 180662306a36Sopenharmony_ci if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED && 180762306a36Sopenharmony_ci idev->cnf.suppress_frag_ndisc) { 180862306a36Sopenharmony_ci net_warn_ratelimited("Received fragmented ndisc packet. Carefully consider disabling suppress_frag_ndisc.\n"); 180962306a36Sopenharmony_ci return true; 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci return false; 181262306a36Sopenharmony_ci} 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_cienum skb_drop_reason ndisc_rcv(struct sk_buff *skb) 181562306a36Sopenharmony_ci{ 181662306a36Sopenharmony_ci struct nd_msg *msg; 181762306a36Sopenharmony_ci SKB_DR(reason); 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci if (ndisc_suppress_frag_ndisc(skb)) 182062306a36Sopenharmony_ci return SKB_DROP_REASON_IPV6_NDISC_FRAG; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci if (skb_linearize(skb)) 182362306a36Sopenharmony_ci return SKB_DROP_REASON_NOMEM; 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci msg = (struct nd_msg *)skb_transport_header(skb); 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci __skb_push(skb, skb->data - skb_transport_header(skb)); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci if (ipv6_hdr(skb)->hop_limit != 255) { 183062306a36Sopenharmony_ci ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n", 183162306a36Sopenharmony_ci ipv6_hdr(skb)->hop_limit); 183262306a36Sopenharmony_ci return SKB_DROP_REASON_IPV6_NDISC_HOP_LIMIT; 183362306a36Sopenharmony_ci } 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci if (msg->icmph.icmp6_code != 0) { 183662306a36Sopenharmony_ci ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n", 183762306a36Sopenharmony_ci msg->icmph.icmp6_code); 183862306a36Sopenharmony_ci return SKB_DROP_REASON_IPV6_NDISC_BAD_CODE; 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci switch (msg->icmph.icmp6_type) { 184262306a36Sopenharmony_ci case NDISC_NEIGHBOUR_SOLICITATION: 184362306a36Sopenharmony_ci memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb)); 184462306a36Sopenharmony_ci reason = ndisc_recv_ns(skb); 184562306a36Sopenharmony_ci break; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci case NDISC_NEIGHBOUR_ADVERTISEMENT: 184862306a36Sopenharmony_ci reason = ndisc_recv_na(skb); 184962306a36Sopenharmony_ci break; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci case NDISC_ROUTER_SOLICITATION: 185262306a36Sopenharmony_ci reason = ndisc_recv_rs(skb); 185362306a36Sopenharmony_ci break; 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci case NDISC_ROUTER_ADVERTISEMENT: 185662306a36Sopenharmony_ci reason = ndisc_router_discovery(skb); 185762306a36Sopenharmony_ci break; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci case NDISC_REDIRECT: 186062306a36Sopenharmony_ci reason = ndisc_redirect_rcv(skb); 186162306a36Sopenharmony_ci break; 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci return reason; 186562306a36Sopenharmony_ci} 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_cistatic int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) 186862306a36Sopenharmony_ci{ 186962306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 187062306a36Sopenharmony_ci struct netdev_notifier_change_info *change_info; 187162306a36Sopenharmony_ci struct net *net = dev_net(dev); 187262306a36Sopenharmony_ci struct inet6_dev *idev; 187362306a36Sopenharmony_ci bool evict_nocarrier; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci switch (event) { 187662306a36Sopenharmony_ci case NETDEV_CHANGEADDR: 187762306a36Sopenharmony_ci neigh_changeaddr(&nd_tbl, dev); 187862306a36Sopenharmony_ci fib6_run_gc(0, net, false); 187962306a36Sopenharmony_ci fallthrough; 188062306a36Sopenharmony_ci case NETDEV_UP: 188162306a36Sopenharmony_ci idev = in6_dev_get(dev); 188262306a36Sopenharmony_ci if (!idev) 188362306a36Sopenharmony_ci break; 188462306a36Sopenharmony_ci if (idev->cnf.ndisc_notify || 188562306a36Sopenharmony_ci net->ipv6.devconf_all->ndisc_notify) 188662306a36Sopenharmony_ci ndisc_send_unsol_na(dev); 188762306a36Sopenharmony_ci in6_dev_put(idev); 188862306a36Sopenharmony_ci break; 188962306a36Sopenharmony_ci case NETDEV_CHANGE: 189062306a36Sopenharmony_ci idev = in6_dev_get(dev); 189162306a36Sopenharmony_ci if (!idev) 189262306a36Sopenharmony_ci evict_nocarrier = true; 189362306a36Sopenharmony_ci else { 189462306a36Sopenharmony_ci evict_nocarrier = idev->cnf.ndisc_evict_nocarrier && 189562306a36Sopenharmony_ci net->ipv6.devconf_all->ndisc_evict_nocarrier; 189662306a36Sopenharmony_ci in6_dev_put(idev); 189762306a36Sopenharmony_ci } 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci change_info = ptr; 190062306a36Sopenharmony_ci if (change_info->flags_changed & IFF_NOARP) 190162306a36Sopenharmony_ci neigh_changeaddr(&nd_tbl, dev); 190262306a36Sopenharmony_ci if (evict_nocarrier && !netif_carrier_ok(dev)) 190362306a36Sopenharmony_ci neigh_carrier_down(&nd_tbl, dev); 190462306a36Sopenharmony_ci break; 190562306a36Sopenharmony_ci case NETDEV_DOWN: 190662306a36Sopenharmony_ci neigh_ifdown(&nd_tbl, dev); 190762306a36Sopenharmony_ci fib6_run_gc(0, net, false); 190862306a36Sopenharmony_ci break; 190962306a36Sopenharmony_ci case NETDEV_NOTIFY_PEERS: 191062306a36Sopenharmony_ci ndisc_send_unsol_na(dev); 191162306a36Sopenharmony_ci break; 191262306a36Sopenharmony_ci default: 191362306a36Sopenharmony_ci break; 191462306a36Sopenharmony_ci } 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci return NOTIFY_DONE; 191762306a36Sopenharmony_ci} 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_cistatic struct notifier_block ndisc_netdev_notifier = { 192062306a36Sopenharmony_ci .notifier_call = ndisc_netdev_event, 192162306a36Sopenharmony_ci .priority = ADDRCONF_NOTIFY_PRIORITY - 5, 192262306a36Sopenharmony_ci}; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 192562306a36Sopenharmony_cistatic void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl, 192662306a36Sopenharmony_ci const char *func, const char *dev_name) 192762306a36Sopenharmony_ci{ 192862306a36Sopenharmony_ci static char warncomm[TASK_COMM_LEN]; 192962306a36Sopenharmony_ci static int warned; 193062306a36Sopenharmony_ci if (strcmp(warncomm, current->comm) && warned < 5) { 193162306a36Sopenharmony_ci strcpy(warncomm, current->comm); 193262306a36Sopenharmony_ci pr_warn("process `%s' is using deprecated sysctl (%s) net.ipv6.neigh.%s.%s - use net.ipv6.neigh.%s.%s_ms instead\n", 193362306a36Sopenharmony_ci warncomm, func, 193462306a36Sopenharmony_ci dev_name, ctl->procname, 193562306a36Sopenharmony_ci dev_name, ctl->procname); 193662306a36Sopenharmony_ci warned++; 193762306a36Sopenharmony_ci } 193862306a36Sopenharmony_ci} 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ciint ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void *buffer, 194162306a36Sopenharmony_ci size_t *lenp, loff_t *ppos) 194262306a36Sopenharmony_ci{ 194362306a36Sopenharmony_ci struct net_device *dev = ctl->extra1; 194462306a36Sopenharmony_ci struct inet6_dev *idev; 194562306a36Sopenharmony_ci int ret; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci if ((strcmp(ctl->procname, "retrans_time") == 0) || 194862306a36Sopenharmony_ci (strcmp(ctl->procname, "base_reachable_time") == 0)) 194962306a36Sopenharmony_ci ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default"); 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci if (strcmp(ctl->procname, "retrans_time") == 0) 195262306a36Sopenharmony_ci ret = neigh_proc_dointvec(ctl, write, buffer, lenp, ppos); 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci else if (strcmp(ctl->procname, "base_reachable_time") == 0) 195562306a36Sopenharmony_ci ret = neigh_proc_dointvec_jiffies(ctl, write, 195662306a36Sopenharmony_ci buffer, lenp, ppos); 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) || 195962306a36Sopenharmony_ci (strcmp(ctl->procname, "base_reachable_time_ms") == 0)) 196062306a36Sopenharmony_ci ret = neigh_proc_dointvec_ms_jiffies(ctl, write, 196162306a36Sopenharmony_ci buffer, lenp, ppos); 196262306a36Sopenharmony_ci else 196362306a36Sopenharmony_ci ret = -1; 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) { 196662306a36Sopenharmony_ci if (ctl->data == &NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME)) 196762306a36Sopenharmony_ci idev->nd_parms->reachable_time = 196862306a36Sopenharmony_ci neigh_rand_reach_time(NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME)); 196962306a36Sopenharmony_ci idev->tstamp = jiffies; 197062306a36Sopenharmony_ci inet6_ifinfo_notify(RTM_NEWLINK, idev); 197162306a36Sopenharmony_ci in6_dev_put(idev); 197262306a36Sopenharmony_ci } 197362306a36Sopenharmony_ci return ret; 197462306a36Sopenharmony_ci} 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci#endif 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_cistatic int __net_init ndisc_net_init(struct net *net) 198062306a36Sopenharmony_ci{ 198162306a36Sopenharmony_ci struct ipv6_pinfo *np; 198262306a36Sopenharmony_ci struct sock *sk; 198362306a36Sopenharmony_ci int err; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci err = inet_ctl_sock_create(&sk, PF_INET6, 198662306a36Sopenharmony_ci SOCK_RAW, IPPROTO_ICMPV6, net); 198762306a36Sopenharmony_ci if (err < 0) { 198862306a36Sopenharmony_ci ND_PRINTK(0, err, 198962306a36Sopenharmony_ci "NDISC: Failed to initialize the control socket (err %d)\n", 199062306a36Sopenharmony_ci err); 199162306a36Sopenharmony_ci return err; 199262306a36Sopenharmony_ci } 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci net->ipv6.ndisc_sk = sk; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci np = inet6_sk(sk); 199762306a36Sopenharmony_ci np->hop_limit = 255; 199862306a36Sopenharmony_ci /* Do not loopback ndisc messages */ 199962306a36Sopenharmony_ci np->mc_loop = 0; 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci return 0; 200262306a36Sopenharmony_ci} 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_cistatic void __net_exit ndisc_net_exit(struct net *net) 200562306a36Sopenharmony_ci{ 200662306a36Sopenharmony_ci inet_ctl_sock_destroy(net->ipv6.ndisc_sk); 200762306a36Sopenharmony_ci} 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_cistatic struct pernet_operations ndisc_net_ops = { 201062306a36Sopenharmony_ci .init = ndisc_net_init, 201162306a36Sopenharmony_ci .exit = ndisc_net_exit, 201262306a36Sopenharmony_ci}; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ciint __init ndisc_init(void) 201562306a36Sopenharmony_ci{ 201662306a36Sopenharmony_ci int err; 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci err = register_pernet_subsys(&ndisc_net_ops); 201962306a36Sopenharmony_ci if (err) 202062306a36Sopenharmony_ci return err; 202162306a36Sopenharmony_ci /* 202262306a36Sopenharmony_ci * Initialize the neighbour table 202362306a36Sopenharmony_ci */ 202462306a36Sopenharmony_ci neigh_table_init(NEIGH_ND_TABLE, &nd_tbl); 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 202762306a36Sopenharmony_ci err = neigh_sysctl_register(NULL, &nd_tbl.parms, 202862306a36Sopenharmony_ci ndisc_ifinfo_sysctl_change); 202962306a36Sopenharmony_ci if (err) 203062306a36Sopenharmony_ci goto out_unregister_pernet; 203162306a36Sopenharmony_ciout: 203262306a36Sopenharmony_ci#endif 203362306a36Sopenharmony_ci return err; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 203662306a36Sopenharmony_ciout_unregister_pernet: 203762306a36Sopenharmony_ci unregister_pernet_subsys(&ndisc_net_ops); 203862306a36Sopenharmony_ci goto out; 203962306a36Sopenharmony_ci#endif 204062306a36Sopenharmony_ci} 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ciint __init ndisc_late_init(void) 204362306a36Sopenharmony_ci{ 204462306a36Sopenharmony_ci return register_netdevice_notifier(&ndisc_netdev_notifier); 204562306a36Sopenharmony_ci} 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_civoid ndisc_late_cleanup(void) 204862306a36Sopenharmony_ci{ 204962306a36Sopenharmony_ci unregister_netdevice_notifier(&ndisc_netdev_notifier); 205062306a36Sopenharmony_ci} 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_civoid ndisc_cleanup(void) 205362306a36Sopenharmony_ci{ 205462306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 205562306a36Sopenharmony_ci neigh_sysctl_unregister(&nd_tbl.parms); 205662306a36Sopenharmony_ci#endif 205762306a36Sopenharmony_ci neigh_table_clear(NEIGH_ND_TABLE, &nd_tbl); 205862306a36Sopenharmony_ci unregister_pernet_subsys(&ndisc_net_ops); 205962306a36Sopenharmony_ci} 2060