Commit 750f679c authored by David S. Miller's avatar David S. Miller

Merge branch '6lowpan'

Alexander Aring says:

====================
6lowpan: reimplementation of fragmentation handling

this patch series reimplementation the fragmentation handling of 6lowpan
accroding to rfc4944 [1].

The first big note is, that the current fragmentation behaviour isn't rfc
complaint. The main issue is a wrong datagram_size value which needs to be:
datagram_size = ipv6_payload + ipv6 header + (maybe compressed transport header,
                currently only udp is supported)

but the current datagram_size value is calculated as:
datagram_size = ipv6_payload

Fragmentation work in a linux<->linux communication only.

Why reimplementation?

I reimplemted the reassembly side only. The current behaviour is to allocate a
skb with the reassembled size and hold all fragments in a list, protected by a
spinlock. After we received all fragments (detected by the sum of all fragments,
it begins to place all fragments into the allocated skb).

This reassembly implementation has some race condition. Additional I make it more
rfc complaint. The current implementation match on the tag value inside the frag
header only, but rfc4944 says we need to match on dst addr(mac), src addr(mac),
tag value, datagram_size value. [2]

The new reassembly handling use the inet_frag api (I mean the callback interface
of ipv6 and ipv4 reassembly). I looked into ipv6 and wanted to see how ipv6 is
dealing with reassembly, so I based my code on this implementation.

On the sending side to generate the fragments I improved the current code to use
the nearest 8 divided payload. (We can do that, because the mac layer has a
dynamic size, so it depends on mac_header how big we can do the payload).

Of course I fix also the reassembly/sending side to be rfc complaint now.

Regards
Alexander Aring

[1] http://tools.ietf.org/html/rfc4944
[2] http://tools.ietf.org/html/rfc4944#section-5.3

changes since v2:
 - rework checkpatch code style issue patch.
   Merge two pr_debugs into one pr_debug.

changes since v3:
 - rename 6lowpan.ko to 6lowpan_rtnl.c in commit msg of patch 5/8.

changes since v4:
 - Add a new patch 2/8 to introduce lowpan_uncompress_size function. Also
   improving this function a little bit.
 - Add a new patch 4/8 to change tag value to __be16.
 - use skb_header_reset function on FRAG1 only, which should have the
   lowpan header. See lowpan_get_frag_info function. (slightly improving
   of fragmentation header parsing).
 - changes types of variables to u16 in lowpan_skb_fragmentation.
 - use lowpan_uncompress_size instead of storing necessary information
   in skb control block, this can be destroyed after dev_queue_xmit call.
   Thanks David for this hint.
 - remove Tested-by: Martin Townsend <martin.townsend@xsilon.com>, because
   too many funcionality change.

changes since v5:
 - handle lowpan_addr_mode_size with lookup table.

changes since v6:
 - remove unnecessary parameter in lowpan_frag_queue.
 - fix commit message in patch 8/8 which included a describtion of adding the
   lownpan_uncompress_size function. This was splitted in a seperate patch.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4e76ca7f 7240cdec
......@@ -29,6 +29,12 @@
#include <net/af_ieee802154.h>
struct ieee802154_frag_info {
__be16 d_tag;
u16 d_size;
u8 d_offset;
};
/*
* A control block of skb passed between the ARPHRD_IEEE802154 device
* and other stack parts.
......@@ -39,6 +45,7 @@ struct ieee802154_mac_cb {
struct ieee802154_addr da;
u8 flags;
u8 seq;
struct ieee802154_frag_info frag_info;
};
static inline struct ieee802154_mac_cb *mac_cb(struct sk_buff *skb)
......
......@@ -15,6 +15,7 @@
#include <net/netns/packet.h>
#include <net/netns/ipv4.h>
#include <net/netns/ipv6.h>
#include <net/netns/ieee802154_6lowpan.h>
#include <net/netns/sctp.h>
#include <net/netns/dccp.h>
#include <net/netns/netfilter.h>
......@@ -90,6 +91,9 @@ struct net {
#if IS_ENABLED(CONFIG_IPV6)
struct netns_ipv6 ipv6;
#endif
#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
struct netns_ieee802154_lowpan ieee802154_lowpan;
#endif
#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE)
struct netns_sctp sctp;
#endif
......
/*
* ieee802154 6lowpan in net namespaces
*/
#include <net/inet_frag.h>
#ifndef __NETNS_IEEE802154_6LOWPAN_H__
#define __NETNS_IEEE802154_6LOWPAN_H__
struct netns_sysctl_lowpan {
#ifdef CONFIG_SYSCTL
struct ctl_table_header *frags_hdr;
#endif
};
struct netns_ieee802154_lowpan {
struct netns_sysctl_lowpan sysctl;
struct netns_frags frags;
u16 max_dsize;
};
#endif
......@@ -306,6 +306,119 @@ static inline void lowpan_push_hc_data(u8 **hc_ptr, const void *data,
*hc_ptr += len;
}
static inline u8 lowpan_addr_mode_size(const u8 addr_mode)
{
static const u8 addr_sizes[] = {
[LOWPAN_IPHC_ADDR_00] = 16,
[LOWPAN_IPHC_ADDR_01] = 8,
[LOWPAN_IPHC_ADDR_02] = 2,
[LOWPAN_IPHC_ADDR_03] = 0,
};
return addr_sizes[addr_mode];
}
static inline u8 lowpan_next_hdr_size(const u8 h_enc, u16 *uncomp_header)
{
u8 ret = 1;
if ((h_enc & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
*uncomp_header += sizeof(struct udphdr);
switch (h_enc & LOWPAN_NHC_UDP_CS_P_11) {
case LOWPAN_NHC_UDP_CS_P_00:
ret += 4;
break;
case LOWPAN_NHC_UDP_CS_P_01:
case LOWPAN_NHC_UDP_CS_P_10:
ret += 3;
break;
case LOWPAN_NHC_UDP_CS_P_11:
ret++;
break;
default:
break;
}
if (!(h_enc & LOWPAN_NHC_UDP_CS_C))
ret += 2;
}
return ret;
}
/**
* lowpan_uncompress_size - returns skb->len size with uncompressed header
* @skb: sk_buff with 6lowpan header inside
* @datagram_offset: optional to get the datagram_offset value
*
* Returns the skb->len with uncompressed header
*/
static inline u16
lowpan_uncompress_size(const struct sk_buff *skb, u16 *dgram_offset)
{
u16 ret = 2, uncomp_header = sizeof(struct ipv6hdr);
u8 iphc0, iphc1, h_enc;
iphc0 = skb_network_header(skb)[0];
iphc1 = skb_network_header(skb)[1];
switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
case 0:
ret += 4;
break;
case 1:
ret += 3;
break;
case 2:
ret++;
break;
default:
break;
}
if (!(iphc0 & LOWPAN_IPHC_NH_C))
ret++;
if (!(iphc0 & 0x03))
ret++;
ret += lowpan_addr_mode_size((iphc1 & LOWPAN_IPHC_SAM) >>
LOWPAN_IPHC_SAM_BIT);
if (iphc1 & LOWPAN_IPHC_M) {
switch ((iphc1 & LOWPAN_IPHC_DAM_11) >>
LOWPAN_IPHC_DAM_BIT) {
case LOWPAN_IPHC_DAM_00:
ret += 16;
break;
case LOWPAN_IPHC_DAM_01:
ret += 6;
break;
case LOWPAN_IPHC_DAM_10:
ret += 4;
break;
case LOWPAN_IPHC_DAM_11:
ret++;
break;
default:
break;
}
} else {
ret += lowpan_addr_mode_size((iphc1 & LOWPAN_IPHC_DAM_11) >>
LOWPAN_IPHC_DAM_BIT);
}
if (iphc0 & LOWPAN_IPHC_NH_C) {
h_enc = skb_network_header(skb)[ret];
ret += lowpan_next_hdr_size(h_enc, &uncomp_header);
}
if (dgram_offset)
*dgram_offset = uncomp_header;
return skb->len + uncomp_header - ret;
}
typedef int (*skb_delivery_cb)(struct sk_buff *skb, struct net_device *dev);
int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
......
......@@ -2,5 +2,6 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o
obj-$(CONFIG_6LOWPAN_IPHC) += 6lowpan_iphc.o
6lowpan-y := 6lowpan_rtnl.o reassembly.o
ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o
af_802154-y := af_ieee802154.o raw.o dgram.o
This diff is collapsed.
#ifndef __IEEE802154_6LOWPAN_REASSEMBLY_H__
#define __IEEE802154_6LOWPAN_REASSEMBLY_H__
#include <net/inet_frag.h>
struct lowpan_create_arg {
__be16 tag;
u16 d_size;
const struct ieee802154_addr *src;
const struct ieee802154_addr *dst;
};
/* Equivalent of ipv4 struct ip
*/
struct lowpan_frag_queue {
struct inet_frag_queue q;
__be16 tag;
u16 d_size;
struct ieee802154_addr saddr;
struct ieee802154_addr daddr;
};
static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a)
{
switch (a->addr_type) {
case IEEE802154_ADDR_LONG:
return (__force u32)((((u32 *)a->hwaddr))[0] ^
((u32 *)(a->hwaddr))[1]);
case IEEE802154_ADDR_SHORT:
return (__force u32)(a->short_addr);
default:
return 0;
}
}
static inline bool ieee802154_addr_addr_equal(const struct ieee802154_addr *a1,
const struct ieee802154_addr *a2)
{
if (a1->pan_id != a2->pan_id)
return false;
if (a1->addr_type != a2->addr_type)
return false;
switch (a1->addr_type) {
case IEEE802154_ADDR_LONG:
if (memcmp(a1->hwaddr, a2->hwaddr, IEEE802154_ADDR_LEN))
return false;
break;
case IEEE802154_ADDR_SHORT:
if (a1->short_addr != a2->short_addr)
return false;
break;
default:
return false;
}
return true;
}
int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type);
void lowpan_net_frag_exit(void);
int lowpan_net_frag_init(void);
#endif /* __IEEE802154_6LOWPAN_REASSEMBLY_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment