Skip to content

Commit a985343

Browse files
neocturnedavem330
authored andcommitted
vxlan: refactor verification and application of configuration
The vxlan_dev_configure function was mixing validation and application of the vxlan configuration; this could easily lead to bugs with the changelink operation, as it was hard to see if the function wcould return an error after parts of the configuration had already been applied. This commit splits validation and application out of vxlan_dev_configure as separate functions to make it clearer where error returns are allowed and where the vxlan_dev or net_device may be configured. Log messages in these functions are removed, as it is generally unexpected to find error output for netlink requests in the kernel log. Userspace should be able to handle errors based on the error codes returned via netlink just fine. In addition, some validation and initialization is moved to vxlan_validate and vxlan_setup respectively to improve grouping of similar settings. Finally, this also fixes two actual bugs: * if set, conf->mtu would overwrite dev->mtu in each changelink operation, reverting other changes of dev->mtu * the "if (!conf->dst_port)" branch would never be run, as conf->dst_port was set in vxlan_setup before. This caused VXLAN-GPE to use the same default port as other VXLAN sockets instead of the intended IANA-assigned 4790. Signed-off-by: Matthias Schiffer <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent c0ca9e7 commit a985343

File tree

1 file changed

+111
-97
lines changed

1 file changed

+111
-97
lines changed

drivers/net/vxlan.c

Lines changed: 111 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -2623,16 +2623,19 @@ static void vxlan_setup(struct net_device *dev)
26232623
netif_keep_dst(dev);
26242624
dev->priv_flags |= IFF_NO_QUEUE;
26252625

2626+
/* MTU range: 68 - 65535 */
2627+
dev->min_mtu = ETH_MIN_MTU;
2628+
dev->max_mtu = ETH_MAX_MTU;
2629+
26262630
INIT_LIST_HEAD(&vxlan->next);
26272631
spin_lock_init(&vxlan->hash_lock);
26282632

26292633
init_timer_deferrable(&vxlan->age_timer);
26302634
vxlan->age_timer.function = vxlan_cleanup;
26312635
vxlan->age_timer.data = (unsigned long) vxlan;
26322636

2633-
vxlan->cfg.dst_port = htons(vxlan_port);
2634-
26352637
vxlan->dev = dev;
2638+
vxlan->net = dev_net(dev);
26362639

26372640
gro_cells_init(&vxlan->gro_cells, dev);
26382641

@@ -2701,11 +2704,19 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[])
27012704
}
27022705
}
27032706

2707+
if (tb[IFLA_MTU]) {
2708+
u32 mtu = nla_get_u32(data[IFLA_MTU]);
2709+
2710+
if (mtu < ETH_MIN_MTU || mtu > ETH_MAX_MTU)
2711+
return -EINVAL;
2712+
}
2713+
27042714
if (!data)
27052715
return -EINVAL;
27062716

27072717
if (data[IFLA_VXLAN_ID]) {
2708-
__u32 id = nla_get_u32(data[IFLA_VXLAN_ID]);
2718+
u32 id = nla_get_u32(data[IFLA_VXLAN_ID]);
2719+
27092720
if (id >= VXLAN_N_VID)
27102721
return -ERANGE;
27112722
}
@@ -2866,148 +2877,151 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan)
28662877
return ret;
28672878
}
28682879

2869-
static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
2870-
struct vxlan_config *conf,
2871-
bool changelink)
2880+
static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf,
2881+
struct net_device **lower,
2882+
struct vxlan_dev *old)
28722883
{
28732884
struct vxlan_net *vn = net_generic(src_net, vxlan_net_id);
2874-
struct vxlan_dev *vxlan = netdev_priv(dev), *tmp;
2875-
struct vxlan_rdst *dst = &vxlan->default_dst;
2876-
unsigned short needed_headroom = ETH_HLEN;
2885+
struct vxlan_dev *tmp;
28772886
bool use_ipv6 = false;
2878-
__be16 default_port = vxlan->cfg.dst_port;
2879-
struct net_device *lowerdev = NULL;
28802887

2881-
if (!changelink) {
2882-
if (conf->flags & VXLAN_F_GPE) {
2883-
/* For now, allow GPE only together with
2884-
* COLLECT_METADATA. This can be relaxed later; in such
2885-
* case, the other side of the PtP link will have to be
2886-
* provided.
2887-
*/
2888-
if ((conf->flags & ~VXLAN_F_ALLOWED_GPE) ||
2889-
!(conf->flags & VXLAN_F_COLLECT_METADATA)) {
2890-
pr_info("unsupported combination of extensions\n");
2891-
return -EINVAL;
2892-
}
2893-
vxlan_raw_setup(dev);
2894-
} else {
2895-
vxlan_ether_setup(dev);
2888+
if (conf->flags & VXLAN_F_GPE) {
2889+
/* For now, allow GPE only together with
2890+
* COLLECT_METADATA. This can be relaxed later; in such
2891+
* case, the other side of the PtP link will have to be
2892+
* provided.
2893+
*/
2894+
if ((conf->flags & ~VXLAN_F_ALLOWED_GPE) ||
2895+
!(conf->flags & VXLAN_F_COLLECT_METADATA)) {
2896+
return -EINVAL;
28962897
}
2897-
2898-
/* MTU range: 68 - 65535 */
2899-
dev->min_mtu = ETH_MIN_MTU;
2900-
dev->max_mtu = ETH_MAX_MTU;
2901-
vxlan->net = src_net;
29022898
}
29032899

2904-
dst->remote_vni = conf->vni;
2905-
2906-
memcpy(&dst->remote_ip, &conf->remote_ip, sizeof(conf->remote_ip));
2907-
2908-
/* Unless IPv6 is explicitly requested, assume IPv4 */
2909-
if (!dst->remote_ip.sa.sa_family)
2910-
dst->remote_ip.sa.sa_family = AF_INET;
2900+
if (!conf->remote_ip.sa.sa_family)
2901+
conf->remote_ip.sa.sa_family = AF_INET;
29112902

2912-
if (dst->remote_ip.sa.sa_family == AF_INET6 ||
2913-
vxlan->cfg.saddr.sa.sa_family == AF_INET6) {
2903+
if (conf->remote_ip.sa.sa_family == AF_INET6 ||
2904+
conf->saddr.sa.sa_family == AF_INET6) {
29142905
if (!IS_ENABLED(CONFIG_IPV6))
29152906
return -EPFNOSUPPORT;
29162907
use_ipv6 = true;
2917-
vxlan->flags |= VXLAN_F_IPV6;
2908+
conf->flags |= VXLAN_F_IPV6;
29182909
}
29192910

2920-
if (conf->label && !use_ipv6) {
2921-
pr_info("label only supported in use with IPv6\n");
2911+
if (conf->label && !use_ipv6)
29222912
return -EINVAL;
2923-
}
29242913

2925-
if (conf->remote_ifindex &&
2926-
conf->remote_ifindex != vxlan->cfg.remote_ifindex) {
2927-
lowerdev = __dev_get_by_index(src_net, conf->remote_ifindex);
2928-
dst->remote_ifindex = conf->remote_ifindex;
2914+
if (conf->remote_ifindex) {
2915+
struct net_device *lowerdev;
29292916

2930-
if (!lowerdev) {
2931-
pr_info("ifindex %d does not exist\n",
2932-
dst->remote_ifindex);
2917+
lowerdev = __dev_get_by_index(src_net, conf->remote_ifindex);
2918+
if (!lowerdev)
29332919
return -ENODEV;
2934-
}
29352920

29362921
#if IS_ENABLED(CONFIG_IPV6)
29372922
if (use_ipv6) {
29382923
struct inet6_dev *idev = __in6_dev_get(lowerdev);
2939-
if (idev && idev->cnf.disable_ipv6) {
2940-
pr_info("IPv6 is disabled via sysctl\n");
2924+
if (idev && idev->cnf.disable_ipv6)
29412925
return -EPERM;
2942-
}
29432926
}
29442927
#endif
29452928

2946-
if (!conf->mtu)
2947-
dev->mtu = lowerdev->mtu -
2948-
(use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM);
2929+
*lower = lowerdev;
2930+
} else {
2931+
if (vxlan_addr_multicast(&conf->remote_ip))
2932+
return -EINVAL;
29492933

2950-
needed_headroom = lowerdev->hard_header_len;
2951-
} else if (!conf->remote_ifindex &&
2952-
vxlan_addr_multicast(&dst->remote_ip)) {
2953-
pr_info("multicast destination requires interface to be specified\n");
2954-
return -EINVAL;
2934+
*lower = NULL;
29552935
}
29562936

2957-
if (lowerdev) {
2958-
dev->gso_max_size = lowerdev->gso_max_size;
2959-
dev->gso_max_segs = lowerdev->gso_max_segs;
2937+
if (!conf->dst_port) {
2938+
if (conf->flags & VXLAN_F_GPE)
2939+
conf->dst_port = htons(4790); /* IANA VXLAN-GPE port */
2940+
else
2941+
conf->dst_port = htons(vxlan_port);
29602942
}
29612943

2962-
if (conf->mtu) {
2963-
int max_mtu = ETH_MAX_MTU;
2944+
if (!conf->age_interval)
2945+
conf->age_interval = FDB_AGE_DEFAULT;
29642946

2965-
if (lowerdev)
2966-
max_mtu = lowerdev->mtu;
2947+
list_for_each_entry(tmp, &vn->vxlan_list, next) {
2948+
if (tmp == old)
2949+
continue;
29672950

2968-
max_mtu -= (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM);
2951+
if (tmp->cfg.vni == conf->vni &&
2952+
(tmp->default_dst.remote_ip.sa.sa_family == AF_INET6 ||
2953+
tmp->cfg.saddr.sa.sa_family == AF_INET6) == use_ipv6 &&
2954+
tmp->cfg.dst_port == conf->dst_port &&
2955+
(tmp->flags & VXLAN_F_RCV_FLAGS) ==
2956+
(conf->flags & VXLAN_F_RCV_FLAGS))
2957+
return -EEXIST;
2958+
}
29692959

2970-
if (conf->mtu < dev->min_mtu || conf->mtu > dev->max_mtu)
2971-
return -EINVAL;
2960+
return 0;
2961+
}
29722962

2973-
dev->mtu = conf->mtu;
2963+
static void vxlan_config_apply(struct net_device *dev,
2964+
struct vxlan_config *conf,
2965+
struct net_device *lowerdev, bool changelink)
2966+
{
2967+
struct vxlan_dev *vxlan = netdev_priv(dev);
2968+
struct vxlan_rdst *dst = &vxlan->default_dst;
2969+
unsigned short needed_headroom = ETH_HLEN;
2970+
bool use_ipv6 = !!(conf->flags & VXLAN_F_IPV6);
2971+
int max_mtu = ETH_MAX_MTU;
2972+
2973+
if (!changelink) {
2974+
if (conf->flags & VXLAN_F_GPE)
2975+
vxlan_raw_setup(dev);
2976+
else
2977+
vxlan_ether_setup(dev);
29742978

2975-
if (conf->mtu > max_mtu)
2976-
dev->mtu = max_mtu;
2979+
if (conf->mtu)
2980+
dev->mtu = conf->mtu;
29772981
}
29782982

2983+
dst->remote_vni = conf->vni;
2984+
2985+
memcpy(&dst->remote_ip, &conf->remote_ip, sizeof(conf->remote_ip));
2986+
2987+
if (lowerdev) {
2988+
dst->remote_ifindex = conf->remote_ifindex;
2989+
2990+
dev->gso_max_size = lowerdev->gso_max_size;
2991+
dev->gso_max_segs = lowerdev->gso_max_segs;
2992+
2993+
needed_headroom = lowerdev->hard_header_len;
2994+
2995+
max_mtu = lowerdev->mtu - (use_ipv6 ? VXLAN6_HEADROOM :
2996+
VXLAN_HEADROOM);
2997+
}
2998+
2999+
if (dev->mtu > max_mtu)
3000+
dev->mtu = max_mtu;
3001+
29793002
if (use_ipv6 || conf->flags & VXLAN_F_COLLECT_METADATA)
29803003
needed_headroom += VXLAN6_HEADROOM;
29813004
else
29823005
needed_headroom += VXLAN_HEADROOM;
29833006
dev->needed_headroom = needed_headroom;
29843007

29853008
memcpy(&vxlan->cfg, conf, sizeof(*conf));
2986-
if (!vxlan->cfg.dst_port) {
2987-
if (conf->flags & VXLAN_F_GPE)
2988-
vxlan->cfg.dst_port = htons(4790); /* IANA VXLAN-GPE port */
2989-
else
2990-
vxlan->cfg.dst_port = default_port;
2991-
}
29923009
vxlan->flags |= conf->flags;
3010+
}
29933011

2994-
if (!vxlan->cfg.age_interval)
2995-
vxlan->cfg.age_interval = FDB_AGE_DEFAULT;
3012+
static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
3013+
struct vxlan_config *conf,
3014+
bool changelink)
3015+
{
3016+
struct vxlan_dev *vxlan = netdev_priv(dev);
3017+
struct net_device *lowerdev;
3018+
int ret;
29963019

2997-
if (changelink)
2998-
return 0;
3020+
ret = vxlan_config_validate(src_net, conf, &lowerdev, vxlan);
3021+
if (ret)
3022+
return ret;
29993023

3000-
list_for_each_entry(tmp, &vn->vxlan_list, next) {
3001-
if (tmp->cfg.vni == conf->vni &&
3002-
(tmp->default_dst.remote_ip.sa.sa_family == AF_INET6 ||
3003-
tmp->cfg.saddr.sa.sa_family == AF_INET6) == use_ipv6 &&
3004-
tmp->cfg.dst_port == vxlan->cfg.dst_port &&
3005-
(tmp->flags & VXLAN_F_RCV_FLAGS) ==
3006-
(vxlan->flags & VXLAN_F_RCV_FLAGS)) {
3007-
pr_info("duplicate VNI %u\n", be32_to_cpu(conf->vni));
3008-
return -EEXIST;
3009-
}
3010-
}
3024+
vxlan_config_apply(dev, conf, lowerdev, changelink);
30113025

30123026
return 0;
30133027
}

0 commit comments

Comments
 (0)