|
@@ -2623,6 +2623,10 @@ static void vxlan_setup(struct net_device *dev)
|
|
netif_keep_dst(dev);
|
|
netif_keep_dst(dev);
|
|
dev->priv_flags |= IFF_NO_QUEUE;
|
|
dev->priv_flags |= IFF_NO_QUEUE;
|
|
|
|
|
|
|
|
+ /* MTU range: 68 - 65535 */
|
|
|
|
+ dev->min_mtu = ETH_MIN_MTU;
|
|
|
|
+ dev->max_mtu = ETH_MAX_MTU;
|
|
|
|
+
|
|
INIT_LIST_HEAD(&vxlan->next);
|
|
INIT_LIST_HEAD(&vxlan->next);
|
|
spin_lock_init(&vxlan->hash_lock);
|
|
spin_lock_init(&vxlan->hash_lock);
|
|
|
|
|
|
@@ -2630,9 +2634,8 @@ static void vxlan_setup(struct net_device *dev)
|
|
vxlan->age_timer.function = vxlan_cleanup;
|
|
vxlan->age_timer.function = vxlan_cleanup;
|
|
vxlan->age_timer.data = (unsigned long) vxlan;
|
|
vxlan->age_timer.data = (unsigned long) vxlan;
|
|
|
|
|
|
- vxlan->cfg.dst_port = htons(vxlan_port);
|
|
|
|
-
|
|
|
|
vxlan->dev = dev;
|
|
vxlan->dev = dev;
|
|
|
|
+ vxlan->net = dev_net(dev);
|
|
|
|
|
|
gro_cells_init(&vxlan->gro_cells, dev);
|
|
gro_cells_init(&vxlan->gro_cells, dev);
|
|
|
|
|
|
@@ -2701,11 +2704,19 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (tb[IFLA_MTU]) {
|
|
|
|
+ u32 mtu = nla_get_u32(data[IFLA_MTU]);
|
|
|
|
+
|
|
|
|
+ if (mtu < ETH_MIN_MTU || mtu > ETH_MAX_MTU)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (!data)
|
|
if (!data)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
if (data[IFLA_VXLAN_ID]) {
|
|
if (data[IFLA_VXLAN_ID]) {
|
|
- __u32 id = nla_get_u32(data[IFLA_VXLAN_ID]);
|
|
|
|
|
|
+ u32 id = nla_get_u32(data[IFLA_VXLAN_ID]);
|
|
|
|
+
|
|
if (id >= VXLAN_N_VID)
|
|
if (id >= VXLAN_N_VID)
|
|
return -ERANGE;
|
|
return -ERANGE;
|
|
}
|
|
}
|
|
@@ -2866,116 +2877,128 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
|
|
|
|
- struct vxlan_config *conf,
|
|
|
|
- bool changelink)
|
|
|
|
|
|
+static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf,
|
|
|
|
+ struct net_device **lower,
|
|
|
|
+ struct vxlan_dev *old)
|
|
{
|
|
{
|
|
struct vxlan_net *vn = net_generic(src_net, vxlan_net_id);
|
|
struct vxlan_net *vn = net_generic(src_net, vxlan_net_id);
|
|
- struct vxlan_dev *vxlan = netdev_priv(dev), *tmp;
|
|
|
|
- struct vxlan_rdst *dst = &vxlan->default_dst;
|
|
|
|
- unsigned short needed_headroom = ETH_HLEN;
|
|
|
|
|
|
+ struct vxlan_dev *tmp;
|
|
bool use_ipv6 = false;
|
|
bool use_ipv6 = false;
|
|
- __be16 default_port = vxlan->cfg.dst_port;
|
|
|
|
- struct net_device *lowerdev = NULL;
|
|
|
|
|
|
|
|
- if (!changelink) {
|
|
|
|
- if (conf->flags & VXLAN_F_GPE) {
|
|
|
|
- /* For now, allow GPE only together with
|
|
|
|
- * COLLECT_METADATA. This can be relaxed later; in such
|
|
|
|
- * case, the other side of the PtP link will have to be
|
|
|
|
- * provided.
|
|
|
|
- */
|
|
|
|
- if ((conf->flags & ~VXLAN_F_ALLOWED_GPE) ||
|
|
|
|
- !(conf->flags & VXLAN_F_COLLECT_METADATA)) {
|
|
|
|
- pr_info("unsupported combination of extensions\n");
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
- vxlan_raw_setup(dev);
|
|
|
|
- } else {
|
|
|
|
- vxlan_ether_setup(dev);
|
|
|
|
|
|
+ if (conf->flags & VXLAN_F_GPE) {
|
|
|
|
+ /* For now, allow GPE only together with
|
|
|
|
+ * COLLECT_METADATA. This can be relaxed later; in such
|
|
|
|
+ * case, the other side of the PtP link will have to be
|
|
|
|
+ * provided.
|
|
|
|
+ */
|
|
|
|
+ if ((conf->flags & ~VXLAN_F_ALLOWED_GPE) ||
|
|
|
|
+ !(conf->flags & VXLAN_F_COLLECT_METADATA)) {
|
|
|
|
+ return -EINVAL;
|
|
}
|
|
}
|
|
-
|
|
|
|
- /* MTU range: 68 - 65535 */
|
|
|
|
- dev->min_mtu = ETH_MIN_MTU;
|
|
|
|
- dev->max_mtu = ETH_MAX_MTU;
|
|
|
|
- vxlan->net = src_net;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- dst->remote_vni = conf->vni;
|
|
|
|
-
|
|
|
|
- memcpy(&dst->remote_ip, &conf->remote_ip, sizeof(conf->remote_ip));
|
|
|
|
-
|
|
|
|
- /* Unless IPv6 is explicitly requested, assume IPv4 */
|
|
|
|
- if (!dst->remote_ip.sa.sa_family)
|
|
|
|
- dst->remote_ip.sa.sa_family = AF_INET;
|
|
|
|
|
|
+ if (!conf->remote_ip.sa.sa_family)
|
|
|
|
+ conf->remote_ip.sa.sa_family = AF_INET;
|
|
|
|
|
|
- if (dst->remote_ip.sa.sa_family == AF_INET6 ||
|
|
|
|
- vxlan->cfg.saddr.sa.sa_family == AF_INET6) {
|
|
|
|
|
|
+ if (conf->remote_ip.sa.sa_family == AF_INET6 ||
|
|
|
|
+ conf->saddr.sa.sa_family == AF_INET6) {
|
|
if (!IS_ENABLED(CONFIG_IPV6))
|
|
if (!IS_ENABLED(CONFIG_IPV6))
|
|
return -EPFNOSUPPORT;
|
|
return -EPFNOSUPPORT;
|
|
use_ipv6 = true;
|
|
use_ipv6 = true;
|
|
- vxlan->flags |= VXLAN_F_IPV6;
|
|
|
|
|
|
+ conf->flags |= VXLAN_F_IPV6;
|
|
}
|
|
}
|
|
|
|
|
|
- if (conf->label && !use_ipv6) {
|
|
|
|
- pr_info("label only supported in use with IPv6\n");
|
|
|
|
|
|
+ if (conf->label && !use_ipv6)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
- }
|
|
|
|
|
|
|
|
- if (conf->remote_ifindex &&
|
|
|
|
- conf->remote_ifindex != vxlan->cfg.remote_ifindex) {
|
|
|
|
- lowerdev = __dev_get_by_index(src_net, conf->remote_ifindex);
|
|
|
|
- dst->remote_ifindex = conf->remote_ifindex;
|
|
|
|
|
|
+ if (conf->remote_ifindex) {
|
|
|
|
+ struct net_device *lowerdev;
|
|
|
|
|
|
- if (!lowerdev) {
|
|
|
|
- pr_info("ifindex %d does not exist\n",
|
|
|
|
- dst->remote_ifindex);
|
|
|
|
|
|
+ lowerdev = __dev_get_by_index(src_net, conf->remote_ifindex);
|
|
|
|
+ if (!lowerdev)
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
- }
|
|
|
|
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
if (use_ipv6) {
|
|
if (use_ipv6) {
|
|
struct inet6_dev *idev = __in6_dev_get(lowerdev);
|
|
struct inet6_dev *idev = __in6_dev_get(lowerdev);
|
|
- if (idev && idev->cnf.disable_ipv6) {
|
|
|
|
- pr_info("IPv6 is disabled via sysctl\n");
|
|
|
|
|
|
+ if (idev && idev->cnf.disable_ipv6)
|
|
return -EPERM;
|
|
return -EPERM;
|
|
- }
|
|
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
- if (!conf->mtu)
|
|
|
|
- dev->mtu = lowerdev->mtu -
|
|
|
|
- (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM);
|
|
|
|
|
|
+ *lower = lowerdev;
|
|
|
|
+ } else {
|
|
|
|
+ if (vxlan_addr_multicast(&conf->remote_ip))
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- needed_headroom = lowerdev->hard_header_len;
|
|
|
|
- } else if (!conf->remote_ifindex &&
|
|
|
|
- vxlan_addr_multicast(&dst->remote_ip)) {
|
|
|
|
- pr_info("multicast destination requires interface to be specified\n");
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ *lower = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
- if (lowerdev) {
|
|
|
|
- dev->gso_max_size = lowerdev->gso_max_size;
|
|
|
|
- dev->gso_max_segs = lowerdev->gso_max_segs;
|
|
|
|
|
|
+ if (!conf->dst_port) {
|
|
|
|
+ if (conf->flags & VXLAN_F_GPE)
|
|
|
|
+ conf->dst_port = htons(4790); /* IANA VXLAN-GPE port */
|
|
|
|
+ else
|
|
|
|
+ conf->dst_port = htons(vxlan_port);
|
|
}
|
|
}
|
|
|
|
|
|
- if (conf->mtu) {
|
|
|
|
- int max_mtu = ETH_MAX_MTU;
|
|
|
|
|
|
+ if (!conf->age_interval)
|
|
|
|
+ conf->age_interval = FDB_AGE_DEFAULT;
|
|
|
|
|
|
- if (lowerdev)
|
|
|
|
- max_mtu = lowerdev->mtu;
|
|
|
|
|
|
+ list_for_each_entry(tmp, &vn->vxlan_list, next) {
|
|
|
|
+ if (tmp == old)
|
|
|
|
+ continue;
|
|
|
|
|
|
- max_mtu -= (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM);
|
|
|
|
|
|
+ if (tmp->cfg.vni == conf->vni &&
|
|
|
|
+ (tmp->default_dst.remote_ip.sa.sa_family == AF_INET6 ||
|
|
|
|
+ tmp->cfg.saddr.sa.sa_family == AF_INET6) == use_ipv6 &&
|
|
|
|
+ tmp->cfg.dst_port == conf->dst_port &&
|
|
|
|
+ (tmp->flags & VXLAN_F_RCV_FLAGS) ==
|
|
|
|
+ (conf->flags & VXLAN_F_RCV_FLAGS))
|
|
|
|
+ return -EEXIST;
|
|
|
|
+ }
|
|
|
|
|
|
- if (conf->mtu < dev->min_mtu || conf->mtu > dev->max_mtu)
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
|
|
- dev->mtu = conf->mtu;
|
|
|
|
|
|
+static void vxlan_config_apply(struct net_device *dev,
|
|
|
|
+ struct vxlan_config *conf,
|
|
|
|
+ struct net_device *lowerdev, bool changelink)
|
|
|
|
+{
|
|
|
|
+ struct vxlan_dev *vxlan = netdev_priv(dev);
|
|
|
|
+ struct vxlan_rdst *dst = &vxlan->default_dst;
|
|
|
|
+ unsigned short needed_headroom = ETH_HLEN;
|
|
|
|
+ bool use_ipv6 = !!(conf->flags & VXLAN_F_IPV6);
|
|
|
|
+ int max_mtu = ETH_MAX_MTU;
|
|
|
|
+
|
|
|
|
+ if (!changelink) {
|
|
|
|
+ if (conf->flags & VXLAN_F_GPE)
|
|
|
|
+ vxlan_raw_setup(dev);
|
|
|
|
+ else
|
|
|
|
+ vxlan_ether_setup(dev);
|
|
|
|
|
|
- if (conf->mtu > max_mtu)
|
|
|
|
- dev->mtu = max_mtu;
|
|
|
|
|
|
+ if (conf->mtu)
|
|
|
|
+ dev->mtu = conf->mtu;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ dst->remote_vni = conf->vni;
|
|
|
|
+
|
|
|
|
+ memcpy(&dst->remote_ip, &conf->remote_ip, sizeof(conf->remote_ip));
|
|
|
|
+
|
|
|
|
+ if (lowerdev) {
|
|
|
|
+ dst->remote_ifindex = conf->remote_ifindex;
|
|
|
|
+
|
|
|
|
+ dev->gso_max_size = lowerdev->gso_max_size;
|
|
|
|
+ dev->gso_max_segs = lowerdev->gso_max_segs;
|
|
|
|
+
|
|
|
|
+ needed_headroom = lowerdev->hard_header_len;
|
|
|
|
+
|
|
|
|
+ max_mtu = lowerdev->mtu - (use_ipv6 ? VXLAN6_HEADROOM :
|
|
|
|
+ VXLAN_HEADROOM);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dev->mtu > max_mtu)
|
|
|
|
+ dev->mtu = max_mtu;
|
|
|
|
+
|
|
if (use_ipv6 || conf->flags & VXLAN_F_COLLECT_METADATA)
|
|
if (use_ipv6 || conf->flags & VXLAN_F_COLLECT_METADATA)
|
|
needed_headroom += VXLAN6_HEADROOM;
|
|
needed_headroom += VXLAN6_HEADROOM;
|
|
else
|
|
else
|
|
@@ -2983,31 +3006,22 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
|
|
dev->needed_headroom = needed_headroom;
|
|
dev->needed_headroom = needed_headroom;
|
|
|
|
|
|
memcpy(&vxlan->cfg, conf, sizeof(*conf));
|
|
memcpy(&vxlan->cfg, conf, sizeof(*conf));
|
|
- if (!vxlan->cfg.dst_port) {
|
|
|
|
- if (conf->flags & VXLAN_F_GPE)
|
|
|
|
- vxlan->cfg.dst_port = htons(4790); /* IANA VXLAN-GPE port */
|
|
|
|
- else
|
|
|
|
- vxlan->cfg.dst_port = default_port;
|
|
|
|
- }
|
|
|
|
vxlan->flags |= conf->flags;
|
|
vxlan->flags |= conf->flags;
|
|
|
|
+}
|
|
|
|
|
|
- if (!vxlan->cfg.age_interval)
|
|
|
|
- vxlan->cfg.age_interval = FDB_AGE_DEFAULT;
|
|
|
|
|
|
+static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
|
|
|
|
+ struct vxlan_config *conf,
|
|
|
|
+ bool changelink)
|
|
|
|
+{
|
|
|
|
+ struct vxlan_dev *vxlan = netdev_priv(dev);
|
|
|
|
+ struct net_device *lowerdev;
|
|
|
|
+ int ret;
|
|
|
|
|
|
- if (changelink)
|
|
|
|
- return 0;
|
|
|
|
|
|
+ ret = vxlan_config_validate(src_net, conf, &lowerdev, vxlan);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
|
|
- list_for_each_entry(tmp, &vn->vxlan_list, next) {
|
|
|
|
- if (tmp->cfg.vni == conf->vni &&
|
|
|
|
- (tmp->default_dst.remote_ip.sa.sa_family == AF_INET6 ||
|
|
|
|
- tmp->cfg.saddr.sa.sa_family == AF_INET6) == use_ipv6 &&
|
|
|
|
- tmp->cfg.dst_port == vxlan->cfg.dst_port &&
|
|
|
|
- (tmp->flags & VXLAN_F_RCV_FLAGS) ==
|
|
|
|
- (vxlan->flags & VXLAN_F_RCV_FLAGS)) {
|
|
|
|
- pr_info("duplicate VNI %u\n", be32_to_cpu(conf->vni));
|
|
|
|
- return -EEXIST;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ vxlan_config_apply(dev, conf, lowerdev, changelink);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|