ndisc.c 6.7 KB

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