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.
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.
11 * (C) 2016 Pengutronix, Alexander Aring <aar@pengutronix.de>
14 #include <net/6lowpan.h>
15 #include <net/addrconf.h>
16 #include <net/ndisc.h>
18 #include "6lowpan_i.h"
20 static int lowpan_ndisc_is_useropt(u8 nd_opt_type
)
22 return nd_opt_type
== ND_OPT_6CO
;
25 #if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
26 #define NDISC_802154_SHORT_ADDR_LENGTH 1
27 static int lowpan_ndisc_parse_802154_options(const struct net_device
*dev
,
28 struct nd_opt_hdr
*nd_opt
,
29 struct ndisc_options
*ndopts
)
31 switch (nd_opt
->nd_opt_len
) {
32 case NDISC_802154_SHORT_ADDR_LENGTH
:
33 if (ndopts
->nd_802154_opt_array
[nd_opt
->nd_opt_type
])
35 "%s: duplicated short addr ND6 option found: type=%d\n",
36 __func__
, nd_opt
->nd_opt_type
);
38 ndopts
->nd_802154_opt_array
[nd_opt
->nd_opt_type
] = nd_opt
;
41 /* all others will be handled by ndisc IPv6 option parsing */
46 static int lowpan_ndisc_parse_options(const struct net_device
*dev
,
47 struct nd_opt_hdr
*nd_opt
,
48 struct ndisc_options
*ndopts
)
50 switch (nd_opt
->nd_opt_type
) {
51 case ND_OPT_SOURCE_LL_ADDR
:
52 case ND_OPT_TARGET_LL_ADDR
:
53 return lowpan_ndisc_parse_802154_options(dev
, nd_opt
, ndopts
);
59 static void lowpan_ndisc_802154_update(struct neighbour
*n
, u32 flags
,
61 const struct ndisc_options
*ndopts
)
63 struct lowpan_802154_neigh
*neigh
= lowpan_802154_neigh(neighbour_priv(n
));
64 u8
*lladdr_short
= NULL
;
67 case NDISC_ROUTER_SOLICITATION
:
68 case NDISC_ROUTER_ADVERTISEMENT
:
69 case NDISC_NEIGHBOUR_SOLICITATION
:
70 if (ndopts
->nd_802154_opts_src_lladdr
) {
71 lladdr_short
= __ndisc_opt_addr_data(ndopts
->nd_802154_opts_src_lladdr
,
72 IEEE802154_SHORT_ADDR_LEN
, 0);
75 "NA: invalid short link-layer address length\n");
81 case NDISC_NEIGHBOUR_ADVERTISEMENT
:
82 if (ndopts
->nd_802154_opts_tgt_lladdr
) {
83 lladdr_short
= __ndisc_opt_addr_data(ndopts
->nd_802154_opts_tgt_lladdr
,
84 IEEE802154_SHORT_ADDR_LEN
, 0);
87 "NA: invalid short link-layer address length\n");
96 write_lock_bh(&n
->lock
);
98 ieee802154_be16_to_le16(&neigh
->short_addr
, lladdr_short
);
100 neigh
->short_addr
= cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC
);
101 write_unlock_bh(&n
->lock
);
104 static void lowpan_ndisc_update(const struct net_device
*dev
,
105 struct neighbour
*n
, u32 flags
, u8 icmp6_type
,
106 const struct ndisc_options
*ndopts
)
108 if (!lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
))
111 /* react on overrides only. TODO check if this is really right. */
112 if (flags
& NEIGH_UPDATE_F_OVERRIDE
)
113 lowpan_ndisc_802154_update(n
, flags
, icmp6_type
, ndopts
);
116 static int lowpan_ndisc_opt_addr_space(const struct net_device
*dev
,
117 u8 icmp6_type
, struct neighbour
*neigh
,
120 struct lowpan_802154_neigh
*n
;
121 struct wpan_dev
*wpan_dev
;
124 if (!lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
))
127 switch (icmp6_type
) {
129 n
= lowpan_802154_neigh(neighbour_priv(neigh
));
131 read_lock_bh(&neigh
->lock
);
132 if (lowpan_802154_is_valid_src_short_addr(n
->short_addr
)) {
133 memcpy(ha_buf
, &n
->short_addr
,
134 IEEE802154_SHORT_ADDR_LEN
);
135 read_unlock_bh(&neigh
->lock
);
136 addr_space
+= __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN
, 0);
139 read_unlock_bh(&neigh
->lock
);
141 case NDISC_NEIGHBOUR_ADVERTISEMENT
:
142 case NDISC_NEIGHBOUR_SOLICITATION
:
143 case NDISC_ROUTER_SOLICITATION
:
144 wpan_dev
= lowpan_802154_dev(dev
)->wdev
->ieee802154_ptr
;
146 if (lowpan_802154_is_valid_src_short_addr(wpan_dev
->short_addr
))
147 addr_space
= __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN
, 0);
156 static void lowpan_ndisc_fill_addr_option(const struct net_device
*dev
,
157 struct sk_buff
*skb
, u8 icmp6_type
,
160 struct wpan_dev
*wpan_dev
;
164 if (!lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
))
167 switch (icmp6_type
) {
170 ieee802154_le16_to_be16(&short_addr
, ha
);
171 __ndisc_fill_addr_option(skb
, ND_OPT_TARGET_LL_ADDR
,
173 IEEE802154_SHORT_ADDR_LEN
, 0);
176 case NDISC_NEIGHBOUR_ADVERTISEMENT
:
177 opt_type
= ND_OPT_TARGET_LL_ADDR
;
179 case NDISC_ROUTER_SOLICITATION
:
180 case NDISC_NEIGHBOUR_SOLICITATION
:
181 opt_type
= ND_OPT_SOURCE_LL_ADDR
;
187 wpan_dev
= lowpan_802154_dev(dev
)->wdev
->ieee802154_ptr
;
189 if (lowpan_802154_is_valid_src_short_addr(wpan_dev
->short_addr
)) {
190 ieee802154_le16_to_be16(&short_addr
,
191 &wpan_dev
->short_addr
);
192 __ndisc_fill_addr_option(skb
, opt_type
, &short_addr
,
193 IEEE802154_SHORT_ADDR_LEN
, 0);
197 static void lowpan_ndisc_prefix_rcv_add_addr(struct net
*net
,
198 struct net_device
*dev
,
199 const struct prefix_info
*pinfo
,
200 struct inet6_dev
*in6_dev
,
201 struct in6_addr
*addr
,
202 int addr_type
, u32 addr_flags
,
203 bool sllao
, bool tokenized
,
206 bool dev_addr_generated
)
210 /* generates short based address for RA PIO's */
211 if (lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
) && dev_addr_generated
&&
212 !addrconf_ifid_802154_6lowpan(addr
->s6_addr
+ 8, dev
)) {
213 err
= addrconf_prefix_rcv_add_addr(net
, dev
, pinfo
, in6_dev
,
214 addr
, addr_type
, addr_flags
,
215 sllao
, tokenized
, valid_lft
,
219 "RA: could not add a short address based address for prefix: %pI6c\n",
225 const struct ndisc_ops lowpan_ndisc_ops
= {
226 .is_useropt
= lowpan_ndisc_is_useropt
,
227 #if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
228 .parse_options
= lowpan_ndisc_parse_options
,
229 .update
= lowpan_ndisc_update
,
230 .opt_addr_space
= lowpan_ndisc_opt_addr_space
,
231 .fill_addr_option
= lowpan_ndisc_fill_addr_option
,
232 .prefix_rcv_add_addr
= lowpan_ndisc_prefix_rcv_add_addr
,