2005-11-10 04:25:51 +03:00
/*
* NETLINK Netlink attributes
*
* Authors : Thomas Graf < tgraf @ suug . ch >
* Alexey Kuznetsov < kuznet @ ms2 . inr . ac . ru >
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/jiffies.h>
# include <linux/netdevice.h>
# include <linux/skbuff.h>
# include <linux/string.h>
# include <linux/types.h>
# include <net/netlink.h>
static u16 nla_attr_minlen [ NLA_TYPE_MAX + 1 ] __read_mostly = {
[ NLA_U8 ] = sizeof ( u8 ) ,
[ NLA_U16 ] = sizeof ( u16 ) ,
[ NLA_U32 ] = sizeof ( u32 ) ,
[ NLA_U64 ] = sizeof ( u64 ) ,
[ NLA_NESTED ] = NLA_HDRLEN ,
} ;
static int validate_nla ( struct nlattr * nla , int maxtype ,
2007-06-05 23:38:30 +04:00
const struct nla_policy * policy )
2005-11-10 04:25:51 +03:00
{
2007-06-05 23:38:30 +04:00
const struct nla_policy * pt ;
2007-09-12 16:44:36 +04:00
int minlen = 0 , attrlen = nla_len ( nla ) , type = nla_type ( nla ) ;
2005-11-10 04:25:51 +03:00
2007-09-12 16:44:36 +04:00
if ( type < = 0 | | type > maxtype )
2005-11-10 04:25:51 +03:00
return 0 ;
2007-09-12 16:44:36 +04:00
pt = & policy [ type ] ;
2005-11-10 04:25:51 +03:00
BUG_ON ( pt - > type > NLA_TYPE_MAX ) ;
2006-08-27 07:11:47 +04:00
switch ( pt - > type ) {
case NLA_FLAG :
if ( attrlen > 0 )
return - ERANGE ;
break ;
2005-11-10 04:25:51 +03:00
2006-08-27 07:11:47 +04:00
case NLA_NUL_STRING :
if ( pt - > len )
minlen = min_t ( int , attrlen , pt - > len + 1 ) ;
else
minlen = attrlen ;
2005-11-10 04:25:51 +03:00
2006-08-27 07:11:47 +04:00
if ( ! minlen | | memchr ( nla_data ( nla ) , ' \0 ' , minlen ) = = NULL )
return - EINVAL ;
/* fall through */
case NLA_STRING :
if ( attrlen < 1 )
return - ERANGE ;
if ( pt - > len ) {
char * buf = nla_data ( nla ) ;
if ( buf [ attrlen - 1 ] = = ' \0 ' )
attrlen - - ;
if ( attrlen > pt - > len )
return - ERANGE ;
}
break ;
2007-03-23 21:37:48 +03:00
case NLA_BINARY :
if ( pt - > len & & attrlen > pt - > len )
return - ERANGE ;
break ;
2007-06-26 00:49:35 +04:00
case NLA_NESTED_COMPAT :
if ( attrlen < pt - > len )
return - ERANGE ;
if ( attrlen < NLA_ALIGN ( pt - > len ) )
break ;
if ( attrlen < NLA_ALIGN ( pt - > len ) + NLA_HDRLEN )
return - ERANGE ;
nla = nla_data ( nla ) + NLA_ALIGN ( pt - > len ) ;
if ( attrlen < NLA_ALIGN ( pt - > len ) + NLA_HDRLEN + nla_len ( nla ) )
return - ERANGE ;
break ;
2008-11-28 14:05:19 +03:00
case NLA_NESTED :
/* a nested attributes is allowed to be empty; if its not,
* it must have a size of at least NLA_HDRLEN .
*/
if ( attrlen = = 0 )
break ;
2006-08-27 07:11:47 +04:00
default :
if ( pt - > len )
minlen = pt - > len ;
else if ( pt - > type ! = NLA_UNSPEC )
minlen = nla_attr_minlen [ pt - > type ] ;
if ( attrlen < minlen )
return - ERANGE ;
}
2005-11-10 04:25:51 +03:00
return 0 ;
}
/**
* nla_validate - Validate a stream of attributes
* @ head : head of attribute stream
* @ len : length of attribute stream
* @ maxtype : maximum attribute type to be expected
* @ policy : validation policy
*
* Validates all attributes in the specified attribute stream against the
* specified policy . Attributes with a type exceeding maxtype will be
* ignored . See documenation of struct nla_policy for more details .
*
* Returns 0 on success or a negative error code .
*/
int nla_validate ( struct nlattr * head , int len , int maxtype ,
2007-06-05 23:38:30 +04:00
const struct nla_policy * policy )
2005-11-10 04:25:51 +03:00
{
struct nlattr * nla ;
int rem , err ;
nla_for_each_attr ( nla , head , len , rem ) {
err = validate_nla ( nla , maxtype , policy ) ;
if ( err < 0 )
goto errout ;
}
err = 0 ;
errout :
return err ;
}
2009-03-25 20:26:30 +03:00
/**
* nla_policy_len - Determin the max . length of a policy
* @ policy : policy to use
* @ n : number of policies
*
* Determines the max . length of the policy . It is currently used
* to allocated Netlink buffers roughly the size of the actual
* message .
*
* Returns 0 on success or a negative error code .
*/
int
nla_policy_len ( const struct nla_policy * p , int n )
{
int i , len = 0 ;
for ( i = 0 ; i < n ; i + + ) {
if ( p - > len )
len + = nla_total_size ( p - > len ) ;
else if ( nla_attr_minlen [ p - > type ] )
len + = nla_total_size ( nla_attr_minlen [ p - > type ] ) ;
}
return len ;
}
2005-11-10 04:25:51 +03:00
/**
* nla_parse - Parse a stream of attributes into a tb buffer
* @ tb : destination array with maxtype + 1 elements
* @ maxtype : maximum attribute type to be expected
* @ head : head of attribute stream
* @ len : length of attribute stream
2008-06-28 07:02:14 +04:00
* @ policy : validation policy
2005-11-10 04:25:51 +03:00
*
* Parses a stream of attributes and stores a pointer to each attribute in
* the tb array accessable via the attribute type . Attributes with a type
* exceeding maxtype will be silently ignored for backwards compatibility
* reasons . policy may be set to NULL if no validation is required .
*
* Returns 0 on success or a negative error code .
*/
int nla_parse ( struct nlattr * tb [ ] , int maxtype , struct nlattr * head , int len ,
2007-06-05 23:38:30 +04:00
const struct nla_policy * policy )
2005-11-10 04:25:51 +03:00
{
struct nlattr * nla ;
int rem , err ;
memset ( tb , 0 , sizeof ( struct nlattr * ) * ( maxtype + 1 ) ) ;
nla_for_each_attr ( nla , head , len , rem ) {
2007-09-12 16:44:36 +04:00
u16 type = nla_type ( nla ) ;
2005-11-10 04:25:51 +03:00
if ( type > 0 & & type < = maxtype ) {
if ( policy ) {
err = validate_nla ( nla , maxtype , policy ) ;
if ( err < 0 )
goto errout ;
}
tb [ type ] = nla ;
}
}
if ( unlikely ( rem > 0 ) )
printk ( KERN_WARNING " netlink: %d bytes leftover after parsing "
" attributes. \n " , rem ) ;
err = 0 ;
errout :
return err ;
}
/**
* nla_find - Find a specific attribute in a stream of attributes
* @ head : head of attribute stream
* @ len : length of attribute stream
* @ attrtype : type of attribute to look for
*
* Returns the first attribute in the stream matching the specified type .
*/
struct nlattr * nla_find ( struct nlattr * head , int len , int attrtype )
{
struct nlattr * nla ;
int rem ;
nla_for_each_attr ( nla , head , len , rem )
2007-09-12 16:44:36 +04:00
if ( nla_type ( nla ) = = attrtype )
2005-11-10 04:25:51 +03:00
return nla ;
return NULL ;
}
/**
* nla_strlcpy - Copy string attribute payload into a sized buffer
* @ dst : where to copy the string to
2008-06-28 07:02:14 +04:00
* @ nla : attribute to copy the string from
2005-11-10 04:25:51 +03:00
* @ dstsize : size of destination buffer
*
* Copies at most dstsize - 1 bytes into the destination buffer .
* The result is always a valid NUL - terminated string . Unlike
* strlcpy the destination buffer is always padded out .
*
* Returns the length of the source buffer .
*/
size_t nla_strlcpy ( char * dst , const struct nlattr * nla , size_t dstsize )
{
size_t srclen = nla_len ( nla ) ;
char * src = nla_data ( nla ) ;
if ( srclen > 0 & & src [ srclen - 1 ] = = ' \0 ' )
srclen - - ;
if ( dstsize > 0 ) {
size_t len = ( srclen > = dstsize ) ? dstsize - 1 : srclen ;
memset ( dst , 0 , dstsize ) ;
memcpy ( dst , src , len ) ;
}
return srclen ;
}
/**
* nla_memcpy - Copy a netlink attribute into another memory area
* @ dest : where to copy to memcpy
* @ src : netlink attribute to copy from
* @ count : size of the destination area
*
* Note : The number of bytes copied is limited by the length of
* attribute ' s payload . memcpy
*
* Returns the number of bytes copied .
*/
2008-10-28 21:59:11 +03:00
int nla_memcpy ( void * dest , const struct nlattr * src , int count )
2005-11-10 04:25:51 +03:00
{
int minlen = min_t ( int , count , nla_len ( src ) ) ;
memcpy ( dest , nla_data ( src ) , minlen ) ;
return minlen ;
}
/**
* nla_memcmp - Compare an attribute with sized memory area
* @ nla : netlink attribute
* @ data : memory area
* @ size : size of memory area
*/
int nla_memcmp ( const struct nlattr * nla , const void * data ,
size_t size )
{
int d = nla_len ( nla ) - size ;
if ( d = = 0 )
d = memcmp ( nla_data ( nla ) , data , size ) ;
return d ;
}
/**
* nla_strcmp - Compare a string attribute against a string
* @ nla : netlink string attribute
* @ str : another string
*/
int nla_strcmp ( const struct nlattr * nla , const char * str )
{
int len = strlen ( str ) + 1 ;
int d = nla_len ( nla ) - len ;
if ( d = = 0 )
d = memcmp ( nla_data ( nla ) , str , len ) ;
return d ;
}
2009-03-11 18:18:32 +03:00
# ifdef CONFIG_NET
2005-11-10 04:25:51 +03:00
/**
* __nla_reserve - reserve room for attribute on the skb
* @ skb : socket buffer to reserve room on
* @ attrtype : attribute type
* @ attrlen : length of attribute payload
*
* Adds a netlink attribute header to a socket buffer and reserves
* room for the payload but does not copy it .
*
* The caller is responsible to ensure that the skb provides enough
* tailroom for the attribute header and payload .
*/
struct nlattr * __nla_reserve ( struct sk_buff * skb , int attrtype , int attrlen )
{
struct nlattr * nla ;
nla = ( struct nlattr * ) skb_put ( skb , nla_total_size ( attrlen ) ) ;
nla - > nla_type = attrtype ;
nla - > nla_len = nla_attr_size ( attrlen ) ;
memset ( ( unsigned char * ) nla + nla - > nla_len , 0 , nla_padlen ( attrlen ) ) ;
return nla ;
}
2009-03-11 18:18:32 +03:00
EXPORT_SYMBOL ( __nla_reserve ) ;
2005-11-10 04:25:51 +03:00
2006-08-05 10:03:05 +04:00
/**
* __nla_reserve_nohdr - reserve room for attribute without header
* @ skb : socket buffer to reserve room on
* @ attrlen : length of attribute payload
*
* Reserves room for attribute payload without a header .
*
* The caller is responsible to ensure that the skb provides enough
* tailroom for the payload .
*/
void * __nla_reserve_nohdr ( struct sk_buff * skb , int attrlen )
{
void * start ;
start = skb_put ( skb , NLA_ALIGN ( attrlen ) ) ;
memset ( start , 0 , NLA_ALIGN ( attrlen ) ) ;
return start ;
}
2009-03-11 18:18:32 +03:00
EXPORT_SYMBOL ( __nla_reserve_nohdr ) ;
2006-08-05 10:03:05 +04:00
2005-11-10 04:25:51 +03:00
/**
* nla_reserve - reserve room for attribute on the skb
* @ skb : socket buffer to reserve room on
* @ attrtype : attribute type
* @ attrlen : length of attribute payload
*
* Adds a netlink attribute header to a socket buffer and reserves
* room for the payload but does not copy it .
*
* Returns NULL if the tailroom of the skb is insufficient to store
* the attribute header and payload .
*/
struct nlattr * nla_reserve ( struct sk_buff * skb , int attrtype , int attrlen )
{
if ( unlikely ( skb_tailroom ( skb ) < nla_total_size ( attrlen ) ) )
return NULL ;
return __nla_reserve ( skb , attrtype , attrlen ) ;
}
2009-03-11 18:18:32 +03:00
EXPORT_SYMBOL ( nla_reserve ) ;
2005-11-10 04:25:51 +03:00
2006-08-05 10:03:05 +04:00
/**
2008-06-28 07:02:14 +04:00
* nla_reserve_nohdr - reserve room for attribute without header
2006-08-05 10:03:05 +04:00
* @ skb : socket buffer to reserve room on
2008-06-28 07:02:14 +04:00
* @ attrlen : length of attribute payload
2006-08-05 10:03:05 +04:00
*
* Reserves room for attribute payload without a header .
*
* Returns NULL if the tailroom of the skb is insufficient to store
* the attribute payload .
*/
void * nla_reserve_nohdr ( struct sk_buff * skb , int attrlen )
{
if ( unlikely ( skb_tailroom ( skb ) < NLA_ALIGN ( attrlen ) ) )
return NULL ;
return __nla_reserve_nohdr ( skb , attrlen ) ;
}
2009-03-11 18:18:32 +03:00
EXPORT_SYMBOL ( nla_reserve_nohdr ) ;
2006-08-05 10:03:05 +04:00
2005-11-10 04:25:51 +03:00
/**
* __nla_put - Add a netlink attribute to a socket buffer
* @ skb : socket buffer to add attribute to
* @ attrtype : attribute type
* @ attrlen : length of attribute payload
* @ data : head of attribute payload
*
* The caller is responsible to ensure that the skb provides enough
* tailroom for the attribute header and payload .
*/
void __nla_put ( struct sk_buff * skb , int attrtype , int attrlen ,
const void * data )
{
struct nlattr * nla ;
nla = __nla_reserve ( skb , attrtype , attrlen ) ;
memcpy ( nla_data ( nla ) , data , attrlen ) ;
}
2009-03-11 18:18:32 +03:00
EXPORT_SYMBOL ( __nla_put ) ;
2005-11-10 04:25:51 +03:00
2006-08-05 10:03:05 +04:00
/**
* __nla_put_nohdr - Add a netlink attribute without header
* @ skb : socket buffer to add attribute to
* @ attrlen : length of attribute payload
* @ data : head of attribute payload
*
* The caller is responsible to ensure that the skb provides enough
* tailroom for the attribute payload .
*/
void __nla_put_nohdr ( struct sk_buff * skb , int attrlen , const void * data )
{
void * start ;
start = __nla_reserve_nohdr ( skb , attrlen ) ;
memcpy ( start , data , attrlen ) ;
}
2009-03-11 18:18:32 +03:00
EXPORT_SYMBOL ( __nla_put_nohdr ) ;
2005-11-10 04:25:51 +03:00
/**
* nla_put - Add a netlink attribute to a socket buffer
* @ skb : socket buffer to add attribute to
* @ attrtype : attribute type
* @ attrlen : length of attribute payload
* @ data : head of attribute payload
*
2008-06-04 03:36:54 +04:00
* Returns - EMSGSIZE if the tailroom of the skb is insufficient to store
2005-11-10 04:25:51 +03:00
* the attribute header and payload .
*/
int nla_put ( struct sk_buff * skb , int attrtype , int attrlen , const void * data )
{
if ( unlikely ( skb_tailroom ( skb ) < nla_total_size ( attrlen ) ) )
2008-06-04 03:36:54 +04:00
return - EMSGSIZE ;
2005-11-10 04:25:51 +03:00
__nla_put ( skb , attrtype , attrlen , data ) ;
return 0 ;
}
2009-03-11 18:18:32 +03:00
EXPORT_SYMBOL ( nla_put ) ;
2005-11-10 04:25:51 +03:00
2006-08-05 10:03:05 +04:00
/**
* nla_put_nohdr - Add a netlink attribute without header
* @ skb : socket buffer to add attribute to
* @ attrlen : length of attribute payload
* @ data : head of attribute payload
*
2008-06-04 03:36:54 +04:00
* Returns - EMSGSIZE if the tailroom of the skb is insufficient to store
2006-08-05 10:03:05 +04:00
* the attribute payload .
*/
int nla_put_nohdr ( struct sk_buff * skb , int attrlen , const void * data )
{
if ( unlikely ( skb_tailroom ( skb ) < NLA_ALIGN ( attrlen ) ) )
2008-06-04 03:36:54 +04:00
return - EMSGSIZE ;
2006-08-05 10:03:05 +04:00
__nla_put_nohdr ( skb , attrlen , data ) ;
return 0 ;
}
2009-03-11 18:18:32 +03:00
EXPORT_SYMBOL ( nla_put_nohdr ) ;
2005-11-10 04:25:51 +03:00
2008-01-23 09:10:59 +03:00
/**
* nla_append - Add a netlink attribute without header or padding
* @ skb : socket buffer to add attribute to
* @ attrlen : length of attribute payload
* @ data : head of attribute payload
*
2008-06-04 03:36:54 +04:00
* Returns - EMSGSIZE if the tailroom of the skb is insufficient to store
2008-01-23 09:10:59 +03:00
* the attribute payload .
*/
int nla_append ( struct sk_buff * skb , int attrlen , const void * data )
{
if ( unlikely ( skb_tailroom ( skb ) < NLA_ALIGN ( attrlen ) ) )
2008-06-04 03:36:54 +04:00
return - EMSGSIZE ;
2008-01-23 09:10:59 +03:00
memcpy ( skb_put ( skb , attrlen ) , data , attrlen ) ;
return 0 ;
}
2009-03-11 18:18:32 +03:00
EXPORT_SYMBOL ( nla_append ) ;
# endif
2008-01-23 09:10:59 +03:00
2005-11-10 04:25:51 +03:00
EXPORT_SYMBOL ( nla_validate ) ;
2009-03-25 20:26:30 +03:00
EXPORT_SYMBOL ( nla_policy_len ) ;
2005-11-10 04:25:51 +03:00
EXPORT_SYMBOL ( nla_parse ) ;
EXPORT_SYMBOL ( nla_find ) ;
EXPORT_SYMBOL ( nla_strlcpy ) ;
EXPORT_SYMBOL ( nla_memcpy ) ;
EXPORT_SYMBOL ( nla_memcmp ) ;
EXPORT_SYMBOL ( nla_strcmp ) ;