diff options
Diffstat (limited to 'drivers/net/vxlan.c')
| -rw-r--r-- | drivers/net/vxlan.c | 63 | 
1 files changed, 53 insertions, 10 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index cf8b7f0473b3..c1587ece28cf 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2337,6 +2337,46 @@ static int vxlan_change_mtu(struct net_device *dev, int new_mtu)  	return 0;  } +static int egress_ipv4_tun_info(struct net_device *dev, struct sk_buff *skb, +				struct ip_tunnel_info *info, +				__be16 sport, __be16 dport) +{ +	struct vxlan_dev *vxlan = netdev_priv(dev); +	struct rtable *rt; +	struct flowi4 fl4; + +	memset(&fl4, 0, sizeof(fl4)); +	fl4.flowi4_tos = RT_TOS(info->key.tos); +	fl4.flowi4_mark = skb->mark; +	fl4.flowi4_proto = IPPROTO_UDP; +	fl4.daddr = info->key.u.ipv4.dst; + +	rt = ip_route_output_key(vxlan->net, &fl4); +	if (IS_ERR(rt)) +		return PTR_ERR(rt); +	ip_rt_put(rt); + +	info->key.u.ipv4.src = fl4.saddr; +	info->key.tp_src = sport; +	info->key.tp_dst = dport; +	return 0; +} + +static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) +{ +	struct vxlan_dev *vxlan = netdev_priv(dev); +	struct ip_tunnel_info *info = skb_tunnel_info(skb); +	__be16 sport, dport; + +	sport = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min, +				  vxlan->cfg.port_max, true); +	dport = info->key.tp_dst ? : vxlan->cfg.dst_port; + +	if (ip_tunnel_info_af(info) == AF_INET) +		return egress_ipv4_tun_info(dev, skb, info, sport, dport); +	return -EINVAL; +} +  static const struct net_device_ops vxlan_netdev_ops = {  	.ndo_init		= vxlan_init,  	.ndo_uninit		= vxlan_uninit, @@ -2351,6 +2391,7 @@ static const struct net_device_ops vxlan_netdev_ops = {  	.ndo_fdb_add		= vxlan_fdb_add,  	.ndo_fdb_del		= vxlan_fdb_delete,  	.ndo_fdb_dump		= vxlan_fdb_dump, +	.ndo_fill_metadata_dst	= vxlan_fill_metadata_dst,  };  /* Info for udev, that this is a virtual tunnel endpoint */ @@ -2392,10 +2433,6 @@ static void vxlan_setup(struct net_device *dev)  	eth_hw_addr_random(dev);  	ether_setup(dev); -	if (vxlan->default_dst.remote_ip.sa.sa_family == AF_INET6) -		dev->needed_headroom = ETH_HLEN + VXLAN6_HEADROOM; -	else -		dev->needed_headroom = ETH_HLEN + VXLAN_HEADROOM;  	dev->netdev_ops = &vxlan_netdev_ops;  	dev->destructor = free_netdev; @@ -2640,8 +2677,11 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,  		dst->remote_ip.sa.sa_family = AF_INET;  	if (dst->remote_ip.sa.sa_family == AF_INET6 || -	    vxlan->cfg.saddr.sa.sa_family == AF_INET6) +	    vxlan->cfg.saddr.sa.sa_family == AF_INET6) { +		if (!IS_ENABLED(CONFIG_IPV6)) +			return -EPFNOSUPPORT;  		use_ipv6 = true; +	}  	if (conf->remote_ifindex) {  		struct net_device *lowerdev @@ -2670,8 +2710,12 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,  		dev->needed_headroom = lowerdev->hard_header_len +  				       (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM); -	} else if (use_ipv6) +	} else if (use_ipv6) {  		vxlan->flags |= VXLAN_F_IPV6; +		dev->needed_headroom = ETH_HLEN + VXLAN6_HEADROOM; +	} else { +		dev->needed_headroom = ETH_HLEN + VXLAN_HEADROOM; +	}  	memcpy(&vxlan->cfg, conf, sizeof(*conf));  	if (!vxlan->cfg.dst_port) @@ -2742,11 +2786,10 @@ static int vxlan_newlink(struct net *src_net, struct net_device *dev,  	struct vxlan_config conf;  	int err; -	if (!data[IFLA_VXLAN_ID]) -		return -EINVAL; -  	memset(&conf, 0, sizeof(conf)); -	conf.vni = nla_get_u32(data[IFLA_VXLAN_ID]); + +	if (data[IFLA_VXLAN_ID]) +		conf.vni = nla_get_u32(data[IFLA_VXLAN_ID]);  	if (data[IFLA_VXLAN_GROUP]) {  		conf.remote_ip.sin.sin_addr.s_addr = nla_get_in_addr(data[IFLA_VXLAN_GROUP]);  | 
