2012-06-25 23:24:48 +00:00
/*
* Copyright 2007 - 2012 Siemens AG
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* Written by :
* Dmitry Eremin - Solenikov < dbaryshkov @ gmail . com >
* Sergey Lapin < slapin @ ossfans . org >
* Maxim Gorbachyov < maxim . gorbachev @ siemens . com >
* Alexander Smirnov < alex . bluesman . smirnov @ gmail . com >
*/
# include <linux/netdevice.h>
# include <linux/module.h>
# include <linux/if_arp.h>
# include <net/rtnetlink.h>
# include <linux/nl802154.h>
# include <net/af_ieee802154.h>
# include <net/mac802154.h>
# include <net/ieee802154_netdev.h>
# include <net/ieee802154.h>
# include <net/wpan-phy.h>
# include "mac802154.h"
static int
mac802154_wpan_ioctl ( struct net_device * dev , struct ifreq * ifr , int cmd )
{
struct mac802154_sub_if_data * priv = netdev_priv ( dev ) ;
struct sockaddr_ieee802154 * sa =
( struct sockaddr_ieee802154 * ) & ifr - > ifr_addr ;
int err = - ENOIOCTLCMD ;
spin_lock_bh ( & priv - > mib_lock ) ;
switch ( cmd ) {
case SIOCGIFADDR :
2014-03-14 21:23:59 +01:00
{
u16 pan_id , short_addr ;
pan_id = le16_to_cpu ( priv - > pan_id ) ;
short_addr = le16_to_cpu ( priv - > short_addr ) ;
if ( pan_id = = IEEE802154_PANID_BROADCAST | |
short_addr = = IEEE802154_ADDR_BROADCAST ) {
2012-06-25 23:24:48 +00:00
err = - EADDRNOTAVAIL ;
break ;
}
sa - > family = AF_IEEE802154 ;
sa - > addr . addr_type = IEEE802154_ADDR_SHORT ;
2014-03-14 21:23:59 +01:00
sa - > addr . pan_id = pan_id ;
sa - > addr . short_addr = short_addr ;
2012-06-25 23:24:48 +00:00
err = 0 ;
break ;
2014-03-14 21:23:59 +01:00
}
2012-06-25 23:24:48 +00:00
case SIOCSIFADDR :
dev_warn ( & dev - > dev ,
" Using DEBUGing ioctl SIOCSIFADDR isn't recommened! \n " ) ;
if ( sa - > family ! = AF_IEEE802154 | |
sa - > addr . addr_type ! = IEEE802154_ADDR_SHORT | |
sa - > addr . pan_id = = IEEE802154_PANID_BROADCAST | |
sa - > addr . short_addr = = IEEE802154_ADDR_BROADCAST | |
sa - > addr . short_addr = = IEEE802154_ADDR_UNDEF ) {
err = - EINVAL ;
break ;
}
2014-03-14 21:23:59 +01:00
priv - > pan_id = cpu_to_le16 ( sa - > addr . pan_id ) ;
priv - > short_addr = cpu_to_le16 ( sa - > addr . short_addr ) ;
2012-06-25 23:24:48 +00:00
err = 0 ;
break ;
}
spin_unlock_bh ( & priv - > mib_lock ) ;
return err ;
}
static int mac802154_wpan_mac_addr ( struct net_device * dev , void * p )
{
struct sockaddr * addr = p ;
if ( netif_running ( dev ) )
return - EBUSY ;
/* FIXME: validate addr */
memcpy ( dev - > dev_addr , addr - > sa_data , dev - > addr_len ) ;
mac802154_dev_set_ieee_addr ( dev ) ;
return 0 ;
}
static int mac802154_header_create ( struct sk_buff * skb ,
struct net_device * dev ,
unsigned short type ,
2014-03-14 21:24:01 +01:00
const void * daddr ,
const void * saddr ,
2012-06-25 23:24:48 +00:00
unsigned len )
{
2014-03-14 21:24:01 +01:00
struct ieee802154_hdr hdr ;
2012-06-25 23:24:48 +00:00
struct mac802154_sub_if_data * priv = netdev_priv ( dev ) ;
2014-03-14 21:24:01 +01:00
int hlen ;
2012-06-25 23:24:48 +00:00
if ( ! daddr )
return - EINVAL ;
2014-03-14 21:24:01 +01:00
memset ( & hdr . fc , 0 , sizeof ( hdr . fc ) ) ;
hdr . fc . type = mac_cb_type ( skb ) ;
hdr . fc . security_enabled = mac_cb_is_secen ( skb ) ;
hdr . fc . ack_request = mac_cb_is_ackreq ( skb ) ;
2012-06-25 23:24:48 +00:00
if ( ! saddr ) {
spin_lock_bh ( & priv - > mib_lock ) ;
2014-03-14 21:23:59 +01:00
if ( priv - > short_addr = = cpu_to_le16 ( IEEE802154_ADDR_BROADCAST ) | |
priv - > short_addr = = cpu_to_le16 ( IEEE802154_ADDR_UNDEF ) | |
priv - > pan_id = = cpu_to_le16 ( IEEE802154_PANID_BROADCAST ) ) {
2014-03-14 21:24:01 +01:00
hdr . source . mode = IEEE802154_ADDR_LONG ;
hdr . source . extended_addr = priv - > extended_addr ;
2012-06-25 23:24:48 +00:00
} else {
2014-03-14 21:24:01 +01:00
hdr . source . mode = IEEE802154_ADDR_SHORT ;
hdr . source . short_addr = priv - > short_addr ;
2012-06-25 23:24:48 +00:00
}
2014-03-14 21:24:01 +01:00
hdr . source . pan_id = priv - > pan_id ;
2012-06-25 23:24:48 +00:00
spin_unlock_bh ( & priv - > mib_lock ) ;
2014-03-14 21:24:01 +01:00
} else {
hdr . source = * ( const struct ieee802154_addr * ) saddr ;
2012-06-25 23:24:48 +00:00
}
2014-03-14 21:24:01 +01:00
hdr . dest = * ( const struct ieee802154_addr * ) daddr ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
hlen = ieee802154_hdr_push ( skb , & hdr ) ;
if ( hlen < 0 )
return - EINVAL ;
2012-06-25 23:24:48 +00:00
2013-10-30 09:18:22 +01:00
skb_reset_mac_header ( skb ) ;
2014-03-14 21:24:01 +01:00
skb - > mac_len = hlen ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
return hlen ;
2012-06-25 23:24:48 +00:00
}
static int
mac802154_header_parse ( const struct sk_buff * skb , unsigned char * haddr )
{
2014-03-14 21:24:01 +01:00
struct ieee802154_hdr hdr ;
struct ieee802154_addr * addr = ( struct ieee802154_addr * ) haddr ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
if ( ieee802154_hdr_peek_addrs ( skb , & hdr ) < 0 ) {
pr_debug ( " malformed packet \n " ) ;
return 0 ;
2012-06-25 23:24:48 +00:00
}
2014-03-14 21:24:01 +01:00
* addr = hdr . source ;
return sizeof ( * addr ) ;
2012-06-25 23:24:48 +00:00
}
static netdev_tx_t
mac802154_wpan_xmit ( struct sk_buff * skb , struct net_device * dev )
{
struct mac802154_sub_if_data * priv ;
u8 chan , page ;
priv = netdev_priv ( dev ) ;
spin_lock_bh ( & priv - > mib_lock ) ;
chan = priv - > chan ;
page = priv - > page ;
spin_unlock_bh ( & priv - > mib_lock ) ;
if ( chan = = MAC802154_CHAN_NONE | |
page > = WPAN_NUM_PAGES | |
2012-11-29 18:25:10 +00:00
chan > = WPAN_NUM_CHANNELS ) {
kfree_skb ( skb ) ;
2012-06-25 23:24:48 +00:00
return NETDEV_TX_OK ;
2012-11-29 18:25:10 +00:00
}
2012-06-25 23:24:48 +00:00
skb - > skb_iif = dev - > ifindex ;
dev - > stats . tx_packets + + ;
dev - > stats . tx_bytes + = skb - > len ;
return mac802154_tx ( priv - > hw , skb , page , chan ) ;
}
static struct header_ops mac802154_header_ops = {
. create = mac802154_header_create ,
. parse = mac802154_header_parse ,
} ;
static const struct net_device_ops mac802154_wpan_ops = {
. ndo_open = mac802154_slave_open ,
. ndo_stop = mac802154_slave_close ,
. ndo_start_xmit = mac802154_wpan_xmit ,
. ndo_do_ioctl = mac802154_wpan_ioctl ,
. ndo_set_mac_address = mac802154_wpan_mac_addr ,
} ;
void mac802154_wpan_setup ( struct net_device * dev )
{
struct mac802154_sub_if_data * priv ;
dev - > addr_len = IEEE802154_ADDR_LEN ;
memset ( dev - > broadcast , 0xff , IEEE802154_ADDR_LEN ) ;
dev - > hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN ;
dev - > header_ops = & mac802154_header_ops ;
dev - > needed_tailroom = 2 ; /* FCS */
dev - > mtu = IEEE802154_MTU ;
2013-04-03 04:00:57 +00:00
dev - > tx_queue_len = 300 ;
2012-06-25 23:24:48 +00:00
dev - > type = ARPHRD_IEEE802154 ;
dev - > flags = IFF_NOARP | IFF_BROADCAST ;
dev - > watchdog_timeo = 0 ;
dev - > destructor = free_netdev ;
dev - > netdev_ops = & mac802154_wpan_ops ;
dev - > ml_priv = & mac802154_mlme_wpan ;
priv = netdev_priv ( dev ) ;
priv - > type = IEEE802154_DEV_WPAN ;
priv - > chan = MAC802154_CHAN_NONE ;
priv - > page = 0 ;
spin_lock_init ( & priv - > mib_lock ) ;
get_random_bytes ( & priv - > bsn , 1 ) ;
get_random_bytes ( & priv - > dsn , 1 ) ;
2014-03-14 21:23:59 +01:00
priv - > pan_id = cpu_to_le16 ( IEEE802154_PANID_BROADCAST ) ;
priv - > short_addr = cpu_to_le16 ( IEEE802154_ADDR_BROADCAST ) ;
2012-06-25 23:24:48 +00:00
}
static int mac802154_process_data ( struct net_device * dev , struct sk_buff * skb )
{
2013-01-02 01:01:10 +00:00
return netif_rx_ni ( skb ) ;
2012-06-25 23:24:48 +00:00
}
static int
mac802154_subif_frame ( struct mac802154_sub_if_data * sdata , struct sk_buff * skb )
{
2014-03-14 21:23:59 +01:00
u16 span , sshort ;
2012-06-25 23:24:48 +00:00
pr_debug ( " getting packet via slave interface %s \n " , sdata - > dev - > name ) ;
spin_lock_bh ( & sdata - > mib_lock ) ;
2014-03-14 21:23:59 +01:00
span = le16_to_cpu ( sdata - > pan_id ) ;
sshort = le16_to_cpu ( sdata - > short_addr ) ;
2012-06-25 23:24:48 +00:00
switch ( mac_cb ( skb ) - > da . addr_type ) {
case IEEE802154_ADDR_NONE :
if ( mac_cb ( skb ) - > sa . addr_type ! = IEEE802154_ADDR_NONE )
/* FIXME: check if we are PAN coordinator */
skb - > pkt_type = PACKET_OTHERHOST ;
else
/* ACK comes with both addresses empty */
skb - > pkt_type = PACKET_HOST ;
break ;
case IEEE802154_ADDR_LONG :
2014-03-14 21:23:59 +01:00
if ( mac_cb ( skb ) - > da . pan_id ! = span & &
2012-06-25 23:24:48 +00:00
mac_cb ( skb ) - > da . pan_id ! = IEEE802154_PANID_BROADCAST )
skb - > pkt_type = PACKET_OTHERHOST ;
else if ( ! memcmp ( mac_cb ( skb ) - > da . hwaddr , sdata - > dev - > dev_addr ,
IEEE802154_ADDR_LEN ) )
skb - > pkt_type = PACKET_HOST ;
else
skb - > pkt_type = PACKET_OTHERHOST ;
break ;
case IEEE802154_ADDR_SHORT :
2014-03-14 21:23:59 +01:00
if ( mac_cb ( skb ) - > da . pan_id ! = span & &
2012-06-25 23:24:48 +00:00
mac_cb ( skb ) - > da . pan_id ! = IEEE802154_PANID_BROADCAST )
skb - > pkt_type = PACKET_OTHERHOST ;
2014-03-14 21:23:59 +01:00
else if ( mac_cb ( skb ) - > da . short_addr = = sshort )
2012-06-25 23:24:48 +00:00
skb - > pkt_type = PACKET_HOST ;
else if ( mac_cb ( skb ) - > da . short_addr = =
IEEE802154_ADDR_BROADCAST )
skb - > pkt_type = PACKET_BROADCAST ;
else
skb - > pkt_type = PACKET_OTHERHOST ;
break ;
default :
break ;
}
spin_unlock_bh ( & sdata - > mib_lock ) ;
skb - > dev = sdata - > dev ;
sdata - > dev - > stats . rx_packets + + ;
sdata - > dev - > stats . rx_bytes + = skb - > len ;
switch ( mac_cb_type ( skb ) ) {
case IEEE802154_FC_TYPE_DATA :
return mac802154_process_data ( sdata - > dev , skb ) ;
default :
2013-12-19 09:22:26 +08:00
pr_warn ( " ieee802154: bad frame received (type = %d) \n " ,
mac_cb_type ( skb ) ) ;
2012-06-25 23:24:48 +00:00
kfree_skb ( skb ) ;
return NET_RX_DROP ;
}
}
2014-03-14 21:24:01 +01:00
static void mac802154_print_addr ( const char * name ,
const struct ieee802154_addr * addr )
2012-06-25 23:24:48 +00:00
{
2014-03-14 21:24:01 +01:00
if ( addr - > mode = = IEEE802154_ADDR_NONE )
pr_debug ( " %s not present \n " , name ) ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
pr_debug ( " %s PAN ID: %04x \n " , name , le16_to_cpu ( addr - > pan_id ) ) ;
if ( addr - > mode = = IEEE802154_ADDR_SHORT ) {
pr_debug ( " %s is short: %04x \n " , name ,
le16_to_cpu ( addr - > short_addr ) ) ;
} else {
u64 hw = swab64 ( ( __force u64 ) addr - > extended_addr ) ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
pr_debug ( " %s is hardware: %8phC \n " , name , & hw ) ;
}
}
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
static int mac802154_parse_frame_start ( struct sk_buff * skb )
{
struct ieee802154_hdr hdr ;
int hlen ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
hlen = ieee802154_hdr_pull ( skb , & hdr ) ;
if ( hlen < 0 )
return - EINVAL ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
skb - > mac_len = hlen ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
pr_debug ( " fc: %04x dsn: %02x \n " , le16_to_cpup ( ( __le16 * ) & hdr . fc ) ,
hdr . seq ) ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
mac_cb ( skb ) - > flags = hdr . fc . type ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
ieee802154_addr_to_sa ( & mac_cb ( skb ) - > sa , & hdr . source ) ;
ieee802154_addr_to_sa ( & mac_cb ( skb ) - > da , & hdr . dest ) ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
if ( hdr . fc . ack_request )
mac_cb ( skb ) - > flags | = MAC_CB_FLAG_ACKREQ ;
if ( hdr . fc . security_enabled )
mac_cb ( skb ) - > flags | = MAC_CB_FLAG_SECEN ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
mac802154_print_addr ( " destination " , & hdr . dest ) ;
mac802154_print_addr ( " source " , & hdr . source ) ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
if ( hdr . fc . security_enabled ) {
u64 key ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
pr_debug ( " seclevel %i \n " , hdr . sec . level ) ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
switch ( hdr . sec . key_id_mode ) {
case IEEE802154_SCF_KEY_IMPLICIT :
pr_debug ( " implicit key \n " ) ;
break ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
case IEEE802154_SCF_KEY_INDEX :
pr_debug ( " key %02x \n " , hdr . sec . key_id ) ;
break ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
case IEEE802154_SCF_KEY_SHORT_INDEX :
pr_debug ( " key %04x:%04x %02x \n " ,
le32_to_cpu ( hdr . sec . short_src ) > > 16 ,
le32_to_cpu ( hdr . sec . short_src ) & 0xffff ,
hdr . sec . key_id ) ;
break ;
2012-06-25 23:24:48 +00:00
2014-03-14 21:24:01 +01:00
case IEEE802154_SCF_KEY_HW_INDEX :
key = swab64 ( ( __force u64 ) hdr . sec . extended_src ) ;
pr_debug ( " key source %8phC %02x \n " , & key ,
hdr . sec . key_id ) ;
break ;
2012-06-25 23:24:48 +00:00
}
2014-03-14 21:24:01 +01:00
return - EINVAL ;
2012-06-25 23:24:48 +00:00
}
return 0 ;
}
void mac802154_wpans_rx ( struct mac802154_priv * priv , struct sk_buff * skb )
{
int ret ;
struct sk_buff * sskb ;
struct mac802154_sub_if_data * sdata ;
ret = mac802154_parse_frame_start ( skb ) ;
if ( ret ) {
pr_debug ( " got invalid frame \n " ) ;
return ;
}
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( sdata , & priv - > slaves , list ) {
if ( sdata - > type ! = IEEE802154_DEV_WPAN )
continue ;
sskb = skb_clone ( skb , GFP_ATOMIC ) ;
if ( sskb )
mac802154_subif_frame ( sdata , sskb ) ;
}
rcu_read_unlock ( ) ;
}