2019-05-27 08:55:21 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2015-01-04 17:10:56 +01:00
# include <net/6lowpan.h>
2016-06-15 21:20:27 +02:00
# include <net/ndisc.h>
2015-01-04 17:10:56 +01:00
# include <net/ieee802154_netdev.h>
2015-09-18 11:30:43 +02:00
# include <net/mac802154.h>
2015-01-04 17:10:56 +01:00
# include "6lowpan_i.h"
2015-10-20 08:31:22 +02:00
# define LOWPAN_FRAG1_HEAD_SIZE 0x4
# define LOWPAN_FRAGN_HEAD_SIZE 0x5
2015-01-04 17:10:56 +01:00
struct lowpan_addr_info {
2016-06-15 21:20:27 +02:00
struct ieee802154_addr daddr ;
struct ieee802154_addr saddr ;
2015-01-04 17:10:56 +01:00
} ;
static inline struct
lowpan_addr_info * lowpan_skb_priv ( const struct sk_buff * skb )
{
WARN_ON_ONCE ( skb_headroom ( skb ) < sizeof ( struct lowpan_addr_info ) ) ;
return ( struct lowpan_addr_info * ) ( skb - > data -
sizeof ( struct lowpan_addr_info ) ) ;
}
2015-09-18 11:30:43 +02:00
/* This callback will be called from AF_PACKET and IPv6 stack, the AF_PACKET
* sockets gives an 8 byte array for addresses only !
*
* TODO I think AF_PACKET DGRAM ( sending / receiving ) RAW ( sending ) makes no
* sense here . We should disable it , the right use - case would be AF_INET6
* RAW / DGRAM sockets .
*/
2015-09-02 14:21:16 +02:00
int lowpan_header_create ( struct sk_buff * skb , struct net_device * ldev ,
2016-06-15 21:20:27 +02:00
unsigned short type , const void * daddr ,
const void * saddr , unsigned int len )
2015-01-04 17:10:56 +01:00
{
2016-06-15 21:20:27 +02:00
struct wpan_dev * wpan_dev = lowpan_802154_dev ( ldev ) - > wdev - > ieee802154_ptr ;
struct lowpan_addr_info * info = lowpan_skb_priv ( skb ) ;
struct lowpan_802154_neigh * llneigh = NULL ;
const struct ipv6hdr * hdr = ipv6_hdr ( skb ) ;
struct neighbour * n ;
2015-01-04 17:10:56 +01:00
2018-12-23 12:52:18 -05:00
if ( ! daddr )
return - EINVAL ;
2015-01-04 17:10:56 +01:00
/* TODO:
* if this package isn ' t ipv6 one , where should it be routed ?
*/
if ( type ! = ETH_P_IPV6 )
return 0 ;
2016-06-15 21:20:27 +02:00
/* intra-pan communication */
info - > saddr . pan_id = wpan_dev - > pan_id ;
info - > daddr . pan_id = info - > saddr . pan_id ;
2015-01-04 17:10:56 +01:00
2016-06-15 21:20:27 +02:00
if ( ! memcmp ( daddr , ldev - > broadcast , EUI64_ADDR_LEN ) ) {
info - > daddr . short_addr = cpu_to_le16 ( IEEE802154_ADDR_BROADCAST ) ;
info - > daddr . mode = IEEE802154_ADDR_SHORT ;
} else {
__le16 short_addr = cpu_to_le16 ( IEEE802154_ADDR_SHORT_UNSPEC ) ;
n = neigh_lookup ( & nd_tbl , & hdr - > daddr , ldev ) ;
if ( n ) {
llneigh = lowpan_802154_neigh ( neighbour_priv ( n ) ) ;
read_lock_bh ( & n - > lock ) ;
short_addr = llneigh - > short_addr ;
read_unlock_bh ( & n - > lock ) ;
}
2015-01-04 17:10:56 +01:00
2016-06-15 21:20:27 +02:00
if ( llneigh & &
lowpan_802154_is_valid_src_short_addr ( short_addr ) ) {
info - > daddr . short_addr = short_addr ;
info - > daddr . mode = IEEE802154_ADDR_SHORT ;
} else {
info - > daddr . mode = IEEE802154_ADDR_LONG ;
ieee802154_be64_to_le64 ( & info - > daddr . extended_addr ,
daddr ) ;
}
2015-01-04 17:10:56 +01:00
2016-06-15 21:20:27 +02:00
if ( n )
neigh_release ( n ) ;
}
if ( ! saddr ) {
if ( lowpan_802154_is_valid_src_short_addr ( wpan_dev - > short_addr ) ) {
info - > saddr . mode = IEEE802154_ADDR_SHORT ;
info - > saddr . short_addr = wpan_dev - > short_addr ;
} else {
info - > saddr . mode = IEEE802154_ADDR_LONG ;
info - > saddr . extended_addr = wpan_dev - > extended_addr ;
}
} else {
info - > saddr . mode = IEEE802154_ADDR_LONG ;
ieee802154_be64_to_le64 ( & info - > saddr . extended_addr , saddr ) ;
}
2015-01-04 17:10:56 +01:00
return 0 ;
}
static struct sk_buff *
lowpan_alloc_frag ( struct sk_buff * skb , int size ,
2015-09-30 10:20:10 +02:00
const struct ieee802154_hdr * master_hdr , bool frag1 )
2015-01-04 17:10:56 +01:00
{
2016-04-11 11:04:18 +02:00
struct net_device * wdev = lowpan_802154_dev ( skb - > dev ) - > wdev ;
2015-01-04 17:10:56 +01:00
struct sk_buff * frag ;
int rc ;
2015-09-18 11:30:43 +02:00
frag = alloc_skb ( wdev - > needed_headroom + wdev - > needed_tailroom + size ,
2015-01-04 17:10:56 +01:00
GFP_ATOMIC ) ;
if ( likely ( frag ) ) {
2015-09-02 14:21:16 +02:00
frag - > dev = wdev ;
2015-01-04 17:10:56 +01:00
frag - > priority = skb - > priority ;
2015-09-18 11:30:43 +02:00
skb_reserve ( frag , wdev - > needed_headroom ) ;
2015-01-04 17:10:56 +01:00
skb_reset_network_header ( frag ) ;
* mac_cb ( frag ) = * mac_cb ( skb ) ;
2015-09-30 10:20:10 +02:00
if ( frag1 ) {
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 14:29:20 +02:00
skb_put_data ( frag , skb_mac_header ( skb ) , skb - > mac_len ) ;
2015-09-30 10:20:10 +02:00
} else {
rc = wpan_dev_hard_header ( frag , wdev ,
& master_hdr - > dest ,
& master_hdr - > source , size ) ;
if ( rc < 0 ) {
kfree_skb ( frag ) ;
return ERR_PTR ( rc ) ;
}
2015-01-04 17:10:56 +01:00
}
} else {
frag = ERR_PTR ( - ENOMEM ) ;
}
return frag ;
}
static int
lowpan_xmit_fragment ( struct sk_buff * skb , const struct ieee802154_hdr * wpan_hdr ,
u8 * frag_hdr , int frag_hdrlen ,
2015-09-30 10:20:10 +02:00
int offset , int len , bool frag1 )
2015-01-04 17:10:56 +01:00
{
struct sk_buff * frag ;
raw_dump_inline ( __func__ , " fragment header " , frag_hdr , frag_hdrlen ) ;
2015-09-30 10:20:10 +02:00
frag = lowpan_alloc_frag ( skb , frag_hdrlen + len , wpan_hdr , frag1 ) ;
2015-01-04 17:10:56 +01:00
if ( IS_ERR ( frag ) )
2015-08-10 21:15:59 +02:00
return PTR_ERR ( frag ) ;
2015-01-04 17:10:56 +01:00
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 14:29:20 +02:00
skb_put_data ( frag , frag_hdr , frag_hdrlen ) ;
skb_put_data ( frag , skb_network_header ( skb ) + offset , len ) ;
2015-01-04 17:10:56 +01:00
raw_dump_table ( __func__ , " fragment dump " , frag - > data , frag - > len ) ;
return dev_queue_xmit ( frag ) ;
}
static int
2015-09-02 14:21:16 +02:00
lowpan_xmit_fragmented ( struct sk_buff * skb , struct net_device * ldev ,
2015-10-01 08:03:06 +02:00
const struct ieee802154_hdr * wpan_hdr , u16 dgram_size ,
u16 dgram_offset )
2015-01-04 17:10:56 +01:00
{
__be16 frag_tag ;
u8 frag_hdr [ 5 ] ;
int frag_cap , frag_len , payload_cap , rc ;
int skb_unprocessed , skb_offset ;
2016-04-11 11:04:18 +02:00
frag_tag = htons ( lowpan_802154_dev ( ldev ) - > fragment_tag ) ;
lowpan_802154_dev ( ldev ) - > fragment_tag + + ;
2015-01-04 17:10:56 +01:00
frag_hdr [ 0 ] = LOWPAN_DISPATCH_FRAG1 | ( ( dgram_size > > 8 ) & 0x07 ) ;
frag_hdr [ 1 ] = dgram_size & 0xff ;
memcpy ( frag_hdr + 2 , & frag_tag , sizeof ( frag_tag ) ) ;
payload_cap = ieee802154_max_payload ( wpan_hdr ) ;
frag_len = round_down ( payload_cap - LOWPAN_FRAG1_HEAD_SIZE -
skb_network_header_len ( skb ) , 8 ) ;
skb_offset = skb_network_header_len ( skb ) ;
skb_unprocessed = skb - > len - skb - > mac_len - skb_offset ;
rc = lowpan_xmit_fragment ( skb , wpan_hdr , frag_hdr ,
LOWPAN_FRAG1_HEAD_SIZE , 0 ,
2015-09-30 10:20:10 +02:00
frag_len + skb_network_header_len ( skb ) ,
true ) ;
2015-01-04 17:10:56 +01:00
if ( rc ) {
pr_debug ( " %s unable to send FRAG1 packet (tag: %d) " ,
__func__ , ntohs ( frag_tag ) ) ;
goto err ;
}
frag_hdr [ 0 ] & = ~ LOWPAN_DISPATCH_FRAG1 ;
frag_hdr [ 0 ] | = LOWPAN_DISPATCH_FRAGN ;
frag_cap = round_down ( payload_cap - LOWPAN_FRAGN_HEAD_SIZE , 8 ) ;
do {
dgram_offset + = frag_len ;
skb_offset + = frag_len ;
skb_unprocessed - = frag_len ;
frag_len = min ( frag_cap , skb_unprocessed ) ;
frag_hdr [ 4 ] = dgram_offset > > 3 ;
rc = lowpan_xmit_fragment ( skb , wpan_hdr , frag_hdr ,
LOWPAN_FRAGN_HEAD_SIZE , skb_offset ,
2015-09-30 10:20:10 +02:00
frag_len , false ) ;
2015-01-04 17:10:56 +01:00
if ( rc ) {
pr_debug ( " %s unable to send a FRAGN packet. (tag: %d, offset: %d) \n " ,
__func__ , ntohs ( frag_tag ) , skb_offset ) ;
goto err ;
}
} while ( skb_unprocessed > frag_cap ) ;
2015-09-30 10:20:11 +02:00
ldev - > stats . tx_packets + + ;
ldev - > stats . tx_bytes + = dgram_size ;
2015-01-04 17:10:56 +01:00
consume_skb ( skb ) ;
return NET_XMIT_SUCCESS ;
err :
kfree_skb ( skb ) ;
return rc ;
}
2015-09-02 14:21:31 +02:00
static int lowpan_header ( struct sk_buff * skb , struct net_device * ldev ,
2015-10-01 08:03:06 +02:00
u16 * dgram_size , u16 * dgram_offset )
2015-01-04 17:10:56 +01:00
{
2016-04-11 11:04:18 +02:00
struct wpan_dev * wpan_dev = lowpan_802154_dev ( ldev ) - > wdev - > ieee802154_ptr ;
2015-01-04 17:10:56 +01:00
struct ieee802154_mac_cb * cb = mac_cb_init ( skb ) ;
struct lowpan_addr_info info ;
memcpy ( & info , lowpan_skb_priv ( skb ) , sizeof ( info ) ) ;
2015-09-02 14:21:31 +02:00
* dgram_size = skb - > len ;
2016-06-15 21:20:27 +02:00
lowpan_header_compress ( skb , ldev , & info . daddr , & info . saddr ) ;
2015-09-02 14:21:31 +02:00
/* dgram_offset = (saved bytes after compression) + lowpan header len */
* dgram_offset = ( * dgram_size - skb - > len ) + skb_network_header_len ( skb ) ;
2015-01-04 17:10:56 +01:00
cb - > type = IEEE802154_FC_TYPE_DATA ;
2016-06-15 21:20:27 +02:00
if ( info . daddr . mode = = IEEE802154_ADDR_SHORT & &
ieee802154_is_broadcast_short_addr ( info . daddr . short_addr ) )
2015-01-04 17:10:56 +01:00
cb - > ackreq = false ;
2016-06-15 21:20:27 +02:00
else
2015-08-10 21:15:58 +02:00
cb - > ackreq = wpan_dev - > ackreq ;
2015-01-04 17:10:56 +01:00
2016-06-15 21:20:27 +02:00
return wpan_dev_hard_header ( skb , lowpan_802154_dev ( ldev ) - > wdev ,
& info . daddr , & info . saddr , 0 ) ;
2015-01-04 17:10:56 +01:00
}
2015-09-02 14:21:16 +02:00
netdev_tx_t lowpan_xmit ( struct sk_buff * skb , struct net_device * ldev )
2015-01-04 17:10:56 +01:00
{
struct ieee802154_hdr wpan_hdr ;
int max_single , ret ;
2015-10-01 08:03:06 +02:00
u16 dgram_size , dgram_offset ;
2015-01-04 17:10:56 +01:00
pr_debug ( " package xmit \n " ) ;
2015-10-01 08:03:06 +02:00
WARN_ON_ONCE ( skb - > len > IPV6_MIN_MTU ) ;
2015-01-04 17:10:56 +01:00
/* We must take a copy of the skb before we modify/replace the ipv6
* header as the header could be used elsewhere
*/
2018-07-14 12:52:10 -04:00
if ( unlikely ( skb_headroom ( skb ) < ldev - > needed_headroom | |
skb_tailroom ( skb ) < ldev - > needed_tailroom ) ) {
struct sk_buff * nskb ;
nskb = skb_copy_expand ( skb , ldev - > needed_headroom ,
ldev - > needed_tailroom , GFP_ATOMIC ) ;
if ( likely ( nskb ) ) {
consume_skb ( skb ) ;
skb = nskb ;
} else {
kfree_skb ( skb ) ;
return NET_XMIT_DROP ;
}
} else {
skb = skb_unshare ( skb , GFP_ATOMIC ) ;
if ( ! skb )
return NET_XMIT_DROP ;
}
2015-01-04 17:10:56 +01:00
2015-09-02 14:21:31 +02:00
ret = lowpan_header ( skb , ldev , & dgram_size , & dgram_offset ) ;
2015-01-04 17:10:56 +01:00
if ( ret < 0 ) {
kfree_skb ( skb ) ;
return NET_XMIT_DROP ;
}
if ( ieee802154_hdr_peek ( skb , & wpan_hdr ) < 0 ) {
kfree_skb ( skb ) ;
return NET_XMIT_DROP ;
}
max_single = ieee802154_max_payload ( & wpan_hdr ) ;
if ( skb_tail_pointer ( skb ) - skb_network_header ( skb ) < = max_single ) {
2016-04-11 11:04:18 +02:00
skb - > dev = lowpan_802154_dev ( ldev ) - > wdev ;
2015-09-30 10:20:11 +02:00
ldev - > stats . tx_packets + + ;
ldev - > stats . tx_bytes + = dgram_size ;
2015-01-04 17:10:56 +01:00
return dev_queue_xmit ( skb ) ;
} else {
netdev_tx_t rc ;
pr_debug ( " frame is too big, fragmentation is needed \n " ) ;
2015-09-02 14:21:31 +02:00
rc = lowpan_xmit_fragmented ( skb , ldev , & wpan_hdr , dgram_size ,
dgram_offset ) ;
2015-01-04 17:10:56 +01:00
return rc < 0 ? NET_XMIT_DROP : rc ;
}
}