|
@@ -2835,39 +2835,40 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan)
|
|
|
}
|
|
|
|
|
|
static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
|
|
|
- struct vxlan_config *conf)
|
|
|
+ struct vxlan_config *conf,
|
|
|
+ bool changelink)
|
|
|
{
|
|
|
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;
|
|
|
- int err;
|
|
|
bool use_ipv6 = false;
|
|
|
__be16 default_port = vxlan->cfg.dst_port;
|
|
|
struct net_device *lowerdev = NULL;
|
|
|
|
|
|
- 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;
|
|
|
+ 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);
|
|
|
}
|
|
|
|
|
|
- vxlan_raw_setup(dev);
|
|
|
- } else {
|
|
|
- vxlan_ether_setup(dev);
|
|
|
+ /* MTU range: 68 - 65535 */
|
|
|
+ dev->min_mtu = ETH_MIN_MTU;
|
|
|
+ dev->max_mtu = ETH_MAX_MTU;
|
|
|
+ vxlan->net = src_net;
|
|
|
}
|
|
|
|
|
|
- /* 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));
|
|
@@ -2889,12 +2890,14 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- if (conf->remote_ifindex) {
|
|
|
+ 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 (!lowerdev) {
|
|
|
- pr_info("ifindex %d does not exist\n", dst->remote_ifindex);
|
|
|
+ pr_info("ifindex %d does not exist\n",
|
|
|
+ dst->remote_ifindex);
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
|
|
@@ -2913,7 +2916,8 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
|
|
|
(use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM);
|
|
|
|
|
|
needed_headroom = lowerdev->hard_header_len;
|
|
|
- } else if (vxlan_addr_multicast(&dst->remote_ip)) {
|
|
|
+ } else if (!conf->remote_ifindex &&
|
|
|
+ vxlan_addr_multicast(&dst->remote_ip)) {
|
|
|
pr_info("multicast destination requires interface to be specified\n");
|
|
|
return -EINVAL;
|
|
|
}
|
|
@@ -2953,6 +2957,9 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
|
|
|
if (!vxlan->cfg.age_interval)
|
|
|
vxlan->cfg.age_interval = FDB_AGE_DEFAULT;
|
|
|
|
|
|
+ if (changelink)
|
|
|
+ return 0;
|
|
|
+
|
|
|
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 ||
|
|
@@ -2965,147 +2972,296 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- dev->ethtool_ops = &vxlan_ethtool_ops;
|
|
|
-
|
|
|
- /* create an fdb entry for a valid default destination */
|
|
|
- if (!vxlan_addr_any(&vxlan->default_dst.remote_ip)) {
|
|
|
- err = vxlan_fdb_create(vxlan, all_zeros_mac,
|
|
|
- &vxlan->default_dst.remote_ip,
|
|
|
- NUD_REACHABLE|NUD_PERMANENT,
|
|
|
- NLM_F_EXCL|NLM_F_CREATE,
|
|
|
- vxlan->cfg.dst_port,
|
|
|
- vxlan->default_dst.remote_vni,
|
|
|
- vxlan->default_dst.remote_vni,
|
|
|
- vxlan->default_dst.remote_ifindex,
|
|
|
- NTF_SELF);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- err = register_netdevice(dev);
|
|
|
- if (err) {
|
|
|
- vxlan_fdb_delete_default(vxlan, vxlan->cfg.vni);
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- list_add(&vxlan->next, &vn->vxlan_list);
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int vxlan_newlink(struct net *src_net, struct net_device *dev,
|
|
|
- struct nlattr *tb[], struct nlattr *data[])
|
|
|
+static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[],
|
|
|
+ struct net_device *dev, struct vxlan_config *conf,
|
|
|
+ bool changelink)
|
|
|
{
|
|
|
- struct vxlan_config conf;
|
|
|
+ struct vxlan_dev *vxlan = netdev_priv(dev);
|
|
|
|
|
|
- memset(&conf, 0, sizeof(conf));
|
|
|
+ memset(conf, 0, sizeof(*conf));
|
|
|
|
|
|
- if (data[IFLA_VXLAN_ID])
|
|
|
- conf.vni = cpu_to_be32(nla_get_u32(data[IFLA_VXLAN_ID]));
|
|
|
+ /* if changelink operation, start with old existing cfg */
|
|
|
+ if (changelink)
|
|
|
+ memcpy(conf, &vxlan->cfg, sizeof(*conf));
|
|
|
+
|
|
|
+ if (data[IFLA_VXLAN_ID]) {
|
|
|
+ __be32 vni = cpu_to_be32(nla_get_u32(data[IFLA_VXLAN_ID]));
|
|
|
+
|
|
|
+ if (changelink && (vni != conf->vni))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ conf->vni = cpu_to_be32(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]);
|
|
|
+ conf->remote_ip.sin.sin_addr.s_addr = nla_get_in_addr(data[IFLA_VXLAN_GROUP]);
|
|
|
} else if (data[IFLA_VXLAN_GROUP6]) {
|
|
|
if (!IS_ENABLED(CONFIG_IPV6))
|
|
|
return -EPFNOSUPPORT;
|
|
|
|
|
|
- conf.remote_ip.sin6.sin6_addr = nla_get_in6_addr(data[IFLA_VXLAN_GROUP6]);
|
|
|
- conf.remote_ip.sa.sa_family = AF_INET6;
|
|
|
+ conf->remote_ip.sin6.sin6_addr = nla_get_in6_addr(data[IFLA_VXLAN_GROUP6]);
|
|
|
+ conf->remote_ip.sa.sa_family = AF_INET6;
|
|
|
}
|
|
|
|
|
|
if (data[IFLA_VXLAN_LOCAL]) {
|
|
|
- conf.saddr.sin.sin_addr.s_addr = nla_get_in_addr(data[IFLA_VXLAN_LOCAL]);
|
|
|
- conf.saddr.sa.sa_family = AF_INET;
|
|
|
+ conf->saddr.sin.sin_addr.s_addr = nla_get_in_addr(data[IFLA_VXLAN_LOCAL]);
|
|
|
+ conf->saddr.sa.sa_family = AF_INET;
|
|
|
} else if (data[IFLA_VXLAN_LOCAL6]) {
|
|
|
if (!IS_ENABLED(CONFIG_IPV6))
|
|
|
return -EPFNOSUPPORT;
|
|
|
|
|
|
/* TODO: respect scope id */
|
|
|
- conf.saddr.sin6.sin6_addr = nla_get_in6_addr(data[IFLA_VXLAN_LOCAL6]);
|
|
|
- conf.saddr.sa.sa_family = AF_INET6;
|
|
|
+ conf->saddr.sin6.sin6_addr = nla_get_in6_addr(data[IFLA_VXLAN_LOCAL6]);
|
|
|
+ conf->saddr.sa.sa_family = AF_INET6;
|
|
|
}
|
|
|
|
|
|
if (data[IFLA_VXLAN_LINK])
|
|
|
- conf.remote_ifindex = nla_get_u32(data[IFLA_VXLAN_LINK]);
|
|
|
+ conf->remote_ifindex = nla_get_u32(data[IFLA_VXLAN_LINK]);
|
|
|
|
|
|
if (data[IFLA_VXLAN_TOS])
|
|
|
- conf.tos = nla_get_u8(data[IFLA_VXLAN_TOS]);
|
|
|
+ conf->tos = nla_get_u8(data[IFLA_VXLAN_TOS]);
|
|
|
|
|
|
if (data[IFLA_VXLAN_TTL])
|
|
|
- conf.ttl = nla_get_u8(data[IFLA_VXLAN_TTL]);
|
|
|
+ conf->ttl = nla_get_u8(data[IFLA_VXLAN_TTL]);
|
|
|
|
|
|
if (data[IFLA_VXLAN_LABEL])
|
|
|
- conf.label = nla_get_be32(data[IFLA_VXLAN_LABEL]) &
|
|
|
+ conf->label = nla_get_be32(data[IFLA_VXLAN_LABEL]) &
|
|
|
IPV6_FLOWLABEL_MASK;
|
|
|
|
|
|
- if (!data[IFLA_VXLAN_LEARNING] || nla_get_u8(data[IFLA_VXLAN_LEARNING]))
|
|
|
- conf.flags |= VXLAN_F_LEARN;
|
|
|
+ if (data[IFLA_VXLAN_LEARNING]) {
|
|
|
+ if (nla_get_u8(data[IFLA_VXLAN_LEARNING])) {
|
|
|
+ conf->flags |= VXLAN_F_LEARN;
|
|
|
+ } else {
|
|
|
+ conf->flags &= ~VXLAN_F_LEARN;
|
|
|
+ vxlan->flags &= ~VXLAN_F_LEARN;
|
|
|
+ }
|
|
|
+ } else if (!changelink) {
|
|
|
+ /* default to learn on a new device */
|
|
|
+ conf->flags |= VXLAN_F_LEARN;
|
|
|
+ }
|
|
|
|
|
|
- if (data[IFLA_VXLAN_AGEING])
|
|
|
- conf.age_interval = nla_get_u32(data[IFLA_VXLAN_AGEING]);
|
|
|
+ if (data[IFLA_VXLAN_AGEING]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ conf->age_interval = nla_get_u32(data[IFLA_VXLAN_AGEING]);
|
|
|
+ }
|
|
|
|
|
|
- if (data[IFLA_VXLAN_PROXY] && nla_get_u8(data[IFLA_VXLAN_PROXY]))
|
|
|
- conf.flags |= VXLAN_F_PROXY;
|
|
|
+ if (data[IFLA_VXLAN_PROXY]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (nla_get_u8(data[IFLA_VXLAN_PROXY]))
|
|
|
+ conf->flags |= VXLAN_F_PROXY;
|
|
|
+ }
|
|
|
|
|
|
- if (data[IFLA_VXLAN_RSC] && nla_get_u8(data[IFLA_VXLAN_RSC]))
|
|
|
- conf.flags |= VXLAN_F_RSC;
|
|
|
+ if (data[IFLA_VXLAN_RSC]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (nla_get_u8(data[IFLA_VXLAN_RSC]))
|
|
|
+ conf->flags |= VXLAN_F_RSC;
|
|
|
+ }
|
|
|
|
|
|
- if (data[IFLA_VXLAN_L2MISS] && nla_get_u8(data[IFLA_VXLAN_L2MISS]))
|
|
|
- conf.flags |= VXLAN_F_L2MISS;
|
|
|
+ if (data[IFLA_VXLAN_L2MISS]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (nla_get_u8(data[IFLA_VXLAN_L2MISS]))
|
|
|
+ conf->flags |= VXLAN_F_L2MISS;
|
|
|
+ }
|
|
|
|
|
|
- if (data[IFLA_VXLAN_L3MISS] && nla_get_u8(data[IFLA_VXLAN_L3MISS]))
|
|
|
- conf.flags |= VXLAN_F_L3MISS;
|
|
|
+ if (data[IFLA_VXLAN_L3MISS]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (nla_get_u8(data[IFLA_VXLAN_L3MISS]))
|
|
|
+ conf->flags |= VXLAN_F_L3MISS;
|
|
|
+ }
|
|
|
|
|
|
- if (data[IFLA_VXLAN_LIMIT])
|
|
|
- conf.addrmax = nla_get_u32(data[IFLA_VXLAN_LIMIT]);
|
|
|
+ if (data[IFLA_VXLAN_LIMIT]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ conf->addrmax = nla_get_u32(data[IFLA_VXLAN_LIMIT]);
|
|
|
+ }
|
|
|
|
|
|
- if (data[IFLA_VXLAN_COLLECT_METADATA] &&
|
|
|
- nla_get_u8(data[IFLA_VXLAN_COLLECT_METADATA]))
|
|
|
- conf.flags |= VXLAN_F_COLLECT_METADATA;
|
|
|
+ if (data[IFLA_VXLAN_COLLECT_METADATA]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (nla_get_u8(data[IFLA_VXLAN_COLLECT_METADATA]))
|
|
|
+ conf->flags |= VXLAN_F_COLLECT_METADATA;
|
|
|
+ }
|
|
|
|
|
|
if (data[IFLA_VXLAN_PORT_RANGE]) {
|
|
|
- const struct ifla_vxlan_port_range *p
|
|
|
- = nla_data(data[IFLA_VXLAN_PORT_RANGE]);
|
|
|
- conf.port_min = ntohs(p->low);
|
|
|
- conf.port_max = ntohs(p->high);
|
|
|
+ if (!changelink) {
|
|
|
+ const struct ifla_vxlan_port_range *p
|
|
|
+ = nla_data(data[IFLA_VXLAN_PORT_RANGE]);
|
|
|
+ conf->port_min = ntohs(p->low);
|
|
|
+ conf->port_max = ntohs(p->high);
|
|
|
+ } else {
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if (data[IFLA_VXLAN_PORT])
|
|
|
- conf.dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]);
|
|
|
+ if (data[IFLA_VXLAN_PORT]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ conf->dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]);
|
|
|
+ }
|
|
|
|
|
|
- if (data[IFLA_VXLAN_UDP_CSUM] &&
|
|
|
- !nla_get_u8(data[IFLA_VXLAN_UDP_CSUM]))
|
|
|
- conf.flags |= VXLAN_F_UDP_ZERO_CSUM_TX;
|
|
|
+ if (data[IFLA_VXLAN_UDP_CSUM]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (!nla_get_u8(data[IFLA_VXLAN_UDP_CSUM]))
|
|
|
+ conf->flags |= VXLAN_F_UDP_ZERO_CSUM_TX;
|
|
|
+ }
|
|
|
|
|
|
- if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX] &&
|
|
|
- nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]))
|
|
|
- conf.flags |= VXLAN_F_UDP_ZERO_CSUM6_TX;
|
|
|
+ if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]))
|
|
|
+ conf->flags |= VXLAN_F_UDP_ZERO_CSUM6_TX;
|
|
|
+ }
|
|
|
|
|
|
- if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] &&
|
|
|
- nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
|
|
|
- conf.flags |= VXLAN_F_UDP_ZERO_CSUM6_RX;
|
|
|
+ if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
|
|
|
+ conf->flags |= VXLAN_F_UDP_ZERO_CSUM6_RX;
|
|
|
+ }
|
|
|
|
|
|
- if (data[IFLA_VXLAN_REMCSUM_TX] &&
|
|
|
- nla_get_u8(data[IFLA_VXLAN_REMCSUM_TX]))
|
|
|
- conf.flags |= VXLAN_F_REMCSUM_TX;
|
|
|
+ if (data[IFLA_VXLAN_REMCSUM_TX]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (nla_get_u8(data[IFLA_VXLAN_REMCSUM_TX]))
|
|
|
+ conf->flags |= VXLAN_F_REMCSUM_TX;
|
|
|
+ }
|
|
|
|
|
|
- if (data[IFLA_VXLAN_REMCSUM_RX] &&
|
|
|
- nla_get_u8(data[IFLA_VXLAN_REMCSUM_RX]))
|
|
|
- conf.flags |= VXLAN_F_REMCSUM_RX;
|
|
|
+ if (data[IFLA_VXLAN_REMCSUM_RX]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (nla_get_u8(data[IFLA_VXLAN_REMCSUM_RX]))
|
|
|
+ conf->flags |= VXLAN_F_REMCSUM_RX;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data[IFLA_VXLAN_GBP]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ conf->flags |= VXLAN_F_GBP;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data[IFLA_VXLAN_GPE]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ conf->flags |= VXLAN_F_GPE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data[IFLA_VXLAN_REMCSUM_NOPARTIAL]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ conf->flags |= VXLAN_F_REMCSUM_NOPARTIAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (tb[IFLA_MTU]) {
|
|
|
+ if (changelink)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ conf->mtu = nla_get_u32(tb[IFLA_MTU]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int vxlan_newlink(struct net *src_net, struct net_device *dev,
|
|
|
+ struct nlattr *tb[], struct nlattr *data[])
|
|
|
+{
|
|
|
+ struct vxlan_net *vn = net_generic(src_net, vxlan_net_id);
|
|
|
+ struct vxlan_dev *vxlan = netdev_priv(dev);
|
|
|
+ struct vxlan_config conf;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = vxlan_nl2conf(tb, data, dev, &conf, false);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ err = vxlan_dev_configure(src_net, dev, &conf, false);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ dev->ethtool_ops = &vxlan_ethtool_ops;
|
|
|
+
|
|
|
+ /* create an fdb entry for a valid default destination */
|
|
|
+ if (!vxlan_addr_any(&vxlan->default_dst.remote_ip)) {
|
|
|
+ err = vxlan_fdb_create(vxlan, all_zeros_mac,
|
|
|
+ &vxlan->default_dst.remote_ip,
|
|
|
+ NUD_REACHABLE | NUD_PERMANENT,
|
|
|
+ NLM_F_EXCL | NLM_F_CREATE,
|
|
|
+ vxlan->cfg.dst_port,
|
|
|
+ vxlan->default_dst.remote_vni,
|
|
|
+ vxlan->default_dst.remote_vni,
|
|
|
+ vxlan->default_dst.remote_ifindex,
|
|
|
+ NTF_SELF);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = register_netdevice(dev);
|
|
|
+ if (err) {
|
|
|
+ vxlan_fdb_delete_default(vxlan, vxlan->default_dst.remote_vni);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ list_add(&vxlan->next, &vn->vxlan_list);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- if (data[IFLA_VXLAN_GBP])
|
|
|
- conf.flags |= VXLAN_F_GBP;
|
|
|
+static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
|
|
|
+ struct nlattr *data[])
|
|
|
+{
|
|
|
+ struct vxlan_dev *vxlan = netdev_priv(dev);
|
|
|
+ struct vxlan_rdst *dst = &vxlan->default_dst;
|
|
|
+ struct vxlan_rdst old_dst;
|
|
|
+ struct vxlan_config conf;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = vxlan_nl2conf(tb, data,
|
|
|
+ dev, &conf, true);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
|
|
|
- if (data[IFLA_VXLAN_GPE])
|
|
|
- conf.flags |= VXLAN_F_GPE;
|
|
|
+ memcpy(&old_dst, dst, sizeof(struct vxlan_rdst));
|
|
|
|
|
|
- if (data[IFLA_VXLAN_REMCSUM_NOPARTIAL])
|
|
|
- conf.flags |= VXLAN_F_REMCSUM_NOPARTIAL;
|
|
|
+ err = vxlan_dev_configure(vxlan->net, dev, &conf, true);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
|
|
|
- if (tb[IFLA_MTU])
|
|
|
- conf.mtu = nla_get_u32(tb[IFLA_MTU]);
|
|
|
+ /* handle default dst entry */
|
|
|
+ if (!vxlan_addr_equal(&dst->remote_ip, &old_dst.remote_ip)) {
|
|
|
+ spin_lock_bh(&vxlan->hash_lock);
|
|
|
+ if (!vxlan_addr_any(&old_dst.remote_ip))
|
|
|
+ __vxlan_fdb_delete(vxlan, all_zeros_mac,
|
|
|
+ old_dst.remote_ip,
|
|
|
+ vxlan->cfg.dst_port,
|
|
|
+ old_dst.remote_vni,
|
|
|
+ old_dst.remote_vni,
|
|
|
+ old_dst.remote_ifindex, 0);
|
|
|
+
|
|
|
+ if (!vxlan_addr_any(&dst->remote_ip)) {
|
|
|
+ err = vxlan_fdb_create(vxlan, all_zeros_mac,
|
|
|
+ &dst->remote_ip,
|
|
|
+ NUD_REACHABLE | NUD_PERMANENT,
|
|
|
+ NLM_F_CREATE | NLM_F_APPEND,
|
|
|
+ vxlan->cfg.dst_port,
|
|
|
+ dst->remote_vni,
|
|
|
+ dst->remote_vni,
|
|
|
+ dst->remote_ifindex,
|
|
|
+ NTF_SELF);
|
|
|
+ if (err) {
|
|
|
+ spin_unlock_bh(&vxlan->hash_lock);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&vxlan->hash_lock);
|
|
|
+ }
|
|
|
|
|
|
- return vxlan_dev_configure(src_net, dev, &conf);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static void vxlan_dellink(struct net_device *dev, struct list_head *head)
|
|
@@ -3261,6 +3417,7 @@ static struct rtnl_link_ops vxlan_link_ops __read_mostly = {
|
|
|
.setup = vxlan_setup,
|
|
|
.validate = vxlan_validate,
|
|
|
.newlink = vxlan_newlink,
|
|
|
+ .changelink = vxlan_changelink,
|
|
|
.dellink = vxlan_dellink,
|
|
|
.get_size = vxlan_get_size,
|
|
|
.fill_info = vxlan_fill_info,
|
|
@@ -3282,7 +3439,7 @@ struct net_device *vxlan_dev_create(struct net *net, const char *name,
|
|
|
if (IS_ERR(dev))
|
|
|
return dev;
|
|
|
|
|
|
- err = vxlan_dev_configure(net, dev, conf);
|
|
|
+ err = vxlan_dev_configure(net, dev, conf, false);
|
|
|
if (err < 0) {
|
|
|
free_netdev(dev);
|
|
|
return ERR_PTR(err);
|