2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
2016-06-15 22:20:25 +03:00
*
* Authors :
* ( C ) 2016 Pengutronix , Alexander Aring < aar @ pengutronix . de >
*/
# include <net/6lowpan.h>
# include <net/addrconf.h>
# include <net/ndisc.h>
# include "6lowpan_i.h"
static int lowpan_ndisc_is_useropt ( u8 nd_opt_type )
{
return nd_opt_type = = ND_OPT_6CO ;
}
# if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
# define NDISC_802154_SHORT_ADDR_LENGTH 1
static int lowpan_ndisc_parse_802154_options ( const struct net_device * dev ,
struct nd_opt_hdr * nd_opt ,
struct ndisc_options * ndopts )
{
switch ( nd_opt - > nd_opt_len ) {
case NDISC_802154_SHORT_ADDR_LENGTH :
if ( ndopts - > nd_802154_opt_array [ nd_opt - > nd_opt_type ] )
ND_PRINTK ( 2 , warn ,
" %s: duplicated short addr ND6 option found: type=%d \n " ,
__func__ , nd_opt - > nd_opt_type ) ;
else
ndopts - > nd_802154_opt_array [ nd_opt - > nd_opt_type ] = nd_opt ;
return 1 ;
default :
/* all others will be handled by ndisc IPv6 option parsing */
return 0 ;
}
}
static int lowpan_ndisc_parse_options ( const struct net_device * dev ,
struct nd_opt_hdr * nd_opt ,
struct ndisc_options * ndopts )
{
2016-06-21 11:27:19 +03:00
if ( ! lowpan_is_ll ( dev , LOWPAN_LLTYPE_IEEE802154 ) )
return 0 ;
2016-06-15 22:20:25 +03:00
switch ( nd_opt - > nd_opt_type ) {
case ND_OPT_SOURCE_LL_ADDR :
case ND_OPT_TARGET_LL_ADDR :
return lowpan_ndisc_parse_802154_options ( dev , nd_opt , ndopts ) ;
default :
return 0 ;
}
}
static void lowpan_ndisc_802154_update ( struct neighbour * n , u32 flags ,
u8 icmp6_type ,
const struct ndisc_options * ndopts )
{
struct lowpan_802154_neigh * neigh = lowpan_802154_neigh ( neighbour_priv ( n ) ) ;
u8 * lladdr_short = NULL ;
switch ( icmp6_type ) {
case NDISC_ROUTER_SOLICITATION :
case NDISC_ROUTER_ADVERTISEMENT :
case NDISC_NEIGHBOUR_SOLICITATION :
if ( ndopts - > nd_802154_opts_src_lladdr ) {
lladdr_short = __ndisc_opt_addr_data ( ndopts - > nd_802154_opts_src_lladdr ,
IEEE802154_SHORT_ADDR_LEN , 0 ) ;
if ( ! lladdr_short ) {
ND_PRINTK ( 2 , warn ,
" NA: invalid short link-layer address length \n " ) ;
return ;
}
}
break ;
case NDISC_REDIRECT :
case NDISC_NEIGHBOUR_ADVERTISEMENT :
if ( ndopts - > nd_802154_opts_tgt_lladdr ) {
lladdr_short = __ndisc_opt_addr_data ( ndopts - > nd_802154_opts_tgt_lladdr ,
IEEE802154_SHORT_ADDR_LEN , 0 ) ;
if ( ! lladdr_short ) {
ND_PRINTK ( 2 , warn ,
" NA: invalid short link-layer address length \n " ) ;
return ;
}
}
break ;
default :
break ;
}
write_lock_bh ( & n - > lock ) ;
2016-07-07 00:32:28 +03:00
if ( lladdr_short ) {
2016-06-15 22:20:25 +03:00
ieee802154_be16_to_le16 ( & neigh - > short_addr , lladdr_short ) ;
2016-07-07 00:32:28 +03:00
if ( ! lowpan_802154_is_valid_src_short_addr ( neigh - > short_addr ) )
neigh - > short_addr = cpu_to_le16 ( IEEE802154_ADDR_SHORT_UNSPEC ) ;
}
2016-06-15 22:20:25 +03:00
write_unlock_bh ( & n - > lock ) ;
}
static void lowpan_ndisc_update ( const struct net_device * dev ,
struct neighbour * n , u32 flags , u8 icmp6_type ,
const struct ndisc_options * ndopts )
{
if ( ! lowpan_is_ll ( dev , LOWPAN_LLTYPE_IEEE802154 ) )
return ;
/* react on overrides only. TODO check if this is really right. */
if ( flags & NEIGH_UPDATE_F_OVERRIDE )
lowpan_ndisc_802154_update ( n , flags , icmp6_type , ndopts ) ;
}
static int lowpan_ndisc_opt_addr_space ( const struct net_device * dev ,
u8 icmp6_type , struct neighbour * neigh ,
u8 * ha_buf , u8 * * ha )
{
struct lowpan_802154_neigh * n ;
struct wpan_dev * wpan_dev ;
int addr_space = 0 ;
if ( ! lowpan_is_ll ( dev , LOWPAN_LLTYPE_IEEE802154 ) )
return 0 ;
switch ( icmp6_type ) {
case NDISC_REDIRECT :
n = lowpan_802154_neigh ( neighbour_priv ( neigh ) ) ;
read_lock_bh ( & neigh - > lock ) ;
if ( lowpan_802154_is_valid_src_short_addr ( n - > short_addr ) ) {
memcpy ( ha_buf , & n - > short_addr ,
IEEE802154_SHORT_ADDR_LEN ) ;
read_unlock_bh ( & neigh - > lock ) ;
addr_space + = __ndisc_opt_addr_space ( IEEE802154_SHORT_ADDR_LEN , 0 ) ;
* ha = ha_buf ;
2016-06-21 11:27:18 +03:00
} else {
read_unlock_bh ( & neigh - > lock ) ;
2016-06-15 22:20:25 +03:00
}
break ;
case NDISC_NEIGHBOUR_ADVERTISEMENT :
case NDISC_NEIGHBOUR_SOLICITATION :
case NDISC_ROUTER_SOLICITATION :
wpan_dev = lowpan_802154_dev ( dev ) - > wdev - > ieee802154_ptr ;
if ( lowpan_802154_is_valid_src_short_addr ( wpan_dev - > short_addr ) )
addr_space = __ndisc_opt_addr_space ( IEEE802154_SHORT_ADDR_LEN , 0 ) ;
break ;
default :
break ;
}
return addr_space ;
}
static void lowpan_ndisc_fill_addr_option ( const struct net_device * dev ,
struct sk_buff * skb , u8 icmp6_type ,
const u8 * ha )
{
struct wpan_dev * wpan_dev ;
__be16 short_addr ;
u8 opt_type ;
if ( ! lowpan_is_ll ( dev , LOWPAN_LLTYPE_IEEE802154 ) )
return ;
switch ( icmp6_type ) {
case NDISC_REDIRECT :
if ( ha ) {
ieee802154_le16_to_be16 ( & short_addr , ha ) ;
__ndisc_fill_addr_option ( skb , ND_OPT_TARGET_LL_ADDR ,
& short_addr ,
IEEE802154_SHORT_ADDR_LEN , 0 ) ;
}
return ;
case NDISC_NEIGHBOUR_ADVERTISEMENT :
opt_type = ND_OPT_TARGET_LL_ADDR ;
break ;
case NDISC_ROUTER_SOLICITATION :
case NDISC_NEIGHBOUR_SOLICITATION :
opt_type = ND_OPT_SOURCE_LL_ADDR ;
break ;
default :
return ;
}
wpan_dev = lowpan_802154_dev ( dev ) - > wdev - > ieee802154_ptr ;
if ( lowpan_802154_is_valid_src_short_addr ( wpan_dev - > short_addr ) ) {
ieee802154_le16_to_be16 ( & short_addr ,
& wpan_dev - > short_addr ) ;
__ndisc_fill_addr_option ( skb , opt_type , & short_addr ,
IEEE802154_SHORT_ADDR_LEN , 0 ) ;
}
}
static void lowpan_ndisc_prefix_rcv_add_addr ( struct net * net ,
struct net_device * dev ,
const struct prefix_info * pinfo ,
struct inet6_dev * in6_dev ,
struct in6_addr * addr ,
int addr_type , u32 addr_flags ,
bool sllao , bool tokenized ,
__u32 valid_lft ,
u32 prefered_lft ,
bool dev_addr_generated )
{
int err ;
/* generates short based address for RA PIO's */
if ( lowpan_is_ll ( dev , LOWPAN_LLTYPE_IEEE802154 ) & & dev_addr_generated & &
! addrconf_ifid_802154_6lowpan ( addr - > s6_addr + 8 , dev ) ) {
err = addrconf_prefix_rcv_add_addr ( net , dev , pinfo , in6_dev ,
addr , addr_type , addr_flags ,
sllao , tokenized , valid_lft ,
prefered_lft ) ;
if ( err )
ND_PRINTK ( 2 , warn ,
" RA: could not add a short address based address for prefix: %pI6c \n " ,
& pinfo - > prefix ) ;
}
}
# endif
const struct ndisc_ops lowpan_ndisc_ops = {
. is_useropt = lowpan_ndisc_is_useropt ,
# if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
. parse_options = lowpan_ndisc_parse_options ,
. update = lowpan_ndisc_update ,
. opt_addr_space = lowpan_ndisc_opt_addr_space ,
. fill_addr_option = lowpan_ndisc_fill_addr_option ,
. prefix_rcv_add_addr = lowpan_ndisc_prefix_rcv_add_addr ,
# endif
} ;