1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * 4 * Authors: 5 * (C) 2016 Pengutronix, Alexander Aring <aar@pengutronix.de> 6 */ 7 8 #include <net/6lowpan.h> 9 #include <net/addrconf.h> 10 #include <net/ndisc.h> 11 12 #include "6lowpan_i.h" 13 14 #if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN) 15 #define NDISC_802154_SHORT_ADDR_LENGTH 1 16 static int lowpan_ndisc_parse_802154_options(const struct net_device *dev, 17 struct nd_opt_hdr *nd_opt, 18 struct ndisc_options *ndopts) 19 { 20 switch (nd_opt->nd_opt_len) { 21 case NDISC_802154_SHORT_ADDR_LENGTH: 22 if (ndopts->nd_802154_opt_array[nd_opt->nd_opt_type]) 23 net_dbg_ratelimited("%s: duplicated short addr ND6 option found: type=%d\n", 24 __func__, nd_opt->nd_opt_type); 25 else 26 ndopts->nd_802154_opt_array[nd_opt->nd_opt_type] = nd_opt; 27 return 1; 28 default: 29 /* all others will be handled by ndisc IPv6 option parsing */ 30 return 0; 31 } 32 } 33 34 static int lowpan_ndisc_parse_options(const struct net_device *dev, 35 struct nd_opt_hdr *nd_opt, 36 struct ndisc_options *ndopts) 37 { 38 if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) 39 return 0; 40 41 switch (nd_opt->nd_opt_type) { 42 case ND_OPT_SOURCE_LL_ADDR: 43 case ND_OPT_TARGET_LL_ADDR: 44 return lowpan_ndisc_parse_802154_options(dev, nd_opt, ndopts); 45 default: 46 return 0; 47 } 48 } 49 50 static void lowpan_ndisc_802154_update(struct neighbour *n, u32 flags, 51 u8 icmp6_type, 52 const struct ndisc_options *ndopts) 53 { 54 struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n)); 55 u8 *lladdr_short = NULL; 56 57 switch (icmp6_type) { 58 case NDISC_ROUTER_SOLICITATION: 59 case NDISC_ROUTER_ADVERTISEMENT: 60 case NDISC_NEIGHBOUR_SOLICITATION: 61 if (ndopts->nd_802154_opts_src_lladdr) { 62 lladdr_short = __ndisc_opt_addr_data(ndopts->nd_802154_opts_src_lladdr, 63 IEEE802154_SHORT_ADDR_LEN, 0); 64 if (!lladdr_short) { 65 net_dbg_ratelimited("NA: invalid short link-layer address length\n"); 66 return; 67 } 68 } 69 break; 70 case NDISC_REDIRECT: 71 case NDISC_NEIGHBOUR_ADVERTISEMENT: 72 if (ndopts->nd_802154_opts_tgt_lladdr) { 73 lladdr_short = __ndisc_opt_addr_data(ndopts->nd_802154_opts_tgt_lladdr, 74 IEEE802154_SHORT_ADDR_LEN, 0); 75 if (!lladdr_short) { 76 net_dbg_ratelimited("NA: invalid short link-layer address length\n"); 77 return; 78 } 79 } 80 break; 81 default: 82 break; 83 } 84 85 write_lock_bh(&n->lock); 86 if (lladdr_short) { 87 ieee802154_be16_to_le16(&neigh->short_addr, lladdr_short); 88 if (!lowpan_802154_is_valid_src_short_addr(neigh->short_addr)) 89 neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC); 90 } 91 write_unlock_bh(&n->lock); 92 } 93 94 static void lowpan_ndisc_update(const struct net_device *dev, 95 struct neighbour *n, u32 flags, u8 icmp6_type, 96 const struct ndisc_options *ndopts) 97 { 98 if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) 99 return; 100 101 /* react on overrides only. TODO check if this is really right. */ 102 if (flags & NEIGH_UPDATE_F_OVERRIDE) 103 lowpan_ndisc_802154_update(n, flags, icmp6_type, ndopts); 104 } 105 106 static int lowpan_ndisc_opt_addr_space(const struct net_device *dev, 107 u8 icmp6_type, struct neighbour *neigh, 108 u8 *ha_buf, u8 **ha) 109 { 110 struct lowpan_802154_neigh *n; 111 struct wpan_dev *wpan_dev; 112 int addr_space = 0; 113 114 if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) 115 return 0; 116 117 switch (icmp6_type) { 118 case NDISC_REDIRECT: 119 n = lowpan_802154_neigh(neighbour_priv(neigh)); 120 121 read_lock_bh(&neigh->lock); 122 if (lowpan_802154_is_valid_src_short_addr(n->short_addr)) { 123 memcpy(ha_buf, &n->short_addr, 124 IEEE802154_SHORT_ADDR_LEN); 125 read_unlock_bh(&neigh->lock); 126 addr_space += __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0); 127 *ha = ha_buf; 128 } else { 129 read_unlock_bh(&neigh->lock); 130 } 131 break; 132 case NDISC_NEIGHBOUR_ADVERTISEMENT: 133 case NDISC_NEIGHBOUR_SOLICITATION: 134 case NDISC_ROUTER_SOLICITATION: 135 wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr; 136 137 if (lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr)) 138 addr_space = __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0); 139 break; 140 default: 141 break; 142 } 143 144 return addr_space; 145 } 146 147 static void lowpan_ndisc_fill_addr_option(const struct net_device *dev, 148 struct sk_buff *skb, u8 icmp6_type, 149 const u8 *ha) 150 { 151 struct wpan_dev *wpan_dev; 152 __be16 short_addr; 153 u8 opt_type; 154 155 if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) 156 return; 157 158 switch (icmp6_type) { 159 case NDISC_REDIRECT: 160 if (ha) { 161 ieee802154_le16_to_be16(&short_addr, ha); 162 __ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, 163 &short_addr, 164 IEEE802154_SHORT_ADDR_LEN, 0); 165 } 166 return; 167 case NDISC_NEIGHBOUR_ADVERTISEMENT: 168 opt_type = ND_OPT_TARGET_LL_ADDR; 169 break; 170 case NDISC_ROUTER_SOLICITATION: 171 case NDISC_NEIGHBOUR_SOLICITATION: 172 opt_type = ND_OPT_SOURCE_LL_ADDR; 173 break; 174 default: 175 return; 176 } 177 178 wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr; 179 180 if (lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr)) { 181 ieee802154_le16_to_be16(&short_addr, 182 &wpan_dev->short_addr); 183 __ndisc_fill_addr_option(skb, opt_type, &short_addr, 184 IEEE802154_SHORT_ADDR_LEN, 0); 185 } 186 } 187 188 static void lowpan_ndisc_prefix_rcv_add_addr(struct net *net, 189 struct net_device *dev, 190 const struct prefix_info *pinfo, 191 struct inet6_dev *in6_dev, 192 struct in6_addr *addr, 193 int addr_type, u32 addr_flags, 194 bool sllao, bool tokenized, 195 __u32 valid_lft, 196 u32 prefered_lft, 197 bool dev_addr_generated) 198 { 199 int err; 200 201 /* generates short based address for RA PIO's */ 202 if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) && dev_addr_generated && 203 !addrconf_ifid_802154_6lowpan(addr->s6_addr + 8, dev)) { 204 err = addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, 205 addr, addr_type, addr_flags, 206 sllao, tokenized, valid_lft, 207 prefered_lft); 208 if (err) 209 net_dbg_ratelimited("RA: could not add a short address based address for prefix: %pI6c\n", 210 &pinfo->prefix); 211 } 212 } 213 #endif 214 215 const struct ndisc_ops lowpan_ndisc_ops = { 216 #if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN) 217 .parse_options = lowpan_ndisc_parse_options, 218 .update = lowpan_ndisc_update, 219 .opt_addr_space = lowpan_ndisc_opt_addr_space, 220 .fill_addr_option = lowpan_ndisc_fill_addr_option, 221 .prefix_rcv_add_addr = lowpan_ndisc_prefix_rcv_add_addr, 222 #endif 223 }; 224