diff options
Diffstat (limited to 'drivers/net/vxlan')
-rw-r--r-- | drivers/net/vxlan/vxlan_core.c | 29 | ||||
-rw-r--r-- | drivers/net/vxlan/vxlan_mdb.c | 174 | ||||
-rw-r--r-- | drivers/net/vxlan/vxlan_private.h | 2 |
3 files changed, 180 insertions, 25 deletions
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 412c3c0b69..9ec46048d3 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -1615,6 +1615,10 @@ static bool vxlan_set_mac(struct vxlan_dev *vxlan, if (ether_addr_equal(eth_hdr(skb)->h_source, vxlan->dev->dev_addr)) return false; + /* Ignore packets from invalid src-address */ + if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) + return false; + /* Get address from the outer IP header */ if (vxlan_get_sk_family(vs) == AF_INET) { saddr.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; @@ -2379,7 +2383,17 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, else udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX); #if IS_ENABLED(CONFIG_IPV6) - key.label = vxlan->cfg.label; + switch (vxlan->cfg.label_policy) { + case VXLAN_LABEL_FIXED: + key.label = vxlan->cfg.label; + break; + case VXLAN_LABEL_INHERIT: + key.label = ip_tunnel_get_flowlabel(old_iph, skb); + break; + default: + DEBUG_NET_WARN_ON_ONCE(1); + goto drop; + } #endif } else { if (!info) { @@ -2845,6 +2859,7 @@ static int vxlan_init(struct net_device *dev) if (err) goto err_gro_cells_destroy; + netdev_lockdep_set_classes(dev); return 0; err_gro_cells_destroy: @@ -3225,6 +3240,7 @@ static const struct net_device_ops vxlan_netdev_ether_ops = { .ndo_fdb_get = vxlan_fdb_get, .ndo_mdb_add = vxlan_mdb_add, .ndo_mdb_del = vxlan_mdb_del, + .ndo_mdb_del_bulk = vxlan_mdb_del_bulk, .ndo_mdb_dump = vxlan_mdb_dump, .ndo_mdb_get = vxlan_mdb_get, .ndo_fill_metadata_dst = vxlan_fill_metadata_dst, @@ -3366,6 +3382,7 @@ static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = { [IFLA_VXLAN_DF] = { .type = NLA_U8 }, [IFLA_VXLAN_VNIFILTER] = { .type = NLA_U8 }, [IFLA_VXLAN_LOCALBYPASS] = NLA_POLICY_MAX(NLA_U8, 1), + [IFLA_VXLAN_LABEL_POLICY] = NLA_POLICY_MAX(NLA_U32, VXLAN_LABEL_MAX), }; static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[], @@ -3740,6 +3757,12 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf, return -EINVAL; } + if (conf->label_policy && !use_ipv6) { + NL_SET_ERR_MSG(extack, + "Label policy only applies to IPv6 VXLAN devices"); + return -EINVAL; + } + if (conf->remote_ifindex) { struct net_device *lowerdev; @@ -4082,6 +4105,8 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[], if (data[IFLA_VXLAN_LABEL]) conf->label = nla_get_be32(data[IFLA_VXLAN_LABEL]) & IPV6_FLOWLABEL_MASK; + if (data[IFLA_VXLAN_LABEL_POLICY]) + conf->label_policy = nla_get_u32(data[IFLA_VXLAN_LABEL_POLICY]); if (data[IFLA_VXLAN_LEARNING]) { err = vxlan_nl2flag(conf, data, IFLA_VXLAN_LEARNING, @@ -4398,6 +4423,7 @@ static size_t vxlan_get_size(const struct net_device *dev) nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_TOS */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_DF */ nla_total_size(sizeof(__be32)) + /* IFLA_VXLAN_LABEL */ + nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LABEL_POLICY */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_LEARNING */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_PROXY */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_RSC */ @@ -4471,6 +4497,7 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan->cfg.tos) || nla_put_u8(skb, IFLA_VXLAN_DF, vxlan->cfg.df) || nla_put_be32(skb, IFLA_VXLAN_LABEL, vxlan->cfg.label) || + nla_put_u32(skb, IFLA_VXLAN_LABEL_POLICY, vxlan->cfg.label_policy) || nla_put_u8(skb, IFLA_VXLAN_LEARNING, !!(vxlan->cfg.flags & VXLAN_F_LEARN)) || nla_put_u8(skb, IFLA_VXLAN_PROXY, diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index eb4c580b5c..60eb95a06d 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -74,6 +74,14 @@ struct vxlan_mdb_config { u8 rt_protocol; }; +struct vxlan_mdb_flush_desc { + union vxlan_addr remote_ip; + __be32 src_vni; + __be32 remote_vni; + __be16 remote_port; + u8 rt_protocol; +}; + static const struct rhashtable_params vxlan_mdb_rht_params = { .head_offset = offsetof(struct vxlan_mdb_entry, rhnode), .key_offset = offsetof(struct vxlan_mdb_entry, key), @@ -1306,6 +1314,145 @@ int vxlan_mdb_del(struct net_device *dev, struct nlattr *tb[], return err; } +static const struct nla_policy +vxlan_mdbe_attrs_del_bulk_pol[MDBE_ATTR_MAX + 1] = { + [MDBE_ATTR_RTPROT] = NLA_POLICY_MIN(NLA_U8, RTPROT_STATIC), + [MDBE_ATTR_DST] = NLA_POLICY_RANGE(NLA_BINARY, + sizeof(struct in_addr), + sizeof(struct in6_addr)), + [MDBE_ATTR_DST_PORT] = { .type = NLA_U16 }, + [MDBE_ATTR_VNI] = NLA_POLICY_FULL_RANGE(NLA_U32, &vni_range), + [MDBE_ATTR_SRC_VNI] = NLA_POLICY_FULL_RANGE(NLA_U32, &vni_range), + [MDBE_ATTR_STATE_MASK] = NLA_POLICY_MASK(NLA_U8, MDB_PERMANENT), +}; + +static int vxlan_mdb_flush_desc_init(struct vxlan_dev *vxlan, + struct vxlan_mdb_flush_desc *desc, + struct nlattr *tb[], + struct netlink_ext_ack *extack) +{ + struct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]); + struct nlattr *mdbe_attrs[MDBE_ATTR_MAX + 1]; + int err; + + if (entry->ifindex && entry->ifindex != vxlan->dev->ifindex) { + NL_SET_ERR_MSG_MOD(extack, "Invalid port net device"); + return -EINVAL; + } + + if (entry->vid) { + NL_SET_ERR_MSG_MOD(extack, "VID must not be specified"); + return -EINVAL; + } + + if (!tb[MDBA_SET_ENTRY_ATTRS]) + return 0; + + err = nla_parse_nested(mdbe_attrs, MDBE_ATTR_MAX, + tb[MDBA_SET_ENTRY_ATTRS], + vxlan_mdbe_attrs_del_bulk_pol, extack); + if (err) + return err; + + if (mdbe_attrs[MDBE_ATTR_STATE_MASK]) { + u8 state_mask = nla_get_u8(mdbe_attrs[MDBE_ATTR_STATE_MASK]); + + if ((state_mask & MDB_PERMANENT) && !(entry->state & MDB_PERMANENT)) { + NL_SET_ERR_MSG_MOD(extack, "Only permanent MDB entries are supported"); + return -EINVAL; + } + } + + if (mdbe_attrs[MDBE_ATTR_RTPROT]) + desc->rt_protocol = nla_get_u8(mdbe_attrs[MDBE_ATTR_RTPROT]); + + if (mdbe_attrs[MDBE_ATTR_DST]) + vxlan_nla_get_addr(&desc->remote_ip, mdbe_attrs[MDBE_ATTR_DST]); + + if (mdbe_attrs[MDBE_ATTR_DST_PORT]) + desc->remote_port = + cpu_to_be16(nla_get_u16(mdbe_attrs[MDBE_ATTR_DST_PORT])); + + if (mdbe_attrs[MDBE_ATTR_VNI]) + desc->remote_vni = + cpu_to_be32(nla_get_u32(mdbe_attrs[MDBE_ATTR_VNI])); + + if (mdbe_attrs[MDBE_ATTR_SRC_VNI]) + desc->src_vni = + cpu_to_be32(nla_get_u32(mdbe_attrs[MDBE_ATTR_SRC_VNI])); + + return 0; +} + +static void vxlan_mdb_remotes_flush(struct vxlan_dev *vxlan, + struct vxlan_mdb_entry *mdb_entry, + const struct vxlan_mdb_flush_desc *desc) +{ + struct vxlan_mdb_remote *remote, *tmp; + + list_for_each_entry_safe(remote, tmp, &mdb_entry->remotes, list) { + struct vxlan_rdst *rd = rtnl_dereference(remote->rd); + __be32 remote_vni; + + if (desc->remote_ip.sa.sa_family && + !vxlan_addr_equal(&desc->remote_ip, &rd->remote_ip)) + continue; + + /* Encapsulation is performed with source VNI if remote VNI + * is not set. + */ + remote_vni = rd->remote_vni ? : mdb_entry->key.vni; + if (desc->remote_vni && desc->remote_vni != remote_vni) + continue; + + if (desc->remote_port && desc->remote_port != rd->remote_port) + continue; + + if (desc->rt_protocol && + desc->rt_protocol != remote->rt_protocol) + continue; + + vxlan_mdb_remote_del(vxlan, mdb_entry, remote); + } +} + +static void vxlan_mdb_flush(struct vxlan_dev *vxlan, + const struct vxlan_mdb_flush_desc *desc) +{ + struct vxlan_mdb_entry *mdb_entry; + struct hlist_node *tmp; + + /* The removal of an entry cannot trigger the removal of another entry + * since entries are always added to the head of the list. + */ + hlist_for_each_entry_safe(mdb_entry, tmp, &vxlan->mdb_list, mdb_node) { + if (desc->src_vni && desc->src_vni != mdb_entry->key.vni) + continue; + + vxlan_mdb_remotes_flush(vxlan, mdb_entry, desc); + /* Entry will only be removed if its remotes list is empty. */ + vxlan_mdb_entry_put(vxlan, mdb_entry); + } +} + +int vxlan_mdb_del_bulk(struct net_device *dev, struct nlattr *tb[], + struct netlink_ext_ack *extack) +{ + struct vxlan_dev *vxlan = netdev_priv(dev); + struct vxlan_mdb_flush_desc desc = {}; + int err; + + ASSERT_RTNL(); + + err = vxlan_mdb_flush_desc_init(vxlan, &desc, tb, extack); + if (err) + return err; + + vxlan_mdb_flush(vxlan, &desc); + + return 0; +} + static const struct nla_policy vxlan_mdbe_attrs_get_pol[MDBE_ATTR_MAX + 1] = { [MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY, sizeof(struct in_addr), @@ -1575,29 +1722,6 @@ static void vxlan_mdb_check_empty(void *ptr, void *arg) WARN_ON_ONCE(1); } -static void vxlan_mdb_remotes_flush(struct vxlan_dev *vxlan, - struct vxlan_mdb_entry *mdb_entry) -{ - struct vxlan_mdb_remote *remote, *tmp; - - list_for_each_entry_safe(remote, tmp, &mdb_entry->remotes, list) - vxlan_mdb_remote_del(vxlan, mdb_entry, remote); -} - -static void vxlan_mdb_entries_flush(struct vxlan_dev *vxlan) -{ - struct vxlan_mdb_entry *mdb_entry; - struct hlist_node *tmp; - - /* The removal of an entry cannot trigger the removal of another entry - * since entries are always added to the head of the list. - */ - hlist_for_each_entry_safe(mdb_entry, tmp, &vxlan->mdb_list, mdb_node) { - vxlan_mdb_remotes_flush(vxlan, mdb_entry); - vxlan_mdb_entry_put(vxlan, mdb_entry); - } -} - int vxlan_mdb_init(struct vxlan_dev *vxlan) { int err; @@ -1613,7 +1737,9 @@ int vxlan_mdb_init(struct vxlan_dev *vxlan) void vxlan_mdb_fini(struct vxlan_dev *vxlan) { - vxlan_mdb_entries_flush(vxlan); + struct vxlan_mdb_flush_desc desc = {}; + + vxlan_mdb_flush(vxlan, &desc); WARN_ON_ONCE(vxlan->cfg.flags & VXLAN_F_MDB); rhashtable_free_and_destroy(&vxlan->mdb_tbl, vxlan_mdb_check_empty, NULL); diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h index db679c3809..b35d96b788 100644 --- a/drivers/net/vxlan/vxlan_private.h +++ b/drivers/net/vxlan/vxlan_private.h @@ -235,6 +235,8 @@ int vxlan_mdb_add(struct net_device *dev, struct nlattr *tb[], u16 nlmsg_flags, struct netlink_ext_ack *extack); int vxlan_mdb_del(struct net_device *dev, struct nlattr *tb[], struct netlink_ext_ack *extack); +int vxlan_mdb_del_bulk(struct net_device *dev, struct nlattr *tb[], + struct netlink_ext_ack *extack); int vxlan_mdb_get(struct net_device *dev, struct nlattr *tb[], u32 portid, u32 seq, struct netlink_ext_ack *extack); struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, |