ndisc.c 6.3 KB

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