root/net/6lowpan/ndisc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lowpan_ndisc_is_useropt
  2. lowpan_ndisc_parse_802154_options
  3. lowpan_ndisc_parse_options
  4. lowpan_ndisc_802154_update
  5. lowpan_ndisc_update
  6. lowpan_ndisc_opt_addr_space
  7. lowpan_ndisc_fill_addr_option
  8. lowpan_ndisc_prefix_rcv_add_addr

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

/* [<][>][^][v][top][bottom][index][help] */