@ -1,6 +1,7 @@
# include <linux/types.h>
# include <linux/skbuff.h>
# include <linux/socket.h>
# include <linux/sysctl.h>
# include <linux/net.h>
# include <linux/module.h>
# include <linux/if_arp.h>
@ -31,6 +32,9 @@ struct mpls_route { /* next hop label forwarding entry */
u8 rt_via [ 0 ] ;
} ;
static int zero = 0 ;
static int label_limit = ( 1 < < 20 ) - 1 ;
static struct mpls_route * mpls_route_input_rcu ( struct net * net , unsigned index )
{
struct mpls_route * rt = NULL ;
@ -273,18 +277,160 @@ static struct notifier_block mpls_dev_notifier = {
. notifier_call = mpls_dev_notify ,
} ;
static int resize_platform_label_table ( struct net * net , size_t limit )
{
size_t size = sizeof ( struct mpls_route * ) * limit ;
size_t old_limit ;
size_t cp_size ;
struct mpls_route __rcu * * labels = NULL , * * old ;
struct mpls_route * rt0 = NULL , * rt2 = NULL ;
unsigned index ;
if ( size ) {
labels = kzalloc ( size , GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY ) ;
if ( ! labels )
labels = vzalloc ( size ) ;
if ( ! labels )
goto nolabels ;
}
/* In case the predefined labels need to be populated */
if ( limit > LABEL_IPV4_EXPLICIT_NULL ) {
struct net_device * lo = net - > loopback_dev ;
rt0 = mpls_rt_alloc ( lo - > addr_len ) ;
if ( ! rt0 )
goto nort0 ;
rt0 - > rt_dev = lo ;
rt0 - > rt_protocol = RTPROT_KERNEL ;
rt0 - > rt_via_family = AF_PACKET ;
memcpy ( rt0 - > rt_via , lo - > dev_addr , lo - > addr_len ) ;
}
if ( limit > LABEL_IPV6_EXPLICIT_NULL ) {
struct net_device * lo = net - > loopback_dev ;
rt2 = mpls_rt_alloc ( lo - > addr_len ) ;
if ( ! rt2 )
goto nort2 ;
rt2 - > rt_dev = lo ;
rt2 - > rt_protocol = RTPROT_KERNEL ;
rt2 - > rt_via_family = AF_PACKET ;
memcpy ( rt2 - > rt_via , lo - > dev_addr , lo - > addr_len ) ;
}
rtnl_lock ( ) ;
/* Remember the original table */
old = net - > mpls . platform_label ;
old_limit = net - > mpls . platform_labels ;
/* Free any labels beyond the new table */
for ( index = limit ; index < old_limit ; index + + )
mpls_route_update ( net , index , NULL , NULL , NULL ) ;
/* Copy over the old labels */
cp_size = size ;
if ( old_limit < limit )
cp_size = old_limit * sizeof ( struct mpls_route * ) ;
memcpy ( labels , old , cp_size ) ;
/* If needed set the predefined labels */
if ( ( old_limit < = LABEL_IPV6_EXPLICIT_NULL ) & &
( limit > LABEL_IPV6_EXPLICIT_NULL ) ) {
labels [ LABEL_IPV6_EXPLICIT_NULL ] = rt2 ;
rt2 = NULL ;
}
if ( ( old_limit < = LABEL_IPV4_EXPLICIT_NULL ) & &
( limit > LABEL_IPV4_EXPLICIT_NULL ) ) {
labels [ LABEL_IPV4_EXPLICIT_NULL ] = rt0 ;
rt0 = NULL ;
}
/* Update the global pointers */
net - > mpls . platform_labels = limit ;
net - > mpls . platform_label = labels ;
rtnl_unlock ( ) ;
mpls_rt_free ( rt2 ) ;
mpls_rt_free ( rt0 ) ;
if ( old ) {
synchronize_rcu ( ) ;
kvfree ( old ) ;
}
return 0 ;
nort2 :
mpls_rt_free ( rt0 ) ;
nort0 :
kvfree ( labels ) ;
nolabels :
return - ENOMEM ;
}
static int mpls_platform_labels ( struct ctl_table * table , int write ,
void __user * buffer , size_t * lenp , loff_t * ppos )
{
struct net * net = table - > data ;
int platform_labels = net - > mpls . platform_labels ;
int ret ;
struct ctl_table tmp = {
. procname = table - > procname ,
. data = & platform_labels ,
. maxlen = sizeof ( int ) ,
. mode = table - > mode ,
. extra1 = & zero ,
. extra2 = & label_limit ,
} ;
ret = proc_dointvec_minmax ( & tmp , write , buffer , lenp , ppos ) ;
if ( write & & ret = = 0 )
ret = resize_platform_label_table ( net , platform_labels ) ;
return ret ;
}
static struct ctl_table mpls_table [ ] = {
{
. procname = " platform_labels " ,
. data = NULL ,
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
. proc_handler = mpls_platform_labels ,
} ,
{ }
} ;
static int mpls_net_init ( struct net * net )
{
struct ctl_table * table ;
net - > mpls . platform_labels = 0 ;
net - > mpls . platform_label = NULL ;
table = kmemdup ( mpls_table , sizeof ( mpls_table ) , GFP_KERNEL ) ;
if ( table = = NULL )
return - ENOMEM ;
table [ 0 ] . data = net ;
net - > mpls . ctl = register_net_sysctl ( net , " net/mpls " , table ) ;
if ( net - > mpls . ctl = = NULL )
return - ENOMEM ;
return 0 ;
}
static void mpls_net_exit ( struct net * net )
{
struct ctl_table * table ;
unsigned int index ;
table = net - > mpls . ctl - > ctl_table_arg ;
unregister_net_sysctl_table ( net - > mpls . ctl ) ;
kfree ( table ) ;
/* An rcu grace period haselapsed since there was a device in
* the network namespace ( and thus the last in fqlight packet )
* left this network namespace . This is because