aboutsummaryrefslogtreecommitdiffstats
path: root/net/ieee802154/6lowpan_rtnl.c
diff options
context:
space:
mode:
authorSimon Vincent <simon.vincent@xsilon.com>2014-09-24 06:21:33 -0400
committerMarcel Holtmann <marcel@holtmann.org>2014-09-24 08:15:08 -0400
commitf19f4f9525cf32f97341fac20ce66392e86a1b67 (patch)
treee0f0090d9b1b96fa67f29a8dfbf639e80a07d4b8 /net/ieee802154/6lowpan_rtnl.c
parentca079ad6af0d9948101992d03e7145ab8b426f66 (diff)
ieee802154: 6lowpan: ensure header compression does not corrupt ipv6 header
The 6lowpan ipv6 header compression was causing problems for other interfaces that expected a ipv6 header to still be in place, as we were replacing the ipv6 header with a compressed version. This happened if you sent a packet to a multicast address as the packet would be output on 802.15.4, ethernet, and also be sent to the loopback interface. The skb data was shared between these interfaces so all interfaces ended up with a compressed ipv6 header. The solution is to ensure that before we do any header compression we are not sharing the skb or skb data with any other interface. If we are then we must take a copy of the skb and skb data before modifying the ipv6 header. The only place we can copy the skb is inside the xmit function so we don't leave dangling references to skb. This patch moves all the header compression to inside the xmit function. Very little code has been changed it has mostly been moved from lowpan_header_create to lowpan_xmit. At the top of the xmit function we now check if the skb is shared and if so copy it. In lowpan_header_create all we do now is store the source and destination addresses for use later when we compress the header. Signed-off-by: Simon Vincent <simon.vincent@xsilon.com> Signed-off-by: Alexander Aring <alex.aring@gmail.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/ieee802154/6lowpan_rtnl.c')
-rw-r--r--net/ieee802154/6lowpan_rtnl.c125
1 files changed, 89 insertions, 36 deletions
diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c
index 5e788cdc499a..44136297b673 100644
--- a/net/ieee802154/6lowpan_rtnl.c
+++ b/net/ieee802154/6lowpan_rtnl.c
@@ -71,20 +71,42 @@ 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 */
75struct 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
84struct lowpan_addr_info {
85 struct lowpan_addr daddr;
86 struct lowpan_addr saddr;
87};
88
74static inline struct 89static inline struct
75lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) 90lowpan_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
95static inline struct
96lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb)
97{
98 WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct lowpan_addr_info));
99 return (struct lowpan_addr_info *)(skb->data -
100 sizeof(struct lowpan_addr_info));
101}
102
80static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, 103static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
81 unsigned short type, const void *_daddr, 104 unsigned short type, const void *_daddr,
82 const void *_saddr, unsigned int len) 105 const void *_saddr, unsigned int len)
83{ 106{
84 const u8 *saddr = _saddr; 107 const u8 *saddr = _saddr;
85 const u8 *daddr = _daddr; 108 const u8 *daddr = _daddr;
86 struct ieee802154_addr sa, da; 109 struct lowpan_addr_info *info;
87 struct ieee802154_mac_cb *cb = mac_cb_init(skb);
88 110
89 /* TODO: 111 /* TODO:
90 * 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?
@@ -98,41 +120,17 @@ static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
98 raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); 120 raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8);
99 raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); 121 raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8);
100 122
101 lowpan_header_compress(skb, dev, type, daddr, saddr, len); 123 info = lowpan_skb_priv(skb);
102
103 /* NOTE1: I'm still unsure about the fact that compression and WPAN
104 * header are created here and not later in the xmit. So wait for
105 * an opinion of net maintainers.
106 */
107 /* NOTE2: to be absolutely correct, we must derive PANid information
108 * from MAC subif of the 'dev' and 'real_dev' network devices, but
109 * this isn't implemented in mainline yet, so currently we assign 0xff
110 */
111 cb->type = IEEE802154_FC_TYPE_DATA;
112
113 /* prepare wpan address data */
114 sa.mode = IEEE802154_ADDR_LONG;
115 sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
116 sa.extended_addr = ieee802154_devaddr_from_raw(saddr);
117
118 /* intra-PAN communications */
119 da.pan_id = sa.pan_id;
120
121 /* if the destination address is the broadcast address, use the
122 * corresponding short address
123 */
124 if (lowpan_is_addr_broadcast(daddr)) {
125 da.mode = IEEE802154_ADDR_SHORT;
126 da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
127 } else {
128 da.mode = IEEE802154_ADDR_LONG;
129 da.extended_addr = ieee802154_devaddr_from_raw(daddr);
130 }
131 124
132 cb->ackreq = !lowpan_is_addr_broadcast(daddr); 125 /* TODO: Currently we only support extended_addr */
126 info->daddr.mode = IEEE802154_ADDR_LONG;
127 memcpy(&info->daddr.u.extended_addr, daddr,
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));
133 132
134 return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, 133 return 0;
135 type, (void *)&da, (void *)&sa, 0);
136} 134}
137 135
138static int lowpan_give_skb_to_devices(struct sk_buff *skb, 136static int lowpan_give_skb_to_devices(struct sk_buff *skb,
@@ -330,13 +328,68 @@ err:
330 return rc; 328 return rc;
331} 329}
332 330
331static 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
333static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) 373static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
334{ 374{
335 struct ieee802154_hdr wpan_hdr; 375 struct ieee802154_hdr wpan_hdr;
336 int max_single; 376 int max_single, ret;
337 377
338 pr_debug("package xmit\n"); 378 pr_debug("package xmit\n");
339 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
340 if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) { 393 if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) {
341 kfree_skb(skb); 394 kfree_skb(skb);
342 return NET_XMIT_DROP; 395 return NET_XMIT_DROP;