|
|
@ -104,6 +104,7 @@ static const u8 lowpan_llprefix[] = {0xfe, 0x80};
|
|
|
|
struct lowpan_dev_info {
|
|
|
|
struct lowpan_dev_info {
|
|
|
|
struct net_device *real_dev; /* real WPAN device ptr */
|
|
|
|
struct net_device *real_dev; /* real WPAN device ptr */
|
|
|
|
struct mutex dev_list_mtx; /* mutex for list ops */
|
|
|
|
struct mutex dev_list_mtx; /* mutex for list ops */
|
|
|
|
|
|
|
|
unsigned short fragment_tag;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct lowpan_dev_record {
|
|
|
|
struct lowpan_dev_record {
|
|
|
@ -120,7 +121,6 @@ struct lowpan_fragment {
|
|
|
|
struct list_head list; /* fragments list */
|
|
|
|
struct list_head list; /* fragments list */
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned short fragment_tag;
|
|
|
|
|
|
|
|
static LIST_HEAD(lowpan_fragments);
|
|
|
|
static LIST_HEAD(lowpan_fragments);
|
|
|
|
static DEFINE_SPINLOCK(flist_lock);
|
|
|
|
static DEFINE_SPINLOCK(flist_lock);
|
|
|
|
|
|
|
|
|
|
|
@ -284,6 +284,9 @@ lowpan_compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb)
|
|
|
|
/* checksum is always inline */
|
|
|
|
/* checksum is always inline */
|
|
|
|
memcpy(*hc06_ptr, &uh->check, 2);
|
|
|
|
memcpy(*hc06_ptr, &uh->check, 2);
|
|
|
|
*hc06_ptr += 2;
|
|
|
|
*hc06_ptr += 2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* skip the UDP header */
|
|
|
|
|
|
|
|
skb_pull(skb, sizeof(struct udphdr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val)
|
|
|
|
static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val)
|
|
|
@ -309,9 +312,8 @@ static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
lowpan_uncompress_udp_header(struct sk_buff *skb)
|
|
|
|
lowpan_uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct udphdr *uh = udp_hdr(skb);
|
|
|
|
|
|
|
|
u8 tmp;
|
|
|
|
u8 tmp;
|
|
|
|
|
|
|
|
|
|
|
|
if (!uh)
|
|
|
|
if (!uh)
|
|
|
@ -358,6 +360,14 @@ lowpan_uncompress_udp_header(struct sk_buff *skb)
|
|
|
|
/* copy checksum */
|
|
|
|
/* copy checksum */
|
|
|
|
memcpy(&uh->check, &skb->data[0], 2);
|
|
|
|
memcpy(&uh->check, &skb->data[0], 2);
|
|
|
|
skb_pull(skb, 2);
|
|
|
|
skb_pull(skb, 2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* UDP lenght needs to be infered from the lower layers
|
|
|
|
|
|
|
|
* here, we obtain the hint from the remaining size of the
|
|
|
|
|
|
|
|
* frame
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
uh->len = htons(skb->len + sizeof(struct udphdr));
|
|
|
|
|
|
|
|
pr_debug("uncompressed UDP length: src = %d", uh->len);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
pr_debug("ERROR: unsupported NH format\n");
|
|
|
|
pr_debug("ERROR: unsupported NH format\n");
|
|
|
|
goto err;
|
|
|
|
goto err;
|
|
|
@ -572,17 +582,31 @@ static int lowpan_header_create(struct sk_buff *skb,
|
|
|
|
* this isn't implemented in mainline yet, so currently we assign 0xff
|
|
|
|
* this isn't implemented in mainline yet, so currently we assign 0xff
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
|
|
|
|
|
|
|
|
mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
|
|
|
|
|
|
|
|
|
|
|
|
/* prepare wpan address data */
|
|
|
|
/* prepare wpan address data */
|
|
|
|
sa.addr_type = IEEE802154_ADDR_LONG;
|
|
|
|
sa.addr_type = IEEE802154_ADDR_LONG;
|
|
|
|
sa.pan_id = 0xff;
|
|
|
|
sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
|
|
|
|
|
|
|
|
|
|
|
|
da.addr_type = IEEE802154_ADDR_LONG;
|
|
|
|
|
|
|
|
da.pan_id = 0xff;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(&(da.hwaddr), daddr, 8);
|
|
|
|
|
|
|
|
memcpy(&(sa.hwaddr), saddr, 8);
|
|
|
|
memcpy(&(sa.hwaddr), saddr, 8);
|
|
|
|
|
|
|
|
/* intra-PAN communications */
|
|
|
|
|
|
|
|
da.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
|
|
|
|
|
|
|
|
|
|
|
|
mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
|
|
|
|
/*
|
|
|
|
|
|
|
|
* if the destination address is the broadcast address, use the
|
|
|
|
|
|
|
|
* corresponding short address
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (lowpan_is_addr_broadcast(daddr)) {
|
|
|
|
|
|
|
|
da.addr_type = IEEE802154_ADDR_SHORT;
|
|
|
|
|
|
|
|
da.short_addr = IEEE802154_ADDR_BROADCAST;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
da.addr_type = IEEE802154_ADDR_LONG;
|
|
|
|
|
|
|
|
memcpy(&(da.hwaddr), daddr, 8);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* request acknowledgment */
|
|
|
|
|
|
|
|
mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
|
|
|
|
return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
|
|
|
|
type, (void *)&da, (void *)&sa, skb->len);
|
|
|
|
type, (void *)&da, (void *)&sa, skb->len);
|
|
|
@ -650,7 +674,7 @@ static void lowpan_fragment_timer_expired(unsigned long entry_addr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct lowpan_fragment *
|
|
|
|
static struct lowpan_fragment *
|
|
|
|
lowpan_alloc_new_frame(struct sk_buff *skb, u8 len, u16 tag)
|
|
|
|
lowpan_alloc_new_frame(struct sk_buff *skb, u16 len, u16 tag)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct lowpan_fragment *frame;
|
|
|
|
struct lowpan_fragment *frame;
|
|
|
|
|
|
|
|
|
|
|
@ -720,7 +744,7 @@ lowpan_process_data(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct lowpan_fragment *frame;
|
|
|
|
struct lowpan_fragment *frame;
|
|
|
|
/* slen stores the rightmost 8 bits of the 11 bits length */
|
|
|
|
/* slen stores the rightmost 8 bits of the 11 bits length */
|
|
|
|
u8 slen, offset;
|
|
|
|
u8 slen, offset = 0;
|
|
|
|
u16 len, tag;
|
|
|
|
u16 len, tag;
|
|
|
|
bool found = false;
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
|
|
|
@ -731,6 +755,18 @@ lowpan_process_data(struct sk_buff *skb)
|
|
|
|
/* adds the 3 MSB to the 8 LSB to retrieve the 11 bits length */
|
|
|
|
/* adds the 3 MSB to the 8 LSB to retrieve the 11 bits length */
|
|
|
|
len = ((iphc0 & 7) << 8) | slen;
|
|
|
|
len = ((iphc0 & 7) << 8) | slen;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1) {
|
|
|
|
|
|
|
|
pr_debug("%s received a FRAG1 packet (tag: %d, "
|
|
|
|
|
|
|
|
"size of the entire IP packet: %d)",
|
|
|
|
|
|
|
|
__func__, tag, len);
|
|
|
|
|
|
|
|
} else { /* FRAGN */
|
|
|
|
|
|
|
|
if (lowpan_fetch_skb_u8(skb, &offset))
|
|
|
|
|
|
|
|
goto unlock_and_drop;
|
|
|
|
|
|
|
|
pr_debug("%s received a FRAGN packet (tag: %d, "
|
|
|
|
|
|
|
|
"size of the entire IP packet: %d, "
|
|
|
|
|
|
|
|
"offset: %d)", __func__, tag, len, offset * 8);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* check if frame assembling with the same tag is
|
|
|
|
* check if frame assembling with the same tag is
|
|
|
|
* already in progress
|
|
|
|
* already in progress
|
|
|
@ -745,17 +781,13 @@ lowpan_process_data(struct sk_buff *skb)
|
|
|
|
|
|
|
|
|
|
|
|
/* alloc new frame structure */
|
|
|
|
/* alloc new frame structure */
|
|
|
|
if (!found) {
|
|
|
|
if (!found) {
|
|
|
|
|
|
|
|
pr_debug("%s first fragment received for tag %d, "
|
|
|
|
|
|
|
|
"begin packet reassembly", __func__, tag);
|
|
|
|
frame = lowpan_alloc_new_frame(skb, len, tag);
|
|
|
|
frame = lowpan_alloc_new_frame(skb, len, tag);
|
|
|
|
if (!frame)
|
|
|
|
if (!frame)
|
|
|
|
goto unlock_and_drop;
|
|
|
|
goto unlock_and_drop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1)
|
|
|
|
|
|
|
|
goto unlock_and_drop;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (lowpan_fetch_skb_u8(skb, &offset)) /* fetch offset */
|
|
|
|
|
|
|
|
goto unlock_and_drop;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* if payload fits buffer, copy it */
|
|
|
|
/* if payload fits buffer, copy it */
|
|
|
|
if (likely((offset * 8 + skb->len) <= frame->length))
|
|
|
|
if (likely((offset * 8 + skb->len) <= frame->length))
|
|
|
|
skb_copy_to_linear_data_offset(frame->skb, offset * 8,
|
|
|
|
skb_copy_to_linear_data_offset(frame->skb, offset * 8,
|
|
|
@ -773,6 +805,9 @@ lowpan_process_data(struct sk_buff *skb)
|
|
|
|
list_del(&frame->list);
|
|
|
|
list_del(&frame->list);
|
|
|
|
spin_unlock_bh(&flist_lock);
|
|
|
|
spin_unlock_bh(&flist_lock);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pr_debug("%s successfully reassembled fragment "
|
|
|
|
|
|
|
|
"(tag %d)", __func__, tag);
|
|
|
|
|
|
|
|
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
skb = frame->skb;
|
|
|
|
skb = frame->skb;
|
|
|
|
kfree(frame);
|
|
|
|
kfree(frame);
|
|
|
@ -918,10 +953,35 @@ lowpan_process_data(struct sk_buff *skb)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* UDP data uncompression */
|
|
|
|
/* UDP data uncompression */
|
|
|
|
if (iphc0 & LOWPAN_IPHC_NH_C)
|
|
|
|
if (iphc0 & LOWPAN_IPHC_NH_C) {
|
|
|
|
if (lowpan_uncompress_udp_header(skb))
|
|
|
|
struct udphdr uh;
|
|
|
|
|
|
|
|
struct sk_buff *new;
|
|
|
|
|
|
|
|
if (lowpan_uncompress_udp_header(skb, &uh))
|
|
|
|
goto drop;
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* replace the compressed UDP head by the uncompressed UDP
|
|
|
|
|
|
|
|
* header
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
new = skb_copy_expand(skb, sizeof(struct udphdr),
|
|
|
|
|
|
|
|
skb_tailroom(skb), GFP_ATOMIC);
|
|
|
|
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!new)
|
|
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
skb = new;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
skb_push(skb, sizeof(struct udphdr));
|
|
|
|
|
|
|
|
skb_reset_transport_header(skb);
|
|
|
|
|
|
|
|
skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lowpan_raw_dump_table(__func__, "raw UDP header dump",
|
|
|
|
|
|
|
|
(u8 *)&uh, sizeof(uh));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hdr.nexthdr = UIP_PROTO_UDP;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Not fragmented package */
|
|
|
|
/* Not fragmented package */
|
|
|
|
hdr.payload_len = htons(skb->len);
|
|
|
|
hdr.payload_len = htons(skb->len);
|
|
|
|
|
|
|
|
|
|
|
@ -969,13 +1029,13 @@ static int lowpan_get_mac_header_length(struct sk_buff *skb)
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
|
|
|
|
lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
|
|
|
|
int mlen, int plen, int offset)
|
|
|
|
int mlen, int plen, int offset, int type)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct sk_buff *frag;
|
|
|
|
struct sk_buff *frag;
|
|
|
|
int hlen, ret;
|
|
|
|
int hlen, ret;
|
|
|
|
|
|
|
|
|
|
|
|
/* if payload length is zero, therefore it's a first fragment */
|
|
|
|
hlen = (type == LOWPAN_DISPATCH_FRAG1) ?
|
|
|
|
hlen = (plen == 0 ? LOWPAN_FRAG1_HEAD_SIZE : LOWPAN_FRAGN_HEAD_SIZE);
|
|
|
|
LOWPAN_FRAG1_HEAD_SIZE : LOWPAN_FRAGN_HEAD_SIZE;
|
|
|
|
|
|
|
|
|
|
|
|
lowpan_raw_dump_inline(__func__, "6lowpan fragment header", head, hlen);
|
|
|
|
lowpan_raw_dump_inline(__func__, "6lowpan fragment header", head, hlen);
|
|
|
|
|
|
|
|
|
|
|
@ -1003,14 +1063,14 @@ lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
lowpan_skb_fragmentation(struct sk_buff *skb)
|
|
|
|
lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
int err, header_length, payload_length, tag, offset = 0;
|
|
|
|
int err, header_length, payload_length, tag, offset = 0;
|
|
|
|
u8 head[5];
|
|
|
|
u8 head[5];
|
|
|
|
|
|
|
|
|
|
|
|
header_length = lowpan_get_mac_header_length(skb);
|
|
|
|
header_length = lowpan_get_mac_header_length(skb);
|
|
|
|
payload_length = skb->len - header_length;
|
|
|
|
payload_length = skb->len - header_length;
|
|
|
|
tag = fragment_tag++;
|
|
|
|
tag = lowpan_dev_info(dev)->fragment_tag++;
|
|
|
|
|
|
|
|
|
|
|
|
/* first fragment header */
|
|
|
|
/* first fragment header */
|
|
|
|
head[0] = LOWPAN_DISPATCH_FRAG1 | ((payload_length >> 8) & 0x7);
|
|
|
|
head[0] = LOWPAN_DISPATCH_FRAG1 | ((payload_length >> 8) & 0x7);
|
|
|
@ -1018,7 +1078,16 @@ lowpan_skb_fragmentation(struct sk_buff *skb)
|
|
|
|
head[2] = tag >> 8;
|
|
|
|
head[2] = tag >> 8;
|
|
|
|
head[3] = tag & 0xff;
|
|
|
|
head[3] = tag & 0xff;
|
|
|
|
|
|
|
|
|
|
|
|
err = lowpan_fragment_xmit(skb, head, header_length, 0, 0);
|
|
|
|
err = lowpan_fragment_xmit(skb, head, header_length, LOWPAN_FRAG_SIZE,
|
|
|
|
|
|
|
|
0, LOWPAN_DISPATCH_FRAG1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
|
|
pr_debug("%s unable to send FRAG1 packet (tag: %d)",
|
|
|
|
|
|
|
|
__func__, tag);
|
|
|
|
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
offset = LOWPAN_FRAG_SIZE;
|
|
|
|
|
|
|
|
|
|
|
|
/* next fragment header */
|
|
|
|
/* next fragment header */
|
|
|
|
head[0] &= ~LOWPAN_DISPATCH_FRAG1;
|
|
|
|
head[0] &= ~LOWPAN_DISPATCH_FRAG1;
|
|
|
@ -1033,10 +1102,17 @@ lowpan_skb_fragmentation(struct sk_buff *skb)
|
|
|
|
len = payload_length - offset;
|
|
|
|
len = payload_length - offset;
|
|
|
|
|
|
|
|
|
|
|
|
err = lowpan_fragment_xmit(skb, head, header_length,
|
|
|
|
err = lowpan_fragment_xmit(skb, head, header_length,
|
|
|
|
len, offset);
|
|
|
|
len, offset, LOWPAN_DISPATCH_FRAGN);
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
|
|
pr_debug("%s unable to send a subsequent FRAGN packet "
|
|
|
|
|
|
|
|
"(tag: %d, offset: %d", __func__, tag, offset);
|
|
|
|
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
offset += len;
|
|
|
|
offset += len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exit:
|
|
|
|
return err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -1059,7 +1135,7 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pr_debug("frame is too big, fragmentation is needed\n");
|
|
|
|
pr_debug("frame is too big, fragmentation is needed\n");
|
|
|
|
err = lowpan_skb_fragmentation(skb);
|
|
|
|
err = lowpan_skb_fragmentation(skb, dev);
|
|
|
|
error:
|
|
|
|
error:
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
out:
|
|
|
|
out:
|
|
|
@ -1087,6 +1163,12 @@ static u16 lowpan_get_short_addr(const struct net_device *dev)
|
|
|
|
return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev);
|
|
|
|
return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static u8 lowpan_get_dsn(const struct net_device *dev)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
|
|
|
|
|
|
|
|
return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct header_ops lowpan_header_ops = {
|
|
|
|
static struct header_ops lowpan_header_ops = {
|
|
|
|
.create = lowpan_header_create,
|
|
|
|
.create = lowpan_header_create,
|
|
|
|
};
|
|
|
|
};
|
|
|
@ -1100,6 +1182,7 @@ static struct ieee802154_mlme_ops lowpan_mlme = {
|
|
|
|
.get_pan_id = lowpan_get_pan_id,
|
|
|
|
.get_pan_id = lowpan_get_pan_id,
|
|
|
|
.get_phy = lowpan_get_phy,
|
|
|
|
.get_phy = lowpan_get_phy,
|
|
|
|
.get_short_addr = lowpan_get_short_addr,
|
|
|
|
.get_short_addr = lowpan_get_short_addr,
|
|
|
|
|
|
|
|
.get_dsn = lowpan_get_dsn,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void lowpan_setup(struct net_device *dev)
|
|
|
|
static void lowpan_setup(struct net_device *dev)
|
|
|
@ -1203,6 +1286,7 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
|
|
|
|
return -ENODEV;
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
|
|
lowpan_dev_info(dev)->real_dev = real_dev;
|
|
|
|
lowpan_dev_info(dev)->real_dev = real_dev;
|
|
|
|
|
|
|
|
lowpan_dev_info(dev)->fragment_tag = 0;
|
|
|
|
mutex_init(&lowpan_dev_info(dev)->dev_list_mtx);
|
|
|
|
mutex_init(&lowpan_dev_info(dev)->dev_list_mtx);
|
|
|
|
|
|
|
|
|
|
|
|
entry = kzalloc(sizeof(struct lowpan_dev_record), GFP_KERNEL);
|
|
|
|
entry = kzalloc(sizeof(struct lowpan_dev_record), GFP_KERNEL);
|
|
|
|