2015-02-09 11:50:03 +03:00
/*
* Copyright ( c ) 2014 , Ericsson AB
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission .
*
* Alternatively , this software may be distributed under the terms of the
* GNU General Public License ( " GPL " ) version 2 as published by the Free
* Software Foundation .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*/
# include "core.h"
# include "config.h"
2015-02-09 11:50:04 +03:00
# include "bearer.h"
2015-02-09 11:50:06 +03:00
# include "link.h"
2015-02-09 11:50:10 +03:00
# include "name_table.h"
2015-02-09 11:50:11 +03:00
# include "socket.h"
2015-02-09 11:50:13 +03:00
# include "node.h"
2015-02-09 11:50:15 +03:00
# include "net.h"
2015-02-09 11:50:03 +03:00
# include <net/genetlink.h>
# include <linux/tipc_config.h>
2015-02-09 11:50:04 +03:00
/* The legacy API had an artificial message length limit called
* ULTRA_STRING_MAX_LEN .
*/
# define ULTRA_STRING_MAX_LEN 32768
# define TIPC_SKB_MAX TLV_SPACE(ULTRA_STRING_MAX_LEN)
# define REPLY_TRUNCATED "<truncated>\n"
struct tipc_nl_compat_msg {
u16 cmd ;
2015-02-09 11:50:06 +03:00
int rep_type ;
2015-02-09 11:50:04 +03:00
int rep_size ;
2015-02-09 11:50:05 +03:00
int req_type ;
2015-02-09 11:50:04 +03:00
struct sk_buff * rep ;
struct tlv_desc * req ;
struct sock * dst_sk ;
} ;
struct tipc_nl_compat_cmd_dump {
2015-02-09 11:50:10 +03:00
int ( * header ) ( struct tipc_nl_compat_msg * ) ;
2015-02-09 11:50:04 +03:00
int ( * dumpit ) ( struct sk_buff * , struct netlink_callback * ) ;
int ( * format ) ( struct tipc_nl_compat_msg * msg , struct nlattr * * attrs ) ;
} ;
2015-02-09 11:50:05 +03:00
struct tipc_nl_compat_cmd_doit {
int ( * doit ) ( struct sk_buff * skb , struct genl_info * info ) ;
int ( * transcode ) ( struct sk_buff * skb , struct tipc_nl_compat_msg * msg ) ;
} ;
2015-02-09 11:50:04 +03:00
static int tipc_skb_tailroom ( struct sk_buff * skb )
{
int tailroom ;
int limit ;
tailroom = skb_tailroom ( skb ) ;
limit = TIPC_SKB_MAX - skb - > len ;
if ( tailroom < limit )
return tailroom ;
return limit ;
}
static int tipc_add_tlv ( struct sk_buff * skb , u16 type , void * data , u16 len )
{
struct tlv_desc * tlv = ( struct tlv_desc * ) skb_tail_pointer ( skb ) ;
if ( tipc_skb_tailroom ( skb ) < TLV_SPACE ( len ) )
return - EMSGSIZE ;
skb_put ( skb , TLV_SPACE ( len ) ) ;
tlv - > tlv_type = htons ( type ) ;
tlv - > tlv_len = htons ( TLV_LENGTH ( len ) ) ;
if ( len & & data )
memcpy ( TLV_DATA ( tlv ) , data , len ) ;
return 0 ;
}
2015-02-09 11:50:06 +03:00
static void tipc_tlv_init ( struct sk_buff * skb , u16 type )
{
struct tlv_desc * tlv = ( struct tlv_desc * ) skb - > data ;
TLV_SET_LEN ( tlv , 0 ) ;
TLV_SET_TYPE ( tlv , type ) ;
skb_put ( skb , sizeof ( struct tlv_desc ) ) ;
}
static int tipc_tlv_sprintf ( struct sk_buff * skb , const char * fmt , . . . )
{
int n ;
u16 len ;
u32 rem ;
char * buf ;
struct tlv_desc * tlv ;
va_list args ;
rem = tipc_skb_tailroom ( skb ) ;
tlv = ( struct tlv_desc * ) skb - > data ;
len = TLV_GET_LEN ( tlv ) ;
buf = TLV_DATA ( tlv ) + len ;
va_start ( args , fmt ) ;
n = vscnprintf ( buf , rem , fmt , args ) ;
va_end ( args ) ;
TLV_SET_LEN ( tlv , n + len ) ;
skb_put ( skb , n ) ;
return n ;
}
2015-02-09 11:50:04 +03:00
static struct sk_buff * tipc_tlv_alloc ( int size )
{
int hdr_len ;
struct sk_buff * buf ;
size = TLV_SPACE ( size ) ;
hdr_len = nlmsg_total_size ( GENL_HDRLEN + TIPC_GENL_HDRLEN ) ;
buf = alloc_skb ( hdr_len + size , GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
skb_reserve ( buf , hdr_len ) ;
return buf ;
}
static struct sk_buff * tipc_get_err_tlv ( char * str )
{
int str_len = strlen ( str ) + 1 ;
struct sk_buff * buf ;
buf = tipc_tlv_alloc ( TLV_SPACE ( str_len ) ) ;
if ( buf )
tipc_add_tlv ( buf , TIPC_TLV_ERROR_STRING , str , str_len ) ;
return buf ;
}
static int __tipc_nl_compat_dumpit ( struct tipc_nl_compat_cmd_dump * cmd ,
struct tipc_nl_compat_msg * msg ,
struct sk_buff * arg )
{
int len = 0 ;
int err ;
struct sk_buff * buf ;
struct nlmsghdr * nlmsg ;
struct netlink_callback cb ;
memset ( & cb , 0 , sizeof ( cb ) ) ;
cb . nlh = ( struct nlmsghdr * ) arg - > data ;
cb . skb = arg ;
buf = nlmsg_new ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
buf - > sk = msg - > dst_sk ;
do {
int rem ;
len = ( * cmd - > dumpit ) ( buf , & cb ) ;
nlmsg_for_each_msg ( nlmsg , nlmsg_hdr ( buf ) , len , rem ) {
struct nlattr * * attrs ;
err = tipc_nlmsg_parse ( nlmsg , & attrs ) ;
if ( err )
goto err_out ;
err = ( * cmd - > format ) ( msg , attrs ) ;
if ( err )
goto err_out ;
if ( tipc_skb_tailroom ( msg - > rep ) < = 1 ) {
err = - EMSGSIZE ;
goto err_out ;
}
}
skb_reset_tail_pointer ( buf ) ;
buf - > len = 0 ;
} while ( len ) ;
err = 0 ;
err_out :
kfree_skb ( buf ) ;
if ( err = = - EMSGSIZE ) {
/* The legacy API only considered messages filling
* " ULTRA_STRING_MAX_LEN " to be truncated .
*/
if ( ( TIPC_SKB_MAX - msg - > rep - > len ) < = 1 ) {
char * tail = skb_tail_pointer ( msg - > rep ) ;
if ( * tail ! = ' \0 ' )
sprintf ( tail - sizeof ( REPLY_TRUNCATED ) - 1 ,
REPLY_TRUNCATED ) ;
}
return 0 ;
}
return err ;
}
static int tipc_nl_compat_dumpit ( struct tipc_nl_compat_cmd_dump * cmd ,
struct tipc_nl_compat_msg * msg )
{
int err ;
struct sk_buff * arg ;
2015-02-09 11:50:06 +03:00
if ( msg - > req_type & & ! TLV_CHECK_TYPE ( msg - > req , msg - > req_type ) )
return - EINVAL ;
2015-02-09 11:50:04 +03:00
msg - > rep = tipc_tlv_alloc ( msg - > rep_size ) ;
if ( ! msg - > rep )
return - ENOMEM ;
2015-02-09 11:50:06 +03:00
if ( msg - > rep_type )
tipc_tlv_init ( msg - > rep , msg - > rep_type ) ;
2015-02-09 11:50:10 +03:00
if ( cmd - > header )
( * cmd - > header ) ( msg ) ;
2015-02-09 11:50:04 +03:00
arg = nlmsg_new ( 0 , GFP_KERNEL ) ;
if ( ! arg ) {
kfree_skb ( msg - > rep ) ;
return - ENOMEM ;
}
err = __tipc_nl_compat_dumpit ( cmd , msg , arg ) ;
if ( err )
kfree_skb ( msg - > rep ) ;
kfree_skb ( arg ) ;
return err ;
}
2015-02-09 11:50:05 +03:00
static int __tipc_nl_compat_doit ( struct tipc_nl_compat_cmd_doit * cmd ,
struct tipc_nl_compat_msg * msg )
{
int err ;
struct sk_buff * doit_buf ;
struct sk_buff * trans_buf ;
struct nlattr * * attrbuf ;
struct genl_info info ;
trans_buf = alloc_skb ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! trans_buf )
return - ENOMEM ;
err = ( * cmd - > transcode ) ( trans_buf , msg ) ;
if ( err )
goto trans_out ;
attrbuf = kmalloc ( ( tipc_genl_family . maxattr + 1 ) *
sizeof ( struct nlattr * ) , GFP_KERNEL ) ;
if ( ! attrbuf ) {
err = - ENOMEM ;
goto trans_out ;
}
err = nla_parse ( attrbuf , tipc_genl_family . maxattr ,
( const struct nlattr * ) trans_buf - > data ,
trans_buf - > len , NULL ) ;
if ( err )
goto parse_out ;
doit_buf = alloc_skb ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! doit_buf ) {
err = - ENOMEM ;
goto parse_out ;
}
doit_buf - > sk = msg - > dst_sk ;
memset ( & info , 0 , sizeof ( info ) ) ;
info . attrs = attrbuf ;
err = ( * cmd - > doit ) ( doit_buf , & info ) ;
kfree_skb ( doit_buf ) ;
parse_out :
kfree ( attrbuf ) ;
trans_out :
kfree_skb ( trans_buf ) ;
return err ;
}
static int tipc_nl_compat_doit ( struct tipc_nl_compat_cmd_doit * cmd ,
struct tipc_nl_compat_msg * msg )
{
int err ;
if ( msg - > req_type & & ! TLV_CHECK_TYPE ( msg - > req , msg - > req_type ) )
return - EINVAL ;
err = __tipc_nl_compat_doit ( cmd , msg ) ;
if ( err )
return err ;
/* The legacy API considered an empty message a success message */
msg - > rep = tipc_tlv_alloc ( 0 ) ;
if ( ! msg - > rep )
return - ENOMEM ;
return 0 ;
}
2015-02-09 11:50:04 +03:00
static int tipc_nl_compat_bearer_dump ( struct tipc_nl_compat_msg * msg ,
struct nlattr * * attrs )
{
struct nlattr * bearer [ TIPC_NLA_BEARER_MAX + 1 ] ;
nla_parse_nested ( bearer , TIPC_NLA_BEARER_MAX , attrs [ TIPC_NLA_BEARER ] ,
NULL ) ;
return tipc_add_tlv ( msg - > rep , TIPC_TLV_BEARER_NAME ,
nla_data ( bearer [ TIPC_NLA_BEARER_NAME ] ) ,
nla_len ( bearer [ TIPC_NLA_BEARER_NAME ] ) ) ;
}
2015-02-09 11:50:05 +03:00
static int tipc_nl_compat_bearer_enable ( struct sk_buff * skb ,
struct tipc_nl_compat_msg * msg )
{
struct nlattr * prop ;
struct nlattr * bearer ;
struct tipc_bearer_config * b ;
b = ( struct tipc_bearer_config * ) TLV_DATA ( msg - > req ) ;
bearer = nla_nest_start ( skb , TIPC_NLA_BEARER ) ;
if ( ! bearer )
return - EMSGSIZE ;
if ( nla_put_string ( skb , TIPC_NLA_BEARER_NAME , b - > name ) )
return - EMSGSIZE ;
if ( nla_put_u32 ( skb , TIPC_NLA_BEARER_DOMAIN , ntohl ( b - > disc_domain ) ) )
return - EMSGSIZE ;
if ( ntohl ( b - > priority ) < = TIPC_MAX_LINK_PRI ) {
prop = nla_nest_start ( skb , TIPC_NLA_BEARER_PROP ) ;
if ( ! prop )
return - EMSGSIZE ;
if ( nla_put_u32 ( skb , TIPC_NLA_PROP_PRIO , ntohl ( b - > priority ) ) )
return - EMSGSIZE ;
nla_nest_end ( skb , prop ) ;
}
nla_nest_end ( skb , bearer ) ;
return 0 ;
}
static int tipc_nl_compat_bearer_disable ( struct sk_buff * skb ,
struct tipc_nl_compat_msg * msg )
{
char * name ;
struct nlattr * bearer ;
name = ( char * ) TLV_DATA ( msg - > req ) ;
bearer = nla_nest_start ( skb , TIPC_NLA_BEARER ) ;
if ( ! bearer )
return - EMSGSIZE ;
if ( nla_put_string ( skb , TIPC_NLA_BEARER_NAME , name ) )
return - EMSGSIZE ;
nla_nest_end ( skb , bearer ) ;
return 0 ;
}
2015-02-09 11:50:06 +03:00
static inline u32 perc ( u32 count , u32 total )
{
return ( count * 100 + ( total / 2 ) ) / total ;
}
static void __fill_bc_link_stat ( struct tipc_nl_compat_msg * msg ,
struct nlattr * prop [ ] , struct nlattr * stats [ ] )
{
tipc_tlv_sprintf ( msg - > rep , " Window:%u packets \n " ,
nla_get_u32 ( prop [ TIPC_NLA_PROP_WIN ] ) ) ;
tipc_tlv_sprintf ( msg - > rep ,
" RX packets:%u fragments:%u/%u bundles:%u/%u \n " ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_INFO ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_FRAGMENTS ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_FRAGMENTED ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_BUNDLES ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_BUNDLED ] ) ) ;
tipc_tlv_sprintf ( msg - > rep ,
" TX packets:%u fragments:%u/%u bundles:%u/%u \n " ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_INFO ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_FRAGMENTS ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_FRAGMENTED ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_BUNDLES ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_BUNDLED ] ) ) ;
tipc_tlv_sprintf ( msg - > rep , " RX naks:%u defs:%u dups:%u \n " ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_NACKS ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_DEFERRED ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_DUPLICATES ] ) ) ;
tipc_tlv_sprintf ( msg - > rep , " TX naks:%u acks:%u dups:%u \n " ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_NACKS ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_ACKS ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RETRANSMITTED ] ) ) ;
tipc_tlv_sprintf ( msg - > rep ,
" Congestion link:%u Send queue max:%u avg:%u " ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_LINK_CONGS ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_MAX_QUEUE ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_AVG_QUEUE ] ) ) ;
}
static int tipc_nl_compat_link_stat_dump ( struct tipc_nl_compat_msg * msg ,
struct nlattr * * attrs )
{
char * name ;
struct nlattr * link [ TIPC_NLA_LINK_MAX + 1 ] ;
struct nlattr * prop [ TIPC_NLA_PROP_MAX + 1 ] ;
struct nlattr * stats [ TIPC_NLA_STATS_MAX + 1 ] ;
nla_parse_nested ( link , TIPC_NLA_LINK_MAX , attrs [ TIPC_NLA_LINK ] , NULL ) ;
nla_parse_nested ( prop , TIPC_NLA_PROP_MAX , link [ TIPC_NLA_LINK_PROP ] ,
NULL ) ;
nla_parse_nested ( stats , TIPC_NLA_STATS_MAX , link [ TIPC_NLA_LINK_STATS ] ,
NULL ) ;
name = ( char * ) TLV_DATA ( msg - > req ) ;
if ( strcmp ( name , nla_data ( link [ TIPC_NLA_LINK_NAME ] ) ) ! = 0 )
return 0 ;
tipc_tlv_sprintf ( msg - > rep , " \n Link <%s> \n " ,
nla_data ( link [ TIPC_NLA_LINK_NAME ] ) ) ;
if ( link [ TIPC_NLA_LINK_BROADCAST ] ) {
__fill_bc_link_stat ( msg , prop , stats ) ;
return 0 ;
}
if ( link [ TIPC_NLA_LINK_ACTIVE ] )
tipc_tlv_sprintf ( msg - > rep , " ACTIVE " ) ;
else if ( link [ TIPC_NLA_LINK_UP ] )
tipc_tlv_sprintf ( msg - > rep , " STANDBY " ) ;
else
tipc_tlv_sprintf ( msg - > rep , " DEFUNCT " ) ;
tipc_tlv_sprintf ( msg - > rep , " MTU:%u Priority:%u " ,
nla_get_u32 ( link [ TIPC_NLA_LINK_MTU ] ) ,
nla_get_u32 ( prop [ TIPC_NLA_PROP_PRIO ] ) ) ;
tipc_tlv_sprintf ( msg - > rep , " Tolerance:%u ms Window:%u packets \n " ,
nla_get_u32 ( prop [ TIPC_NLA_PROP_TOL ] ) ,
nla_get_u32 ( prop [ TIPC_NLA_PROP_WIN ] ) ) ;
tipc_tlv_sprintf ( msg - > rep ,
" RX packets:%u fragments:%u/%u bundles:%u/%u \n " ,
nla_get_u32 ( link [ TIPC_NLA_LINK_RX ] ) -
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_INFO ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_FRAGMENTS ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_FRAGMENTED ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_BUNDLES ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_BUNDLED ] ) ) ;
tipc_tlv_sprintf ( msg - > rep ,
" TX packets:%u fragments:%u/%u bundles:%u/%u \n " ,
nla_get_u32 ( link [ TIPC_NLA_LINK_TX ] ) -
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_INFO ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_FRAGMENTS ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_FRAGMENTED ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_BUNDLES ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_BUNDLED ] ) ) ;
tipc_tlv_sprintf ( msg - > rep ,
" TX profile sample:%u packets average:%u octets \n " ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_LEN_CNT ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_LEN_TOT ] ) /
nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_PROF_TOT ] ) ) ;
tipc_tlv_sprintf ( msg - > rep ,
" 0-64:%u%% -256:%u%% -1024:%u%% -4096:%u%% " ,
perc ( nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_LEN_P0 ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_PROF_TOT ] ) ) ,
perc ( nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_LEN_P1 ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_PROF_TOT ] ) ) ,
perc ( nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_LEN_P2 ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_PROF_TOT ] ) ) ,
perc ( nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_LEN_P3 ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_PROF_TOT ] ) ) ) ;
tipc_tlv_sprintf ( msg - > rep , " -16384:%u%% -32768:%u%% -66000:%u%% \n " ,
perc ( nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_LEN_P4 ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_PROF_TOT ] ) ) ,
perc ( nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_LEN_P5 ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_PROF_TOT ] ) ) ,
perc ( nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_LEN_P6 ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_MSG_PROF_TOT ] ) ) ) ;
tipc_tlv_sprintf ( msg - > rep ,
" RX states:%u probes:%u naks:%u defs:%u dups:%u \n " ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_STATES ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_PROBES ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_NACKS ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RX_DEFERRED ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_DUPLICATES ] ) ) ;
tipc_tlv_sprintf ( msg - > rep ,
" TX states:%u probes:%u naks:%u acks:%u dups:%u \n " ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_STATES ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_PROBES ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_NACKS ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_TX_ACKS ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_RETRANSMITTED ] ) ) ;
tipc_tlv_sprintf ( msg - > rep ,
" Congestion link:%u Send queue max:%u avg:%u " ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_LINK_CONGS ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_MAX_QUEUE ] ) ,
nla_get_u32 ( stats [ TIPC_NLA_STATS_AVG_QUEUE ] ) ) ;
return 0 ;
}
2015-02-09 11:50:07 +03:00
static int tipc_nl_compat_link_dump ( struct tipc_nl_compat_msg * msg ,
struct nlattr * * attrs )
{
struct nlattr * link [ TIPC_NLA_LINK_MAX + 1 ] ;
struct tipc_link_info link_info ;
nla_parse_nested ( link , TIPC_NLA_LINK_MAX , attrs [ TIPC_NLA_LINK ] , NULL ) ;
link_info . dest = nla_get_flag ( link [ TIPC_NLA_LINK_DEST ] ) ;
link_info . up = htonl ( nla_get_flag ( link [ TIPC_NLA_LINK_UP ] ) ) ;
strcpy ( link_info . str , nla_data ( link [ TIPC_NLA_LINK_NAME ] ) ) ;
return tipc_add_tlv ( msg - > rep , TIPC_TLV_LINK_INFO ,
& link_info , sizeof ( link_info ) ) ;
}
2015-02-09 11:50:08 +03:00
static int tipc_nl_compat_link_set ( struct sk_buff * skb ,
struct tipc_nl_compat_msg * msg )
{
struct nlattr * link ;
struct nlattr * prop ;
struct tipc_link_config * lc ;
lc = ( struct tipc_link_config * ) TLV_DATA ( msg - > req ) ;
link = nla_nest_start ( skb , TIPC_NLA_LINK ) ;
if ( ! link )
return - EMSGSIZE ;
if ( nla_put_string ( skb , TIPC_NLA_LINK_NAME , lc - > name ) )
return - EMSGSIZE ;
prop = nla_nest_start ( skb , TIPC_NLA_LINK_PROP ) ;
if ( ! prop )
return - EMSGSIZE ;
if ( msg - > cmd = = TIPC_CMD_SET_LINK_PRI ) {
if ( nla_put_u32 ( skb , TIPC_NLA_PROP_PRIO , ntohl ( lc - > value ) ) )
return - EMSGSIZE ;
} else if ( msg - > cmd = = TIPC_CMD_SET_LINK_TOL ) {
if ( nla_put_u32 ( skb , TIPC_NLA_PROP_TOL , ntohl ( lc - > value ) ) )
return - EMSGSIZE ;
} else if ( msg - > cmd = = TIPC_CMD_SET_LINK_WINDOW ) {
if ( nla_put_u32 ( skb , TIPC_NLA_PROP_WIN , ntohl ( lc - > value ) ) )
return - EMSGSIZE ;
}
nla_nest_end ( skb , prop ) ;
nla_nest_end ( skb , link ) ;
return 0 ;
}
2015-02-09 11:50:09 +03:00
static int tipc_nl_compat_link_reset_stats ( struct sk_buff * skb ,
struct tipc_nl_compat_msg * msg )
{
char * name ;
struct nlattr * link ;
name = ( char * ) TLV_DATA ( msg - > req ) ;
link = nla_nest_start ( skb , TIPC_NLA_LINK ) ;
if ( ! link )
return - EMSGSIZE ;
if ( nla_put_string ( skb , TIPC_NLA_LINK_NAME , name ) )
return - EMSGSIZE ;
nla_nest_end ( skb , link ) ;
return 0 ;
}
2015-02-09 11:50:10 +03:00
static int tipc_nl_compat_name_table_dump_header ( struct tipc_nl_compat_msg * msg )
{
int i ;
u32 depth ;
struct tipc_name_table_query * ntq ;
static const char * const header [ ] = {
" Type " ,
" Lower Upper " ,
" Port Identity " ,
" Publication Scope "
} ;
ntq = ( struct tipc_name_table_query * ) TLV_DATA ( msg - > req ) ;
depth = ntohl ( ntq - > depth ) ;
if ( depth > 4 )
depth = 4 ;
for ( i = 0 ; i < depth ; i + + )
tipc_tlv_sprintf ( msg - > rep , header [ i ] ) ;
tipc_tlv_sprintf ( msg - > rep , " \n " ) ;
return 0 ;
}
static int tipc_nl_compat_name_table_dump ( struct tipc_nl_compat_msg * msg ,
struct nlattr * * attrs )
{
char port_str [ 27 ] ;
struct tipc_name_table_query * ntq ;
struct nlattr * nt [ TIPC_NLA_NAME_TABLE_MAX + 1 ] ;
struct nlattr * publ [ TIPC_NLA_PUBL_MAX + 1 ] ;
u32 node , depth , type , lowbound , upbound ;
static const char * const scope_str [ ] = { " " , " zone " , " cluster " ,
" node " } ;
nla_parse_nested ( nt , TIPC_NLA_NAME_TABLE_MAX ,
attrs [ TIPC_NLA_NAME_TABLE ] , NULL ) ;
nla_parse_nested ( publ , TIPC_NLA_PUBL_MAX , nt [ TIPC_NLA_NAME_TABLE_PUBL ] ,
NULL ) ;
ntq = ( struct tipc_name_table_query * ) TLV_DATA ( msg - > req ) ;
depth = ntohl ( ntq - > depth ) ;
type = ntohl ( ntq - > type ) ;
lowbound = ntohl ( ntq - > lowbound ) ;
upbound = ntohl ( ntq - > upbound ) ;
if ( ! ( depth & TIPC_NTQ_ALLTYPES ) & &
( type ! = nla_get_u32 ( publ [ TIPC_NLA_PUBL_TYPE ] ) ) )
return 0 ;
if ( lowbound & & ( lowbound > nla_get_u32 ( publ [ TIPC_NLA_PUBL_UPPER ] ) ) )
return 0 ;
if ( upbound & & ( upbound < nla_get_u32 ( publ [ TIPC_NLA_PUBL_LOWER ] ) ) )
return 0 ;
tipc_tlv_sprintf ( msg - > rep , " %-10u " ,
nla_get_u32 ( publ [ TIPC_NLA_PUBL_TYPE ] ) ) ;
if ( depth = = 1 )
goto out ;
tipc_tlv_sprintf ( msg - > rep , " %-10u %-10u " ,
nla_get_u32 ( publ [ TIPC_NLA_PUBL_LOWER ] ) ,
nla_get_u32 ( publ [ TIPC_NLA_PUBL_UPPER ] ) ) ;
if ( depth = = 2 )
goto out ;
node = nla_get_u32 ( publ [ TIPC_NLA_PUBL_NODE ] ) ;
sprintf ( port_str , " <%u.%u.%u:%u> " , tipc_zone ( node ) , tipc_cluster ( node ) ,
tipc_node ( node ) , nla_get_u32 ( publ [ TIPC_NLA_PUBL_REF ] ) ) ;
tipc_tlv_sprintf ( msg - > rep , " %-26s " , port_str ) ;
if ( depth = = 3 )
goto out ;
tipc_tlv_sprintf ( msg - > rep , " %-10u %s " ,
nla_get_u32 ( publ [ TIPC_NLA_PUBL_REF ] ) ,
scope_str [ nla_get_u32 ( publ [ TIPC_NLA_PUBL_SCOPE ] ) ] ) ;
out :
tipc_tlv_sprintf ( msg - > rep , " \n " ) ;
return 0 ;
}
2015-02-09 11:50:11 +03:00
static int __tipc_nl_compat_publ_dump ( struct tipc_nl_compat_msg * msg ,
struct nlattr * * attrs )
{
u32 type , lower , upper ;
struct nlattr * publ [ TIPC_NLA_PUBL_MAX + 1 ] ;
nla_parse_nested ( publ , TIPC_NLA_PUBL_MAX , attrs [ TIPC_NLA_PUBL ] , NULL ) ;
type = nla_get_u32 ( publ [ TIPC_NLA_PUBL_TYPE ] ) ;
lower = nla_get_u32 ( publ [ TIPC_NLA_PUBL_LOWER ] ) ;
upper = nla_get_u32 ( publ [ TIPC_NLA_PUBL_UPPER ] ) ;
if ( lower = = upper )
tipc_tlv_sprintf ( msg - > rep , " {%u,%u} " , type , lower ) ;
else
tipc_tlv_sprintf ( msg - > rep , " {%u,%u,%u} " , type , lower , upper ) ;
return 0 ;
}
static int tipc_nl_compat_publ_dump ( struct tipc_nl_compat_msg * msg , u32 sock )
{
int err ;
void * hdr ;
struct nlattr * nest ;
struct sk_buff * args ;
struct tipc_nl_compat_cmd_dump dump ;
args = nlmsg_new ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! args )
return - ENOMEM ;
hdr = genlmsg_put ( args , 0 , 0 , & tipc_genl_family , NLM_F_MULTI ,
TIPC_NL_PUBL_GET ) ;
nest = nla_nest_start ( args , TIPC_NLA_SOCK ) ;
if ( ! nest ) {
kfree_skb ( args ) ;
return - EMSGSIZE ;
}
if ( nla_put_u32 ( args , TIPC_NLA_SOCK_REF , sock ) ) {
kfree_skb ( args ) ;
return - EMSGSIZE ;
}
nla_nest_end ( args , nest ) ;
genlmsg_end ( args , hdr ) ;
dump . dumpit = tipc_nl_publ_dump ;
dump . format = __tipc_nl_compat_publ_dump ;
err = __tipc_nl_compat_dumpit ( & dump , msg , args ) ;
kfree_skb ( args ) ;
return err ;
}
static int tipc_nl_compat_sk_dump ( struct tipc_nl_compat_msg * msg ,
struct nlattr * * attrs )
{
int err ;
u32 sock_ref ;
struct nlattr * sock [ TIPC_NLA_SOCK_MAX + 1 ] ;
nla_parse_nested ( sock , TIPC_NLA_SOCK_MAX , attrs [ TIPC_NLA_SOCK ] , NULL ) ;
sock_ref = nla_get_u32 ( sock [ TIPC_NLA_SOCK_REF ] ) ;
tipc_tlv_sprintf ( msg - > rep , " %u: " , sock_ref ) ;
if ( sock [ TIPC_NLA_SOCK_CON ] ) {
u32 node ;
struct nlattr * con [ TIPC_NLA_CON_MAX + 1 ] ;
nla_parse_nested ( con , TIPC_NLA_CON_MAX , sock [ TIPC_NLA_SOCK_CON ] ,
NULL ) ;
node = nla_get_u32 ( con [ TIPC_NLA_CON_NODE ] ) ;
tipc_tlv_sprintf ( msg - > rep , " connected to <%u.%u.%u:%u> " ,
tipc_zone ( node ) ,
tipc_cluster ( node ) ,
tipc_node ( node ) ,
nla_get_u32 ( con [ TIPC_NLA_CON_SOCK ] ) ) ;
if ( con [ TIPC_NLA_CON_FLAG ] )
tipc_tlv_sprintf ( msg - > rep , " via {%u,%u} \n " ,
nla_get_u32 ( con [ TIPC_NLA_CON_TYPE ] ) ,
nla_get_u32 ( con [ TIPC_NLA_CON_INST ] ) ) ;
else
tipc_tlv_sprintf ( msg - > rep , " \n " ) ;
} else if ( sock [ TIPC_NLA_SOCK_HAS_PUBL ] ) {
tipc_tlv_sprintf ( msg - > rep , " bound to " ) ;
err = tipc_nl_compat_publ_dump ( msg , sock_ref ) ;
if ( err )
return err ;
}
tipc_tlv_sprintf ( msg - > rep , " \n " ) ;
return 0 ;
}
2015-02-09 11:50:12 +03:00
static int tipc_nl_compat_media_dump ( struct tipc_nl_compat_msg * msg ,
struct nlattr * * attrs )
{
struct nlattr * media [ TIPC_NLA_MEDIA_MAX + 1 ] ;
nla_parse_nested ( media , TIPC_NLA_MEDIA_MAX , attrs [ TIPC_NLA_MEDIA ] ,
NULL ) ;
return tipc_add_tlv ( msg - > rep , TIPC_TLV_MEDIA_NAME ,
nla_data ( media [ TIPC_NLA_MEDIA_NAME ] ) ,
nla_len ( media [ TIPC_NLA_MEDIA_NAME ] ) ) ;
}
2015-02-09 11:50:13 +03:00
static int tipc_nl_compat_node_dump ( struct tipc_nl_compat_msg * msg ,
struct nlattr * * attrs )
{
struct tipc_node_info node_info ;
struct nlattr * node [ TIPC_NLA_NODE_MAX + 1 ] ;
nla_parse_nested ( node , TIPC_NLA_NODE_MAX , attrs [ TIPC_NLA_NODE ] , NULL ) ;
node_info . addr = htonl ( nla_get_u32 ( node [ TIPC_NLA_NODE_ADDR ] ) ) ;
node_info . up = htonl ( nla_get_flag ( node [ TIPC_NLA_NODE_UP ] ) ) ;
return tipc_add_tlv ( msg - > rep , TIPC_TLV_NODE_INFO , & node_info ,
sizeof ( node_info ) ) ;
}
2015-02-09 11:50:14 +03:00
static int tipc_nl_compat_net_set ( struct sk_buff * skb ,
struct tipc_nl_compat_msg * msg )
{
u32 val ;
struct nlattr * net ;
val = ntohl ( * ( __be32 * ) TLV_DATA ( msg - > req ) ) ;
net = nla_nest_start ( skb , TIPC_NLA_NET ) ;
if ( ! net )
return - EMSGSIZE ;
2015-02-09 11:50:15 +03:00
if ( msg - > cmd = = TIPC_CMD_SET_NODE_ADDR ) {
if ( nla_put_u32 ( skb , TIPC_NLA_NET_ADDR , val ) )
return - EMSGSIZE ;
} else if ( msg - > cmd = = TIPC_CMD_SET_NETID ) {
if ( nla_put_u32 ( skb , TIPC_NLA_NET_ID , val ) )
return - EMSGSIZE ;
}
2015-02-09 11:50:14 +03:00
nla_nest_end ( skb , net ) ;
return 0 ;
}
2015-02-09 11:50:16 +03:00
static int tipc_nl_compat_net_dump ( struct tipc_nl_compat_msg * msg ,
struct nlattr * * attrs )
{
__be32 id ;
struct nlattr * net [ TIPC_NLA_NET_MAX + 1 ] ;
nla_parse_nested ( net , TIPC_NLA_NET_MAX , attrs [ TIPC_NLA_NET ] , NULL ) ;
id = htonl ( nla_get_u32 ( net [ TIPC_NLA_NET_ID ] ) ) ;
return tipc_add_tlv ( msg - > rep , TIPC_TLV_UNSIGNED , & id , sizeof ( id ) ) ;
}
2015-02-09 11:50:04 +03:00
static int tipc_nl_compat_handle ( struct tipc_nl_compat_msg * msg )
{
struct tipc_nl_compat_cmd_dump dump ;
2015-02-09 11:50:05 +03:00
struct tipc_nl_compat_cmd_doit doit ;
2015-02-09 11:50:04 +03:00
memset ( & dump , 0 , sizeof ( dump ) ) ;
2015-02-09 11:50:05 +03:00
memset ( & doit , 0 , sizeof ( doit ) ) ;
2015-02-09 11:50:04 +03:00
switch ( msg - > cmd ) {
case TIPC_CMD_GET_BEARER_NAMES :
msg - > rep_size = MAX_BEARERS * TLV_SPACE ( TIPC_MAX_BEARER_NAME ) ;
dump . dumpit = tipc_nl_bearer_dump ;
dump . format = tipc_nl_compat_bearer_dump ;
return tipc_nl_compat_dumpit ( & dump , msg ) ;
2015-02-09 11:50:05 +03:00
case TIPC_CMD_ENABLE_BEARER :
msg - > req_type = TIPC_TLV_BEARER_CONFIG ;
doit . doit = tipc_nl_bearer_enable ;
doit . transcode = tipc_nl_compat_bearer_enable ;
return tipc_nl_compat_doit ( & doit , msg ) ;
case TIPC_CMD_DISABLE_BEARER :
msg - > req_type = TIPC_TLV_BEARER_NAME ;
doit . doit = tipc_nl_bearer_disable ;
doit . transcode = tipc_nl_compat_bearer_disable ;
return tipc_nl_compat_doit ( & doit , msg ) ;
2015-02-09 11:50:06 +03:00
case TIPC_CMD_SHOW_LINK_STATS :
msg - > req_type = TIPC_TLV_LINK_NAME ;
msg - > rep_size = ULTRA_STRING_MAX_LEN ;
msg - > rep_type = TIPC_TLV_ULTRA_STRING ;
dump . dumpit = tipc_nl_link_dump ;
dump . format = tipc_nl_compat_link_stat_dump ;
return tipc_nl_compat_dumpit ( & dump , msg ) ;
2015-02-09 11:50:07 +03:00
case TIPC_CMD_GET_LINKS :
msg - > req_type = TIPC_TLV_NET_ADDR ;
msg - > rep_size = ULTRA_STRING_MAX_LEN ;
dump . dumpit = tipc_nl_link_dump ;
dump . format = tipc_nl_compat_link_dump ;
return tipc_nl_compat_dumpit ( & dump , msg ) ;
2015-02-09 11:50:08 +03:00
case TIPC_CMD_SET_LINK_TOL :
case TIPC_CMD_SET_LINK_PRI :
case TIPC_CMD_SET_LINK_WINDOW :
msg - > req_type = TIPC_TLV_LINK_CONFIG ;
doit . doit = tipc_nl_link_set ;
doit . transcode = tipc_nl_compat_link_set ;
return tipc_nl_compat_doit ( & doit , msg ) ;
2015-02-09 11:50:09 +03:00
case TIPC_CMD_RESET_LINK_STATS :
msg - > req_type = TIPC_TLV_LINK_NAME ;
doit . doit = tipc_nl_link_reset_stats ;
doit . transcode = tipc_nl_compat_link_reset_stats ;
return tipc_nl_compat_doit ( & doit , msg ) ;
2015-02-09 11:50:10 +03:00
case TIPC_CMD_SHOW_NAME_TABLE :
msg - > req_type = TIPC_TLV_NAME_TBL_QUERY ;
msg - > rep_size = ULTRA_STRING_MAX_LEN ;
msg - > rep_type = TIPC_TLV_ULTRA_STRING ;
dump . header = tipc_nl_compat_name_table_dump_header ;
dump . dumpit = tipc_nl_name_table_dump ;
dump . format = tipc_nl_compat_name_table_dump ;
return tipc_nl_compat_dumpit ( & dump , msg ) ;
2015-02-09 11:50:11 +03:00
case TIPC_CMD_SHOW_PORTS :
msg - > rep_size = ULTRA_STRING_MAX_LEN ;
msg - > rep_type = TIPC_TLV_ULTRA_STRING ;
dump . dumpit = tipc_nl_sk_dump ;
dump . format = tipc_nl_compat_sk_dump ;
return tipc_nl_compat_dumpit ( & dump , msg ) ;
2015-02-09 11:50:12 +03:00
case TIPC_CMD_GET_MEDIA_NAMES :
msg - > rep_size = MAX_MEDIA * TLV_SPACE ( TIPC_MAX_MEDIA_NAME ) ;
dump . dumpit = tipc_nl_media_dump ;
dump . format = tipc_nl_compat_media_dump ;
return tipc_nl_compat_dumpit ( & dump , msg ) ;
2015-02-09 11:50:13 +03:00
case TIPC_CMD_GET_NODES :
msg - > rep_size = ULTRA_STRING_MAX_LEN ;
dump . dumpit = tipc_nl_node_dump ;
dump . format = tipc_nl_compat_node_dump ;
return tipc_nl_compat_dumpit ( & dump , msg ) ;
2015-02-09 11:50:14 +03:00
case TIPC_CMD_SET_NODE_ADDR :
msg - > req_type = TIPC_TLV_NET_ADDR ;
doit . doit = tipc_nl_net_set ;
doit . transcode = tipc_nl_compat_net_set ;
return tipc_nl_compat_doit ( & doit , msg ) ;
2015-02-09 11:50:15 +03:00
case TIPC_CMD_SET_NETID :
msg - > req_type = TIPC_TLV_UNSIGNED ;
doit . doit = tipc_nl_net_set ;
doit . transcode = tipc_nl_compat_net_set ;
return tipc_nl_compat_doit ( & doit , msg ) ;
2015-02-09 11:50:16 +03:00
case TIPC_CMD_GET_NETID :
msg - > rep_size = sizeof ( u32 ) ;
dump . dumpit = tipc_nl_net_dump ;
dump . format = tipc_nl_compat_net_dump ;
return tipc_nl_compat_dumpit ( & dump , msg ) ;
2015-02-09 11:50:04 +03:00
}
return - EOPNOTSUPP ;
}
static int tipc_nl_compat_recv ( struct sk_buff * skb , struct genl_info * info )
{
int err ;
int len ;
struct tipc_nl_compat_msg msg ;
struct nlmsghdr * req_nlh ;
struct nlmsghdr * rep_nlh ;
struct tipc_genlmsghdr * req_userhdr = info - > userhdr ;
struct net * net = genl_info_net ( info ) ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
req_nlh = ( struct nlmsghdr * ) skb - > data ;
msg . req = nlmsg_data ( req_nlh ) + GENL_HDRLEN + TIPC_GENL_HDRLEN ;
msg . cmd = req_userhdr - > cmd ;
msg . dst_sk = info - > dst_sk ;
if ( ( msg . cmd & 0xC000 ) & & ( ! netlink_net_capable ( skb , CAP_NET_ADMIN ) ) ) {
msg . rep = tipc_get_err_tlv ( TIPC_CFG_NOT_NET_ADMIN ) ;
err = - EACCES ;
goto send ;
}
len = nlmsg_attrlen ( req_nlh , GENL_HDRLEN + TIPC_GENL_HDRLEN ) ;
if ( TLV_GET_LEN ( msg . req ) & & ! TLV_OK ( msg . req , len ) ) {
msg . rep = tipc_get_err_tlv ( TIPC_CFG_NOT_SUPPORTED ) ;
err = - EOPNOTSUPP ;
goto send ;
}
err = tipc_nl_compat_handle ( & msg ) ;
if ( err = = - EOPNOTSUPP )
msg . rep = tipc_get_err_tlv ( TIPC_CFG_NOT_SUPPORTED ) ;
else if ( err = = - EINVAL )
msg . rep = tipc_get_err_tlv ( TIPC_CFG_TLV_ERROR ) ;
send :
if ( ! msg . rep )
return err ;
len = nlmsg_total_size ( GENL_HDRLEN + TIPC_GENL_HDRLEN ) ;
skb_push ( msg . rep , len ) ;
rep_nlh = nlmsg_hdr ( msg . rep ) ;
memcpy ( rep_nlh , info - > nlhdr , len ) ;
rep_nlh - > nlmsg_len = msg . rep - > len ;
genlmsg_unicast ( net , msg . rep , NETLINK_CB ( skb ) . portid ) ;
return err ;
}
2015-02-09 11:50:03 +03:00
static int handle_cmd ( struct sk_buff * skb , struct genl_info * info )
{
struct net * net = genl_info_net ( info ) ;
struct sk_buff * rep_buf ;
struct nlmsghdr * rep_nlh ;
struct nlmsghdr * req_nlh = info - > nlhdr ;
struct tipc_genlmsghdr * req_userhdr = info - > userhdr ;
int hdr_space = nlmsg_total_size ( GENL_HDRLEN + TIPC_GENL_HDRLEN ) ;
u16 cmd ;
if ( ( req_userhdr - > cmd & 0xC000 ) & &
( ! netlink_net_capable ( skb , CAP_NET_ADMIN ) ) )
cmd = TIPC_CMD_NOT_NET_ADMIN ;
else
cmd = req_userhdr - > cmd ;
rep_buf = tipc_cfg_do_cmd ( net , req_userhdr - > dest , cmd ,
nlmsg_data ( req_nlh ) + GENL_HDRLEN +
TIPC_GENL_HDRLEN ,
nlmsg_attrlen ( req_nlh , GENL_HDRLEN +
TIPC_GENL_HDRLEN ) , hdr_space ) ;
if ( rep_buf ) {
skb_push ( rep_buf , hdr_space ) ;
rep_nlh = nlmsg_hdr ( rep_buf ) ;
memcpy ( rep_nlh , req_nlh , hdr_space ) ;
rep_nlh - > nlmsg_len = rep_buf - > len ;
genlmsg_unicast ( net , rep_buf , NETLINK_CB ( skb ) . portid ) ;
}
return 0 ;
}
2015-02-09 11:50:04 +03:00
/* Temporary function to keep functionality throughout the patchset
* without having to mess with the global variables and other trickery
* of the old API .
*/
static int tipc_nl_compat_tmp_wrap ( struct sk_buff * skb , struct genl_info * info )
{
struct tipc_genlmsghdr * req = info - > userhdr ;
switch ( req - > cmd ) {
case TIPC_CMD_GET_BEARER_NAMES :
2015-02-09 11:50:05 +03:00
case TIPC_CMD_ENABLE_BEARER :
case TIPC_CMD_DISABLE_BEARER :
2015-02-09 11:50:06 +03:00
case TIPC_CMD_SHOW_LINK_STATS :
2015-02-09 11:50:07 +03:00
case TIPC_CMD_GET_LINKS :
2015-02-09 11:50:08 +03:00
case TIPC_CMD_SET_LINK_TOL :
case TIPC_CMD_SET_LINK_PRI :
case TIPC_CMD_SET_LINK_WINDOW :
2015-02-09 11:50:09 +03:00
case TIPC_CMD_RESET_LINK_STATS :
2015-02-09 11:50:10 +03:00
case TIPC_CMD_SHOW_NAME_TABLE :
2015-02-09 11:50:11 +03:00
case TIPC_CMD_SHOW_PORTS :
2015-02-09 11:50:12 +03:00
case TIPC_CMD_GET_MEDIA_NAMES :
2015-02-09 11:50:13 +03:00
case TIPC_CMD_GET_NODES :
2015-02-09 11:50:14 +03:00
case TIPC_CMD_SET_NODE_ADDR :
2015-02-09 11:50:15 +03:00
case TIPC_CMD_SET_NETID :
2015-02-09 11:50:16 +03:00
case TIPC_CMD_GET_NETID :
2015-02-09 11:50:04 +03:00
return tipc_nl_compat_recv ( skb , info ) ;
}
return handle_cmd ( skb , info ) ;
}
2015-02-09 11:50:03 +03:00
static struct genl_family tipc_genl_compat_family = {
. id = GENL_ID_GENERATE ,
. name = TIPC_GENL_NAME ,
. version = TIPC_GENL_VERSION ,
. hdrsize = TIPC_GENL_HDRLEN ,
. maxattr = 0 ,
. netnsok = true ,
} ;
static struct genl_ops tipc_genl_compat_ops [ ] = {
{
. cmd = TIPC_GENL_CMD ,
2015-02-09 11:50:04 +03:00
. doit = tipc_nl_compat_tmp_wrap ,
2015-02-09 11:50:03 +03:00
} ,
} ;
int tipc_netlink_compat_start ( void )
{
int res ;
res = genl_register_family_with_ops ( & tipc_genl_compat_family ,
tipc_genl_compat_ops ) ;
if ( res ) {
pr_err ( " Failed to register legacy compat interface \n " ) ;
return res ;
}
return 0 ;
}
void tipc_netlink_compat_stop ( void )
{
genl_unregister_family ( & tipc_genl_compat_family ) ;
}