@ -106,6 +106,8 @@ struct vxlan_dev {
__be32 gaddr ; /* multicast group */
__be32 saddr ; /* source address */
unsigned int link ; /* link to multicast over */
__u16 port_min ; /* source port range */
__u16 port_max ;
__u8 tos ; /* TOS override */
__u8 ttl ;
bool learn ;
@ -654,12 +656,29 @@ static void vxlan_set_owner(struct net_device *dev, struct sk_buff *skb)
skb - > destructor = vxlan_sock_free ;
}
/* Compute source port for outgoing packet
* first choice to use L4 flow hash since it will spread
* better and maybe available from hardware
* secondary choice is to use jhash on the Ethernet header
*/
static u16 vxlan_src_port ( const struct vxlan_dev * vxlan , struct sk_buff * skb )
{
unsigned int range = ( vxlan - > port_max - vxlan - > port_min ) + 1 ;
u32 hash ;
hash = skb_get_rxhash ( skb ) ;
if ( ! hash )
hash = jhash ( skb - > data , 2 * ETH_ALEN ,
( __force u32 ) skb - > protocol ) ;
return ( ( ( u64 ) hash * range ) > > 32 ) + vxlan - > port_min ;
}
/* Transmit local packets over Vxlan
*
* Outer IP header inherits ECN and DF from inner header .
* Outer UDP destination is the VXLAN assigned port .
* source port is based on hash of flow if available
* otherwise use a random value
* source port is based on hash of flow
*/
static netdev_tx_t vxlan_xmit ( struct sk_buff * skb , struct net_device * dev )
{
@ -671,8 +690,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
struct udphdr * uh ;
struct flowi4 fl4 ;
unsigned int pkt_len = skb - > len ;
u32 hash ;
__be32 dst ;
__u16 src_port ;
__be16 df = 0 ;
__u8 tos , ttl ;
int err ;
@ -695,7 +714,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
if ( tos = = 1 )
tos = vxlan_get_dsfield ( old_iph , skb ) ;
hash = skb_get_rxhash ( skb ) ;
src_port = vxlan_src_port ( vxlan , skb ) ;
memset ( & fl4 , 0 , sizeof ( fl4 ) ) ;
fl4 . flowi4_oif = vxlan - > link ;
@ -732,7 +751,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
uh = udp_hdr ( skb ) ;
uh - > dest = htons ( vxlan_port ) ;
uh - > source = hash ? : random32 ( ) ;
uh - > source = htons ( src_port ) ;
uh - > len = htons ( skb - > len ) ;
uh - > check = 0 ;
@ -960,6 +979,7 @@ static void vxlan_setup(struct net_device *dev)
{
struct vxlan_dev * vxlan = netdev_priv ( dev ) ;
unsigned h ;
int low , high ;
eth_hw_addr_random ( dev ) ;
ether_setup ( dev ) ;
@ -979,6 +999,10 @@ static void vxlan_setup(struct net_device *dev)
vxlan - > age_timer . function = vxlan_cleanup ;
vxlan - > age_timer . data = ( unsigned long ) vxlan ;
inet_get_local_port_range ( & low , & high ) ;
vxlan - > port_min = low ;
vxlan - > port_max = high ;
vxlan - > dev = dev ;
for ( h = 0 ; h < FDB_HASH_SIZE ; + + h )
@ -995,6 +1019,7 @@ static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = {
[ IFLA_VXLAN_LEARNING ] = { . type = NLA_U8 } ,
[ IFLA_VXLAN_AGEING ] = { . type = NLA_U32 } ,
[ IFLA_VXLAN_LIMIT ] = { . type = NLA_U32 } ,
[ IFLA_VXLAN_PORT_RANGE ] = { . len = sizeof ( struct ifla_vxlan_port_range ) } ,
} ;
static int vxlan_validate ( struct nlattr * tb [ ] , struct nlattr * data [ ] )
@ -1027,6 +1052,18 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[])
return - EADDRNOTAVAIL ;
}
}
if ( data [ IFLA_VXLAN_PORT_RANGE ] ) {
const struct ifla_vxlan_port_range * p
= nla_data ( data [ IFLA_VXLAN_PORT_RANGE ] ) ;
if ( ntohs ( p - > high ) < ntohs ( p - > low ) ) {
pr_debug ( " port range %u .. %u not valid \n " ,
ntohs ( p - > low ) , ntohs ( p - > high ) ) ;
return - EINVAL ;
}
}
return 0 ;
}
@ -1077,6 +1114,13 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
if ( data [ IFLA_VXLAN_LIMIT ] )
vxlan - > addrmax = nla_get_u32 ( data [ IFLA_VXLAN_LIMIT ] ) ;
if ( data [ IFLA_VXLAN_PORT_RANGE ] ) {
const struct ifla_vxlan_port_range * p
= nla_data ( data [ IFLA_VXLAN_PORT_RANGE ] ) ;
vxlan - > port_min = ntohs ( p - > low ) ;
vxlan - > port_max = ntohs ( p - > high ) ;
}
err = register_netdevice ( dev ) ;
if ( ! err )
hlist_add_head_rcu ( & vxlan - > hlist , vni_head ( net , vxlan - > vni ) ) ;
@ -1105,12 +1149,17 @@ static size_t vxlan_get_size(const struct net_device *dev)
nla_total_size ( sizeof ( __u8 ) ) + /* IFLA_VXLAN_LEARNING */
nla_total_size ( sizeof ( __u32 ) ) + /* IFLA_VXLAN_AGEING */
nla_total_size ( sizeof ( __u32 ) ) + /* IFLA_VXLAN_LIMIT */
nla_total_size ( sizeof ( struct ifla_vxlan_port_range ) ) +
0 ;
}
static int vxlan_fill_info ( struct sk_buff * skb , const struct net_device * dev )
{
const struct vxlan_dev * vxlan = netdev_priv ( dev ) ;
struct ifla_vxlan_port_range ports = {
. low = htons ( vxlan - > port_min ) ,
. high = htons ( vxlan - > port_max ) ,
} ;
if ( nla_put_u32 ( skb , IFLA_VXLAN_ID , vxlan - > vni ) )
goto nla_put_failure ;
@ -1131,6 +1180,9 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
nla_put_u32 ( skb , IFLA_VXLAN_LIMIT , vxlan - > addrmax ) )
goto nla_put_failure ;
if ( nla_put ( skb , IFLA_VXLAN_PORT_RANGE , sizeof ( ports ) , & ports ) )
goto nla_put_failure ;
return 0 ;
nla_put_failure :