ndisc.c 6.2 KB

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