diff options
Diffstat (limited to 'net/ieee802154/6lowpan_rtnl.c')
-rw-r--r-- | net/ieee802154/6lowpan_rtnl.c | 127 |
1 files changed, 86 insertions, 41 deletions
diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index 6591d27e53a4..44136297b673 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c | |||
@@ -71,18 +71,33 @@ struct lowpan_dev_record { | |||
71 | struct list_head list; | 71 | struct list_head list; |
72 | }; | 72 | }; |
73 | 73 | ||
74 | /* don't save pan id, it's intra pan */ | ||
75 | struct lowpan_addr { | ||
76 | u8 mode; | ||
77 | union { | ||
78 | /* IPv6 needs big endian here */ | ||
79 | __be64 extended_addr; | ||
80 | __be16 short_addr; | ||
81 | } u; | ||
82 | }; | ||
83 | |||
84 | struct lowpan_addr_info { | ||
85 | struct lowpan_addr daddr; | ||
86 | struct lowpan_addr saddr; | ||
87 | }; | ||
88 | |||
74 | static inline struct | 89 | static inline struct |
75 | lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) | 90 | lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) |
76 | { | 91 | { |
77 | return netdev_priv(dev); | 92 | return netdev_priv(dev); |
78 | } | 93 | } |
79 | 94 | ||
80 | static inline void lowpan_address_flip(u8 *src, u8 *dest) | 95 | static inline struct |
96 | lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb) | ||
81 | { | 97 | { |
82 | int i; | 98 | WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct lowpan_addr_info)); |
83 | 99 | return (struct lowpan_addr_info *)(skb->data - | |
84 | for (i = 0; i < IEEE802154_ADDR_LEN; i++) | 100 | sizeof(struct lowpan_addr_info)); |
85 | (dest)[IEEE802154_ADDR_LEN - i - 1] = (src)[i]; | ||
86 | } | 101 | } |
87 | 102 | ||
88 | static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, | 103 | static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, |
@@ -91,8 +106,7 @@ static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, | |||
91 | { | 106 | { |
92 | const u8 *saddr = _saddr; | 107 | const u8 *saddr = _saddr; |
93 | const u8 *daddr = _daddr; | 108 | const u8 *daddr = _daddr; |
94 | struct ieee802154_addr sa, da; | 109 | struct lowpan_addr_info *info; |
95 | struct ieee802154_mac_cb *cb = mac_cb_init(skb); | ||
96 | 110 | ||
97 | /* TODO: | 111 | /* TODO: |
98 | * if this package isn't ipv6 one, where should it be routed? | 112 | * if this package isn't ipv6 one, where should it be routed? |
@@ -106,41 +120,17 @@ static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, | |||
106 | raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); | 120 | raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); |
107 | raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); | 121 | raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); |
108 | 122 | ||
109 | lowpan_header_compress(skb, dev, type, daddr, saddr, len); | 123 | info = lowpan_skb_priv(skb); |
110 | |||
111 | /* NOTE1: I'm still unsure about the fact that compression and WPAN | ||
112 | * header are created here and not later in the xmit. So wait for | ||
113 | * an opinion of net maintainers. | ||
114 | */ | ||
115 | /* NOTE2: to be absolutely correct, we must derive PANid information | ||
116 | * from MAC subif of the 'dev' and 'real_dev' network devices, but | ||
117 | * this isn't implemented in mainline yet, so currently we assign 0xff | ||
118 | */ | ||
119 | cb->type = IEEE802154_FC_TYPE_DATA; | ||
120 | 124 | ||
121 | /* prepare wpan address data */ | 125 | /* TODO: Currently we only support extended_addr */ |
122 | sa.mode = IEEE802154_ADDR_LONG; | 126 | info->daddr.mode = IEEE802154_ADDR_LONG; |
123 | sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); | 127 | memcpy(&info->daddr.u.extended_addr, daddr, |
124 | sa.extended_addr = ieee802154_devaddr_from_raw(saddr); | 128 | sizeof(info->daddr.u.extended_addr)); |
129 | info->saddr.mode = IEEE802154_ADDR_LONG; | ||
130 | memcpy(&info->saddr.u.extended_addr, saddr, | ||
131 | sizeof(info->daddr.u.extended_addr)); | ||
125 | 132 | ||
126 | /* intra-PAN communications */ | 133 | return 0; |
127 | da.pan_id = sa.pan_id; | ||
128 | |||
129 | /* if the destination address is the broadcast address, use the | ||
130 | * corresponding short address | ||
131 | */ | ||
132 | if (lowpan_is_addr_broadcast(daddr)) { | ||
133 | da.mode = IEEE802154_ADDR_SHORT; | ||
134 | da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); | ||
135 | } else { | ||
136 | da.mode = IEEE802154_ADDR_LONG; | ||
137 | da.extended_addr = ieee802154_devaddr_from_raw(daddr); | ||
138 | } | ||
139 | |||
140 | cb->ackreq = !lowpan_is_addr_broadcast(daddr); | ||
141 | |||
142 | return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, | ||
143 | type, (void *)&da, (void *)&sa, 0); | ||
144 | } | 134 | } |
145 | 135 | ||
146 | static int lowpan_give_skb_to_devices(struct sk_buff *skb, | 136 | static int lowpan_give_skb_to_devices(struct sk_buff *skb, |
@@ -338,13 +328,68 @@ err: | |||
338 | return rc; | 328 | return rc; |
339 | } | 329 | } |
340 | 330 | ||
331 | static int lowpan_header(struct sk_buff *skb, struct net_device *dev) | ||
332 | { | ||
333 | struct ieee802154_addr sa, da; | ||
334 | struct ieee802154_mac_cb *cb = mac_cb_init(skb); | ||
335 | struct lowpan_addr_info info; | ||
336 | void *daddr, *saddr; | ||
337 | |||
338 | memcpy(&info, lowpan_skb_priv(skb), sizeof(info)); | ||
339 | |||
340 | /* TODO: Currently we only support extended_addr */ | ||
341 | daddr = &info.daddr.u.extended_addr; | ||
342 | saddr = &info.saddr.u.extended_addr; | ||
343 | |||
344 | lowpan_header_compress(skb, dev, ETH_P_IPV6, daddr, saddr, skb->len); | ||
345 | |||
346 | cb->type = IEEE802154_FC_TYPE_DATA; | ||
347 | |||
348 | /* prepare wpan address data */ | ||
349 | sa.mode = IEEE802154_ADDR_LONG; | ||
350 | sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); | ||
351 | sa.extended_addr = ieee802154_devaddr_from_raw(saddr); | ||
352 | |||
353 | /* intra-PAN communications */ | ||
354 | da.pan_id = sa.pan_id; | ||
355 | |||
356 | /* if the destination address is the broadcast address, use the | ||
357 | * corresponding short address | ||
358 | */ | ||
359 | if (lowpan_is_addr_broadcast((const u8 *)daddr)) { | ||
360 | da.mode = IEEE802154_ADDR_SHORT; | ||
361 | da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); | ||
362 | cb->ackreq = false; | ||
363 | } else { | ||
364 | da.mode = IEEE802154_ADDR_LONG; | ||
365 | da.extended_addr = ieee802154_devaddr_from_raw(daddr); | ||
366 | cb->ackreq = true; | ||
367 | } | ||
368 | |||
369 | return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, | ||
370 | ETH_P_IPV6, (void *)&da, (void *)&sa, 0); | ||
371 | } | ||
372 | |||
341 | static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) | 373 | static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) |
342 | { | 374 | { |
343 | struct ieee802154_hdr wpan_hdr; | 375 | struct ieee802154_hdr wpan_hdr; |
344 | int max_single; | 376 | int max_single, ret; |
345 | 377 | ||
346 | pr_debug("package xmit\n"); | 378 | pr_debug("package xmit\n"); |
347 | 379 | ||
380 | /* We must take a copy of the skb before we modify/replace the ipv6 | ||
381 | * header as the header could be used elsewhere | ||
382 | */ | ||
383 | skb = skb_unshare(skb, GFP_ATOMIC); | ||
384 | if (!skb) | ||
385 | return NET_XMIT_DROP; | ||
386 | |||
387 | ret = lowpan_header(skb, dev); | ||
388 | if (ret < 0) { | ||
389 | kfree_skb(skb); | ||
390 | return NET_XMIT_DROP; | ||
391 | } | ||
392 | |||
348 | if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) { | 393 | if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) { |
349 | kfree_skb(skb); | 394 | kfree_skb(skb); |
350 | return NET_XMIT_DROP; | 395 | return NET_XMIT_DROP; |