diff options
author | Alexander Aring <alex.aring@gmail.com> | 2015-01-04 11:10:56 -0500 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2015-01-08 01:25:59 -0500 |
commit | 4dc315e267fe4a3c863fec21f26aa0a418c3f07a (patch) | |
tree | 28520bbff95eb443d5a338072ddab110fc1d5bb5 /net | |
parent | 4662a0da544c8ed7ecf79ef0f6c4dc65be081352 (diff) |
ieee802154: 6lowpan: move transmit functionality
This patch moves all relevant transmit functionality into a separate tx.c
file. We can simple separate this functionality like we did it in mac802154.
Signed-off-by: Alexander Aring <alex.aring@gmail.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/ieee802154/6lowpan/6lowpan_i.h | 6 | ||||
-rw-r--r-- | net/ieee802154/6lowpan/6lowpan_rtnl.c | 264 | ||||
-rw-r--r-- | net/ieee802154/6lowpan/Makefile | 2 | ||||
-rw-r--r-- | net/ieee802154/6lowpan/tx.c | 271 |
4 files changed, 279 insertions, 264 deletions
diff --git a/net/ieee802154/6lowpan/6lowpan_i.h b/net/ieee802154/6lowpan/6lowpan_i.h index 29ec61b01752..e50f69da78eb 100644 --- a/net/ieee802154/6lowpan/6lowpan_i.h +++ b/net/ieee802154/6lowpan/6lowpan_i.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <linux/list.h> | 4 | #include <linux/list.h> |
5 | 5 | ||
6 | #include <net/ieee802154_netdev.h> | ||
6 | #include <net/inet_frag.h> | 7 | #include <net/inet_frag.h> |
7 | 8 | ||
8 | struct lowpan_create_arg { | 9 | struct lowpan_create_arg { |
@@ -63,4 +64,9 @@ int lowpan_net_frag_init(void); | |||
63 | void lowpan_rx_init(void); | 64 | void lowpan_rx_init(void); |
64 | void lowpan_rx_exit(void); | 65 | void lowpan_rx_exit(void); |
65 | 66 | ||
67 | int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, | ||
68 | unsigned short type, const void *_daddr, | ||
69 | const void *_saddr, unsigned int len); | ||
70 | netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev); | ||
71 | |||
66 | #endif /* __IEEE802154_6LOWPAN_I_H__ */ | 72 | #endif /* __IEEE802154_6LOWPAN_I_H__ */ |
diff --git a/net/ieee802154/6lowpan/6lowpan_rtnl.c b/net/ieee802154/6lowpan/6lowpan_rtnl.c index 9dce20e7f6a0..055fbb71ba6f 100644 --- a/net/ieee802154/6lowpan/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan/6lowpan_rtnl.c | |||
@@ -44,15 +44,10 @@ | |||
44 | * SUCH DAMAGE. | 44 | * SUCH DAMAGE. |
45 | */ | 45 | */ |
46 | 46 | ||
47 | #include <linux/bitops.h> | ||
48 | #include <linux/if_arp.h> | ||
49 | #include <linux/module.h> | 47 | #include <linux/module.h> |
50 | #include <linux/moduleparam.h> | ||
51 | #include <linux/netdevice.h> | 48 | #include <linux/netdevice.h> |
52 | #include <linux/ieee802154.h> | 49 | #include <linux/ieee802154.h> |
53 | #include <net/af_ieee802154.h> | 50 | |
54 | #include <net/ieee802154_netdev.h> | ||
55 | #include <net/6lowpan.h> | ||
56 | #include <net/ipv6.h> | 51 | #include <net/ipv6.h> |
57 | 52 | ||
58 | #include "6lowpan_i.h" | 53 | #include "6lowpan_i.h" |
@@ -60,263 +55,6 @@ | |||
60 | LIST_HEAD(lowpan_devices); | 55 | LIST_HEAD(lowpan_devices); |
61 | static int lowpan_open_count; | 56 | static int lowpan_open_count; |
62 | 57 | ||
63 | /* don't save pan id, it's intra pan */ | ||
64 | struct lowpan_addr { | ||
65 | u8 mode; | ||
66 | union { | ||
67 | /* IPv6 needs big endian here */ | ||
68 | __be64 extended_addr; | ||
69 | __be16 short_addr; | ||
70 | } u; | ||
71 | }; | ||
72 | |||
73 | struct lowpan_addr_info { | ||
74 | struct lowpan_addr daddr; | ||
75 | struct lowpan_addr saddr; | ||
76 | }; | ||
77 | |||
78 | static inline struct | ||
79 | lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb) | ||
80 | { | ||
81 | WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct lowpan_addr_info)); | ||
82 | return (struct lowpan_addr_info *)(skb->data - | ||
83 | sizeof(struct lowpan_addr_info)); | ||
84 | } | ||
85 | |||
86 | static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, | ||
87 | unsigned short type, const void *_daddr, | ||
88 | const void *_saddr, unsigned int len) | ||
89 | { | ||
90 | const u8 *saddr = _saddr; | ||
91 | const u8 *daddr = _daddr; | ||
92 | struct lowpan_addr_info *info; | ||
93 | |||
94 | /* TODO: | ||
95 | * if this package isn't ipv6 one, where should it be routed? | ||
96 | */ | ||
97 | if (type != ETH_P_IPV6) | ||
98 | return 0; | ||
99 | |||
100 | if (!saddr) | ||
101 | saddr = dev->dev_addr; | ||
102 | |||
103 | raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); | ||
104 | raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); | ||
105 | |||
106 | info = lowpan_skb_priv(skb); | ||
107 | |||
108 | /* TODO: Currently we only support extended_addr */ | ||
109 | info->daddr.mode = IEEE802154_ADDR_LONG; | ||
110 | memcpy(&info->daddr.u.extended_addr, daddr, | ||
111 | sizeof(info->daddr.u.extended_addr)); | ||
112 | info->saddr.mode = IEEE802154_ADDR_LONG; | ||
113 | memcpy(&info->saddr.u.extended_addr, saddr, | ||
114 | sizeof(info->daddr.u.extended_addr)); | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static struct sk_buff* | ||
120 | lowpan_alloc_frag(struct sk_buff *skb, int size, | ||
121 | const struct ieee802154_hdr *master_hdr) | ||
122 | { | ||
123 | struct net_device *real_dev = lowpan_dev_info(skb->dev)->real_dev; | ||
124 | struct sk_buff *frag; | ||
125 | int rc; | ||
126 | |||
127 | frag = alloc_skb(real_dev->hard_header_len + | ||
128 | real_dev->needed_tailroom + size, | ||
129 | GFP_ATOMIC); | ||
130 | |||
131 | if (likely(frag)) { | ||
132 | frag->dev = real_dev; | ||
133 | frag->priority = skb->priority; | ||
134 | skb_reserve(frag, real_dev->hard_header_len); | ||
135 | skb_reset_network_header(frag); | ||
136 | *mac_cb(frag) = *mac_cb(skb); | ||
137 | |||
138 | rc = dev_hard_header(frag, real_dev, 0, &master_hdr->dest, | ||
139 | &master_hdr->source, size); | ||
140 | if (rc < 0) { | ||
141 | kfree_skb(frag); | ||
142 | return ERR_PTR(rc); | ||
143 | } | ||
144 | } else { | ||
145 | frag = ERR_PTR(-ENOMEM); | ||
146 | } | ||
147 | |||
148 | return frag; | ||
149 | } | ||
150 | |||
151 | static int | ||
152 | lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr, | ||
153 | u8 *frag_hdr, int frag_hdrlen, | ||
154 | int offset, int len) | ||
155 | { | ||
156 | struct sk_buff *frag; | ||
157 | |||
158 | raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen); | ||
159 | |||
160 | frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr); | ||
161 | if (IS_ERR(frag)) | ||
162 | return -PTR_ERR(frag); | ||
163 | |||
164 | memcpy(skb_put(frag, frag_hdrlen), frag_hdr, frag_hdrlen); | ||
165 | memcpy(skb_put(frag, len), skb_network_header(skb) + offset, len); | ||
166 | |||
167 | raw_dump_table(__func__, " fragment dump", frag->data, frag->len); | ||
168 | |||
169 | return dev_queue_xmit(frag); | ||
170 | } | ||
171 | |||
172 | static int | ||
173 | lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, | ||
174 | const struct ieee802154_hdr *wpan_hdr) | ||
175 | { | ||
176 | u16 dgram_size, dgram_offset; | ||
177 | __be16 frag_tag; | ||
178 | u8 frag_hdr[5]; | ||
179 | int frag_cap, frag_len, payload_cap, rc; | ||
180 | int skb_unprocessed, skb_offset; | ||
181 | |||
182 | dgram_size = lowpan_uncompress_size(skb, &dgram_offset) - | ||
183 | skb->mac_len; | ||
184 | frag_tag = htons(lowpan_dev_info(dev)->fragment_tag); | ||
185 | lowpan_dev_info(dev)->fragment_tag++; | ||
186 | |||
187 | frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07); | ||
188 | frag_hdr[1] = dgram_size & 0xff; | ||
189 | memcpy(frag_hdr + 2, &frag_tag, sizeof(frag_tag)); | ||
190 | |||
191 | payload_cap = ieee802154_max_payload(wpan_hdr); | ||
192 | |||
193 | frag_len = round_down(payload_cap - LOWPAN_FRAG1_HEAD_SIZE - | ||
194 | skb_network_header_len(skb), 8); | ||
195 | |||
196 | skb_offset = skb_network_header_len(skb); | ||
197 | skb_unprocessed = skb->len - skb->mac_len - skb_offset; | ||
198 | |||
199 | rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, | ||
200 | LOWPAN_FRAG1_HEAD_SIZE, 0, | ||
201 | frag_len + skb_network_header_len(skb)); | ||
202 | if (rc) { | ||
203 | pr_debug("%s unable to send FRAG1 packet (tag: %d)", | ||
204 | __func__, ntohs(frag_tag)); | ||
205 | goto err; | ||
206 | } | ||
207 | |||
208 | frag_hdr[0] &= ~LOWPAN_DISPATCH_FRAG1; | ||
209 | frag_hdr[0] |= LOWPAN_DISPATCH_FRAGN; | ||
210 | frag_cap = round_down(payload_cap - LOWPAN_FRAGN_HEAD_SIZE, 8); | ||
211 | |||
212 | do { | ||
213 | dgram_offset += frag_len; | ||
214 | skb_offset += frag_len; | ||
215 | skb_unprocessed -= frag_len; | ||
216 | frag_len = min(frag_cap, skb_unprocessed); | ||
217 | |||
218 | frag_hdr[4] = dgram_offset >> 3; | ||
219 | |||
220 | rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, | ||
221 | LOWPAN_FRAGN_HEAD_SIZE, skb_offset, | ||
222 | frag_len); | ||
223 | if (rc) { | ||
224 | pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n", | ||
225 | __func__, ntohs(frag_tag), skb_offset); | ||
226 | goto err; | ||
227 | } | ||
228 | } while (skb_unprocessed > frag_cap); | ||
229 | |||
230 | consume_skb(skb); | ||
231 | return NET_XMIT_SUCCESS; | ||
232 | |||
233 | err: | ||
234 | kfree_skb(skb); | ||
235 | return rc; | ||
236 | } | ||
237 | |||
238 | static int lowpan_header(struct sk_buff *skb, struct net_device *dev) | ||
239 | { | ||
240 | struct ieee802154_addr sa, da; | ||
241 | struct ieee802154_mac_cb *cb = mac_cb_init(skb); | ||
242 | struct lowpan_addr_info info; | ||
243 | void *daddr, *saddr; | ||
244 | |||
245 | memcpy(&info, lowpan_skb_priv(skb), sizeof(info)); | ||
246 | |||
247 | /* TODO: Currently we only support extended_addr */ | ||
248 | daddr = &info.daddr.u.extended_addr; | ||
249 | saddr = &info.saddr.u.extended_addr; | ||
250 | |||
251 | lowpan_header_compress(skb, dev, ETH_P_IPV6, daddr, saddr, skb->len); | ||
252 | |||
253 | cb->type = IEEE802154_FC_TYPE_DATA; | ||
254 | |||
255 | /* prepare wpan address data */ | ||
256 | sa.mode = IEEE802154_ADDR_LONG; | ||
257 | sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); | ||
258 | sa.extended_addr = ieee802154_devaddr_from_raw(saddr); | ||
259 | |||
260 | /* intra-PAN communications */ | ||
261 | da.pan_id = sa.pan_id; | ||
262 | |||
263 | /* if the destination address is the broadcast address, use the | ||
264 | * corresponding short address | ||
265 | */ | ||
266 | if (lowpan_is_addr_broadcast((const u8 *)daddr)) { | ||
267 | da.mode = IEEE802154_ADDR_SHORT; | ||
268 | da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); | ||
269 | cb->ackreq = false; | ||
270 | } else { | ||
271 | da.mode = IEEE802154_ADDR_LONG; | ||
272 | da.extended_addr = ieee802154_devaddr_from_raw(daddr); | ||
273 | cb->ackreq = true; | ||
274 | } | ||
275 | |||
276 | return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, | ||
277 | ETH_P_IPV6, (void *)&da, (void *)&sa, 0); | ||
278 | } | ||
279 | |||
280 | static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) | ||
281 | { | ||
282 | struct ieee802154_hdr wpan_hdr; | ||
283 | int max_single, ret; | ||
284 | |||
285 | pr_debug("package xmit\n"); | ||
286 | |||
287 | /* We must take a copy of the skb before we modify/replace the ipv6 | ||
288 | * header as the header could be used elsewhere | ||
289 | */ | ||
290 | skb = skb_unshare(skb, GFP_ATOMIC); | ||
291 | if (!skb) | ||
292 | return NET_XMIT_DROP; | ||
293 | |||
294 | ret = lowpan_header(skb, dev); | ||
295 | if (ret < 0) { | ||
296 | kfree_skb(skb); | ||
297 | return NET_XMIT_DROP; | ||
298 | } | ||
299 | |||
300 | if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) { | ||
301 | kfree_skb(skb); | ||
302 | return NET_XMIT_DROP; | ||
303 | } | ||
304 | |||
305 | max_single = ieee802154_max_payload(&wpan_hdr); | ||
306 | |||
307 | if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) { | ||
308 | skb->dev = lowpan_dev_info(dev)->real_dev; | ||
309 | return dev_queue_xmit(skb); | ||
310 | } else { | ||
311 | netdev_tx_t rc; | ||
312 | |||
313 | pr_debug("frame is too big, fragmentation is needed\n"); | ||
314 | rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr); | ||
315 | |||
316 | return rc < 0 ? NET_XMIT_DROP : rc; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | static __le16 lowpan_get_pan_id(const struct net_device *dev) | 58 | static __le16 lowpan_get_pan_id(const struct net_device *dev) |
321 | { | 59 | { |
322 | struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; | 60 | struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; |
diff --git a/net/ieee802154/6lowpan/Makefile b/net/ieee802154/6lowpan/Makefile index 8e8397185751..7f11c136bcfe 100644 --- a/net/ieee802154/6lowpan/Makefile +++ b/net/ieee802154/6lowpan/Makefile | |||
@@ -1,3 +1,3 @@ | |||
1 | obj-y += ieee802154_6lowpan.o | 1 | obj-y += ieee802154_6lowpan.o |
2 | 2 | ||
3 | ieee802154_6lowpan-y := 6lowpan_rtnl.o rx.o reassembly.o | 3 | ieee802154_6lowpan-y := 6lowpan_rtnl.o rx.o reassembly.o tx.o |
diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c new file mode 100644 index 000000000000..2349070bd534 --- /dev/null +++ b/net/ieee802154/6lowpan/tx.c | |||
@@ -0,0 +1,271 @@ | |||
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 | |||
11 | #include <net/6lowpan.h> | ||
12 | #include <net/ieee802154_netdev.h> | ||
13 | |||
14 | #include "6lowpan_i.h" | ||
15 | |||
16 | /* don't save pan id, it's intra pan */ | ||
17 | struct lowpan_addr { | ||
18 | u8 mode; | ||
19 | union { | ||
20 | /* IPv6 needs big endian here */ | ||
21 | __be64 extended_addr; | ||
22 | __be16 short_addr; | ||
23 | } u; | ||
24 | }; | ||
25 | |||
26 | struct lowpan_addr_info { | ||
27 | struct lowpan_addr daddr; | ||
28 | struct lowpan_addr saddr; | ||
29 | }; | ||
30 | |||
31 | static inline struct | ||
32 | lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb) | ||
33 | { | ||
34 | WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct lowpan_addr_info)); | ||
35 | return (struct lowpan_addr_info *)(skb->data - | ||
36 | sizeof(struct lowpan_addr_info)); | ||
37 | } | ||
38 | |||
39 | int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, | ||
40 | unsigned short type, const void *_daddr, | ||
41 | const void *_saddr, unsigned int len) | ||
42 | { | ||
43 | const u8 *saddr = _saddr; | ||
44 | const u8 *daddr = _daddr; | ||
45 | struct lowpan_addr_info *info; | ||
46 | |||
47 | /* TODO: | ||
48 | * if this package isn't ipv6 one, where should it be routed? | ||
49 | */ | ||
50 | if (type != ETH_P_IPV6) | ||
51 | return 0; | ||
52 | |||
53 | if (!saddr) | ||
54 | saddr = dev->dev_addr; | ||
55 | |||
56 | raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); | ||
57 | raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); | ||
58 | |||
59 | info = lowpan_skb_priv(skb); | ||
60 | |||
61 | /* TODO: Currently we only support extended_addr */ | ||
62 | info->daddr.mode = IEEE802154_ADDR_LONG; | ||
63 | memcpy(&info->daddr.u.extended_addr, daddr, | ||
64 | sizeof(info->daddr.u.extended_addr)); | ||
65 | info->saddr.mode = IEEE802154_ADDR_LONG; | ||
66 | memcpy(&info->saddr.u.extended_addr, saddr, | ||
67 | sizeof(info->daddr.u.extended_addr)); | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static struct sk_buff* | ||
73 | lowpan_alloc_frag(struct sk_buff *skb, int size, | ||
74 | const struct ieee802154_hdr *master_hdr) | ||
75 | { | ||
76 | struct net_device *real_dev = lowpan_dev_info(skb->dev)->real_dev; | ||
77 | struct sk_buff *frag; | ||
78 | int rc; | ||
79 | |||
80 | frag = alloc_skb(real_dev->hard_header_len + | ||
81 | real_dev->needed_tailroom + size, | ||
82 | GFP_ATOMIC); | ||
83 | |||
84 | if (likely(frag)) { | ||
85 | frag->dev = real_dev; | ||
86 | frag->priority = skb->priority; | ||
87 | skb_reserve(frag, real_dev->hard_header_len); | ||
88 | skb_reset_network_header(frag); | ||
89 | *mac_cb(frag) = *mac_cb(skb); | ||
90 | |||
91 | rc = dev_hard_header(frag, real_dev, 0, &master_hdr->dest, | ||
92 | &master_hdr->source, size); | ||
93 | if (rc < 0) { | ||
94 | kfree_skb(frag); | ||
95 | return ERR_PTR(rc); | ||
96 | } | ||
97 | } else { | ||
98 | frag = ERR_PTR(-ENOMEM); | ||
99 | } | ||
100 | |||
101 | return frag; | ||
102 | } | ||
103 | |||
104 | static int | ||
105 | lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr, | ||
106 | u8 *frag_hdr, int frag_hdrlen, | ||
107 | int offset, int len) | ||
108 | { | ||
109 | struct sk_buff *frag; | ||
110 | |||
111 | raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen); | ||
112 | |||
113 | frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr); | ||
114 | if (IS_ERR(frag)) | ||
115 | return -PTR_ERR(frag); | ||
116 | |||
117 | memcpy(skb_put(frag, frag_hdrlen), frag_hdr, frag_hdrlen); | ||
118 | memcpy(skb_put(frag, len), skb_network_header(skb) + offset, len); | ||
119 | |||
120 | raw_dump_table(__func__, " fragment dump", frag->data, frag->len); | ||
121 | |||
122 | return dev_queue_xmit(frag); | ||
123 | } | ||
124 | |||
125 | static int | ||
126 | lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, | ||
127 | const struct ieee802154_hdr *wpan_hdr) | ||
128 | { | ||
129 | u16 dgram_size, dgram_offset; | ||
130 | __be16 frag_tag; | ||
131 | u8 frag_hdr[5]; | ||
132 | int frag_cap, frag_len, payload_cap, rc; | ||
133 | int skb_unprocessed, skb_offset; | ||
134 | |||
135 | dgram_size = lowpan_uncompress_size(skb, &dgram_offset) - | ||
136 | skb->mac_len; | ||
137 | frag_tag = htons(lowpan_dev_info(dev)->fragment_tag); | ||
138 | lowpan_dev_info(dev)->fragment_tag++; | ||
139 | |||
140 | frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07); | ||
141 | frag_hdr[1] = dgram_size & 0xff; | ||
142 | memcpy(frag_hdr + 2, &frag_tag, sizeof(frag_tag)); | ||
143 | |||
144 | payload_cap = ieee802154_max_payload(wpan_hdr); | ||
145 | |||
146 | frag_len = round_down(payload_cap - LOWPAN_FRAG1_HEAD_SIZE - | ||
147 | skb_network_header_len(skb), 8); | ||
148 | |||
149 | skb_offset = skb_network_header_len(skb); | ||
150 | skb_unprocessed = skb->len - skb->mac_len - skb_offset; | ||
151 | |||
152 | rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, | ||
153 | LOWPAN_FRAG1_HEAD_SIZE, 0, | ||
154 | frag_len + skb_network_header_len(skb)); | ||
155 | if (rc) { | ||
156 | pr_debug("%s unable to send FRAG1 packet (tag: %d)", | ||
157 | __func__, ntohs(frag_tag)); | ||
158 | goto err; | ||
159 | } | ||
160 | |||
161 | frag_hdr[0] &= ~LOWPAN_DISPATCH_FRAG1; | ||
162 | frag_hdr[0] |= LOWPAN_DISPATCH_FRAGN; | ||
163 | frag_cap = round_down(payload_cap - LOWPAN_FRAGN_HEAD_SIZE, 8); | ||
164 | |||
165 | do { | ||
166 | dgram_offset += frag_len; | ||
167 | skb_offset += frag_len; | ||
168 | skb_unprocessed -= frag_len; | ||
169 | frag_len = min(frag_cap, skb_unprocessed); | ||
170 | |||
171 | frag_hdr[4] = dgram_offset >> 3; | ||
172 | |||
173 | rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, | ||
174 | LOWPAN_FRAGN_HEAD_SIZE, skb_offset, | ||
175 | frag_len); | ||
176 | if (rc) { | ||
177 | pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n", | ||
178 | __func__, ntohs(frag_tag), skb_offset); | ||
179 | goto err; | ||
180 | } | ||
181 | } while (skb_unprocessed > frag_cap); | ||
182 | |||
183 | consume_skb(skb); | ||
184 | return NET_XMIT_SUCCESS; | ||
185 | |||
186 | err: | ||
187 | kfree_skb(skb); | ||
188 | return rc; | ||
189 | } | ||
190 | |||
191 | static int lowpan_header(struct sk_buff *skb, struct net_device *dev) | ||
192 | { | ||
193 | struct ieee802154_addr sa, da; | ||
194 | struct ieee802154_mac_cb *cb = mac_cb_init(skb); | ||
195 | struct lowpan_addr_info info; | ||
196 | void *daddr, *saddr; | ||
197 | |||
198 | memcpy(&info, lowpan_skb_priv(skb), sizeof(info)); | ||
199 | |||
200 | /* TODO: Currently we only support extended_addr */ | ||
201 | daddr = &info.daddr.u.extended_addr; | ||
202 | saddr = &info.saddr.u.extended_addr; | ||
203 | |||
204 | lowpan_header_compress(skb, dev, ETH_P_IPV6, daddr, saddr, skb->len); | ||
205 | |||
206 | cb->type = IEEE802154_FC_TYPE_DATA; | ||
207 | |||
208 | /* prepare wpan address data */ | ||
209 | sa.mode = IEEE802154_ADDR_LONG; | ||
210 | sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); | ||
211 | sa.extended_addr = ieee802154_devaddr_from_raw(saddr); | ||
212 | |||
213 | /* intra-PAN communications */ | ||
214 | da.pan_id = sa.pan_id; | ||
215 | |||
216 | /* if the destination address is the broadcast address, use the | ||
217 | * corresponding short address | ||
218 | */ | ||
219 | if (lowpan_is_addr_broadcast((const u8 *)daddr)) { | ||
220 | da.mode = IEEE802154_ADDR_SHORT; | ||
221 | da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); | ||
222 | cb->ackreq = false; | ||
223 | } else { | ||
224 | da.mode = IEEE802154_ADDR_LONG; | ||
225 | da.extended_addr = ieee802154_devaddr_from_raw(daddr); | ||
226 | cb->ackreq = true; | ||
227 | } | ||
228 | |||
229 | return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, | ||
230 | ETH_P_IPV6, (void *)&da, (void *)&sa, 0); | ||
231 | } | ||
232 | |||
233 | netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) | ||
234 | { | ||
235 | struct ieee802154_hdr wpan_hdr; | ||
236 | int max_single, ret; | ||
237 | |||
238 | pr_debug("package xmit\n"); | ||
239 | |||
240 | /* We must take a copy of the skb before we modify/replace the ipv6 | ||
241 | * header as the header could be used elsewhere | ||
242 | */ | ||
243 | skb = skb_unshare(skb, GFP_ATOMIC); | ||
244 | if (!skb) | ||
245 | return NET_XMIT_DROP; | ||
246 | |||
247 | ret = lowpan_header(skb, dev); | ||
248 | if (ret < 0) { | ||
249 | kfree_skb(skb); | ||
250 | return NET_XMIT_DROP; | ||
251 | } | ||
252 | |||
253 | if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) { | ||
254 | kfree_skb(skb); | ||
255 | return NET_XMIT_DROP; | ||
256 | } | ||
257 | |||
258 | max_single = ieee802154_max_payload(&wpan_hdr); | ||
259 | |||
260 | if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) { | ||
261 | skb->dev = lowpan_dev_info(dev)->real_dev; | ||
262 | return dev_queue_xmit(skb); | ||
263 | } else { | ||
264 | netdev_tx_t rc; | ||
265 | |||
266 | pr_debug("frame is too big, fragmentation is needed\n"); | ||
267 | rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr); | ||
268 | |||
269 | return rc < 0 ? NET_XMIT_DROP : rc; | ||
270 | } | ||
271 | } | ||