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 if (!lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
))
53 switch (nd_opt
->nd_opt_type
) {
54 case ND_OPT_SOURCE_LL_ADDR
:
55 case ND_OPT_TARGET_LL_ADDR
:
56 return lowpan_ndisc_parse_802154_options(dev
, nd_opt
, ndopts
);
62 static void lowpan_ndisc_802154_update(struct neighbour
*n
, u32 flags
,
64 const struct ndisc_options
*ndopts
)
66 struct lowpan_802154_neigh
*neigh
= lowpan_802154_neigh(neighbour_priv(n
));
67 u8
*lladdr_short
= NULL
;
70 case NDISC_ROUTER_SOLICITATION
:
71 case NDISC_ROUTER_ADVERTISEMENT
:
72 case NDISC_NEIGHBOUR_SOLICITATION
:
73 if (ndopts
->nd_802154_opts_src_lladdr
) {
74 lladdr_short
= __ndisc_opt_addr_data(ndopts
->nd_802154_opts_src_lladdr
,
75 IEEE802154_SHORT_ADDR_LEN
, 0);
78 "NA: invalid short link-layer address length\n");
84 case NDISC_NEIGHBOUR_ADVERTISEMENT
:
85 if (ndopts
->nd_802154_opts_tgt_lladdr
) {
86 lladdr_short
= __ndisc_opt_addr_data(ndopts
->nd_802154_opts_tgt_lladdr
,
87 IEEE802154_SHORT_ADDR_LEN
, 0);
90 "NA: invalid short link-layer address length\n");
99 write_lock_bh(&n
->lock
);
101 ieee802154_be16_to_le16(&neigh
->short_addr
, lladdr_short
);
102 if (!lowpan_802154_is_valid_src_short_addr(neigh
->short_addr
))
103 neigh
->short_addr
= cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC
);
105 neigh
->short_addr
= cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC
);
107 write_unlock_bh(&n
->lock
);
110 static void lowpan_ndisc_update(const struct net_device
*dev
,
111 struct neighbour
*n
, u32 flags
, u8 icmp6_type
,
112 const struct ndisc_options
*ndopts
)
114 if (!lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
))
117 /* react on overrides only. TODO check if this is really right. */
118 if (flags
& NEIGH_UPDATE_F_OVERRIDE
)
119 lowpan_ndisc_802154_update(n
, flags
, icmp6_type
, ndopts
);
122 static int lowpan_ndisc_opt_addr_space(const struct net_device
*dev
,
123 u8 icmp6_type
, struct neighbour
*neigh
,
126 struct lowpan_802154_neigh
*n
;
127 struct wpan_dev
*wpan_dev
;
130 if (!lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
))
133 switch (icmp6_type
) {
135 n
= lowpan_802154_neigh(neighbour_priv(neigh
));
137 read_lock_bh(&neigh
->lock
);
138 if (lowpan_802154_is_valid_src_short_addr(n
->short_addr
)) {
139 memcpy(ha_buf
, &n
->short_addr
,
140 IEEE802154_SHORT_ADDR_LEN
);
141 read_unlock_bh(&neigh
->lock
);
142 addr_space
+= __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN
, 0);
145 read_unlock_bh(&neigh
->lock
);
148 case NDISC_NEIGHBOUR_ADVERTISEMENT
:
149 case NDISC_NEIGHBOUR_SOLICITATION
:
150 case NDISC_ROUTER_SOLICITATION
:
151 wpan_dev
= lowpan_802154_dev(dev
)->wdev
->ieee802154_ptr
;
153 if (lowpan_802154_is_valid_src_short_addr(wpan_dev
->short_addr
))
154 addr_space
= __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN
, 0);
163 static void lowpan_ndisc_fill_addr_option(const struct net_device
*dev
,
164 struct sk_buff
*skb
, u8 icmp6_type
,
167 struct wpan_dev
*wpan_dev
;
171 if (!lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
))
174 switch (icmp6_type
) {
177 ieee802154_le16_to_be16(&short_addr
, ha
);
178 __ndisc_fill_addr_option(skb
, ND_OPT_TARGET_LL_ADDR
,
180 IEEE802154_SHORT_ADDR_LEN
, 0);
183 case NDISC_NEIGHBOUR_ADVERTISEMENT
:
184 opt_type
= ND_OPT_TARGET_LL_ADDR
;
186 case NDISC_ROUTER_SOLICITATION
:
187 case NDISC_NEIGHBOUR_SOLICITATION
:
188 opt_type
= ND_OPT_SOURCE_LL_ADDR
;
194 wpan_dev
= lowpan_802154_dev(dev
)->wdev
->ieee802154_ptr
;
196 if (lowpan_802154_is_valid_src_short_addr(wpan_dev
->short_addr
)) {
197 ieee802154_le16_to_be16(&short_addr
,
198 &wpan_dev
->short_addr
);
199 __ndisc_fill_addr_option(skb
, opt_type
, &short_addr
,
200 IEEE802154_SHORT_ADDR_LEN
, 0);
204 static void lowpan_ndisc_prefix_rcv_add_addr(struct net
*net
,
205 struct net_device
*dev
,
206 const struct prefix_info
*pinfo
,
207 struct inet6_dev
*in6_dev
,
208 struct in6_addr
*addr
,
209 int addr_type
, u32 addr_flags
,
210 bool sllao
, bool tokenized
,
213 bool dev_addr_generated
)
217 /* generates short based address for RA PIO's */
218 if (lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
) && dev_addr_generated
&&
219 !addrconf_ifid_802154_6lowpan(addr
->s6_addr
+ 8, dev
)) {
220 err
= addrconf_prefix_rcv_add_addr(net
, dev
, pinfo
, in6_dev
,
221 addr
, addr_type
, addr_flags
,
222 sllao
, tokenized
, valid_lft
,
226 "RA: could not add a short address based address for prefix: %pI6c\n",
232 const struct ndisc_ops lowpan_ndisc_ops
= {
233 .is_useropt
= lowpan_ndisc_is_useropt
,
234 #if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
235 .parse_options
= lowpan_ndisc_parse_options
,
236 .update
= lowpan_ndisc_update
,
237 .opt_addr_space
= lowpan_ndisc_opt_addr_space
,
238 .fill_addr_option
= lowpan_ndisc_fill_addr_option
,
239 .prefix_rcv_add_addr
= lowpan_ndisc_prefix_rcv_add_addr
,