| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746 |
- // SPDX-License-Identifier: GPL-2.0-only
- #include <linux/if_bridge.h>
- #include <linux/in.h>
- #include <linux/list.h>
- #include <linux/netdevice.h>
- #include <linux/netlink.h>
- #include <linux/rhashtable.h>
- #include <linux/rhashtable-types.h>
- #include <linux/rtnetlink.h>
- #include <linux/skbuff.h>
- #include <linux/types.h>
- #include <net/netlink.h>
- #include <net/vxlan.h>
- #include "vxlan_private.h"
- struct vxlan_mdb_entry_key {
- union vxlan_addr src;
- union vxlan_addr dst;
- __be32 vni;
- };
- struct vxlan_mdb_entry {
- struct rhash_head rhnode;
- struct list_head remotes;
- struct vxlan_mdb_entry_key key;
- struct hlist_node mdb_node;
- struct rcu_head rcu;
- };
- #define VXLAN_MDB_REMOTE_F_BLOCKED BIT(0)
- struct vxlan_mdb_remote {
- struct list_head list;
- struct vxlan_rdst __rcu *rd;
- u8 flags;
- u8 filter_mode;
- u8 rt_protocol;
- struct hlist_head src_list;
- struct rcu_head rcu;
- };
- #define VXLAN_SGRP_F_DELETE BIT(0)
- struct vxlan_mdb_src_entry {
- struct hlist_node node;
- union vxlan_addr addr;
- u8 flags;
- };
- struct vxlan_mdb_dump_ctx {
- long reserved;
- long entry_idx;
- long remote_idx;
- };
- struct vxlan_mdb_config_src_entry {
- union vxlan_addr addr;
- struct list_head node;
- };
- struct vxlan_mdb_config {
- struct vxlan_dev *vxlan;
- struct vxlan_mdb_entry_key group;
- struct list_head src_list;
- union vxlan_addr remote_ip;
- u32 remote_ifindex;
- __be32 remote_vni;
- __be16 remote_port;
- u16 nlflags;
- u8 flags;
- u8 filter_mode;
- 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),
- .key_len = sizeof(struct vxlan_mdb_entry_key),
- .automatic_shrinking = true,
- };
- static int __vxlan_mdb_add(const struct vxlan_mdb_config *cfg,
- struct netlink_ext_ack *extack);
- static int __vxlan_mdb_del(const struct vxlan_mdb_config *cfg,
- struct netlink_ext_ack *extack);
- static void vxlan_br_mdb_entry_fill(const struct vxlan_dev *vxlan,
- const struct vxlan_mdb_entry *mdb_entry,
- const struct vxlan_mdb_remote *remote,
- struct br_mdb_entry *e)
- {
- const union vxlan_addr *dst = &mdb_entry->key.dst;
- memset(e, 0, sizeof(*e));
- e->ifindex = vxlan->dev->ifindex;
- e->state = MDB_PERMANENT;
- if (remote->flags & VXLAN_MDB_REMOTE_F_BLOCKED)
- e->flags |= MDB_FLAGS_BLOCKED;
- switch (dst->sa.sa_family) {
- case AF_INET:
- e->addr.u.ip4 = dst->sin.sin_addr.s_addr;
- e->addr.proto = htons(ETH_P_IP);
- break;
- #if IS_ENABLED(CONFIG_IPV6)
- case AF_INET6:
- e->addr.u.ip6 = dst->sin6.sin6_addr;
- e->addr.proto = htons(ETH_P_IPV6);
- break;
- #endif
- }
- }
- static int vxlan_mdb_entry_info_fill_srcs(struct sk_buff *skb,
- const struct vxlan_mdb_remote *remote)
- {
- struct vxlan_mdb_src_entry *ent;
- struct nlattr *nest;
- if (hlist_empty(&remote->src_list))
- return 0;
- nest = nla_nest_start(skb, MDBA_MDB_EATTR_SRC_LIST);
- if (!nest)
- return -EMSGSIZE;
- hlist_for_each_entry(ent, &remote->src_list, node) {
- struct nlattr *nest_ent;
- nest_ent = nla_nest_start(skb, MDBA_MDB_SRCLIST_ENTRY);
- if (!nest_ent)
- goto out_cancel_err;
- if (vxlan_nla_put_addr(skb, MDBA_MDB_SRCATTR_ADDRESS,
- &ent->addr) ||
- nla_put_u32(skb, MDBA_MDB_SRCATTR_TIMER, 0))
- goto out_cancel_err;
- nla_nest_end(skb, nest_ent);
- }
- nla_nest_end(skb, nest);
- return 0;
- out_cancel_err:
- nla_nest_cancel(skb, nest);
- return -EMSGSIZE;
- }
- static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,
- struct sk_buff *skb,
- const struct vxlan_mdb_entry *mdb_entry,
- const struct vxlan_mdb_remote *remote)
- {
- struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
- struct br_mdb_entry e;
- struct nlattr *nest;
- nest = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY_INFO);
- if (!nest)
- return -EMSGSIZE;
- vxlan_br_mdb_entry_fill(vxlan, mdb_entry, remote, &e);
- if (nla_put_nohdr(skb, sizeof(e), &e) ||
- nla_put_u32(skb, MDBA_MDB_EATTR_TIMER, 0))
- goto nest_err;
- if (!vxlan_addr_any(&mdb_entry->key.src) &&
- vxlan_nla_put_addr(skb, MDBA_MDB_EATTR_SOURCE, &mdb_entry->key.src))
- goto nest_err;
- if (nla_put_u8(skb, MDBA_MDB_EATTR_RTPROT, remote->rt_protocol) ||
- nla_put_u8(skb, MDBA_MDB_EATTR_GROUP_MODE, remote->filter_mode) ||
- vxlan_mdb_entry_info_fill_srcs(skb, remote) ||
- vxlan_nla_put_addr(skb, MDBA_MDB_EATTR_DST, &rd->remote_ip))
- goto nest_err;
- if (rd->remote_port && rd->remote_port != vxlan->cfg.dst_port &&
- nla_put_u16(skb, MDBA_MDB_EATTR_DST_PORT,
- be16_to_cpu(rd->remote_port)))
- goto nest_err;
- if (rd->remote_vni != vxlan->default_dst.remote_vni &&
- nla_put_u32(skb, MDBA_MDB_EATTR_VNI, be32_to_cpu(rd->remote_vni)))
- goto nest_err;
- if (rd->remote_ifindex &&
- nla_put_u32(skb, MDBA_MDB_EATTR_IFINDEX, rd->remote_ifindex))
- goto nest_err;
- if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) &&
- mdb_entry->key.vni && nla_put_u32(skb, MDBA_MDB_EATTR_SRC_VNI,
- be32_to_cpu(mdb_entry->key.vni)))
- goto nest_err;
- nla_nest_end(skb, nest);
- return 0;
- nest_err:
- nla_nest_cancel(skb, nest);
- return -EMSGSIZE;
- }
- static int vxlan_mdb_entry_fill(const struct vxlan_dev *vxlan,
- struct sk_buff *skb,
- struct vxlan_mdb_dump_ctx *ctx,
- const struct vxlan_mdb_entry *mdb_entry)
- {
- int remote_idx = 0, s_remote_idx = ctx->remote_idx;
- struct vxlan_mdb_remote *remote;
- struct nlattr *nest;
- int err = 0;
- nest = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY);
- if (!nest)
- return -EMSGSIZE;
- list_for_each_entry(remote, &mdb_entry->remotes, list) {
- if (remote_idx < s_remote_idx)
- goto skip;
- err = vxlan_mdb_entry_info_fill(vxlan, skb, mdb_entry, remote);
- if (err)
- break;
- skip:
- remote_idx++;
- }
- ctx->remote_idx = err ? remote_idx : 0;
- nla_nest_end(skb, nest);
- return err;
- }
- static int vxlan_mdb_fill(const struct vxlan_dev *vxlan, struct sk_buff *skb,
- struct vxlan_mdb_dump_ctx *ctx)
- {
- int entry_idx = 0, s_entry_idx = ctx->entry_idx;
- struct vxlan_mdb_entry *mdb_entry;
- struct nlattr *nest;
- int err = 0;
- nest = nla_nest_start_noflag(skb, MDBA_MDB);
- if (!nest)
- return -EMSGSIZE;
- hlist_for_each_entry(mdb_entry, &vxlan->mdb_list, mdb_node) {
- if (entry_idx < s_entry_idx)
- goto skip;
- err = vxlan_mdb_entry_fill(vxlan, skb, ctx, mdb_entry);
- if (err)
- break;
- skip:
- entry_idx++;
- }
- ctx->entry_idx = err ? entry_idx : 0;
- nla_nest_end(skb, nest);
- return err;
- }
- int vxlan_mdb_dump(struct net_device *dev, struct sk_buff *skb,
- struct netlink_callback *cb)
- {
- struct vxlan_mdb_dump_ctx *ctx = (void *)cb->ctx;
- struct vxlan_dev *vxlan = netdev_priv(dev);
- struct br_port_msg *bpm;
- struct nlmsghdr *nlh;
- int err;
- ASSERT_RTNL();
- NL_ASSERT_DUMP_CTX_FITS(struct vxlan_mdb_dump_ctx);
- nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq, RTM_NEWMDB, sizeof(*bpm),
- NLM_F_MULTI);
- if (!nlh)
- return -EMSGSIZE;
- bpm = nlmsg_data(nlh);
- memset(bpm, 0, sizeof(*bpm));
- bpm->family = AF_BRIDGE;
- bpm->ifindex = dev->ifindex;
- err = vxlan_mdb_fill(vxlan, skb, ctx);
- nlmsg_end(skb, nlh);
- cb->seq = vxlan->mdb_seq;
- nl_dump_check_consistent(cb, nlh);
- return err;
- }
- static const struct nla_policy
- vxlan_mdbe_src_list_entry_pol[MDBE_SRCATTR_MAX + 1] = {
- [MDBE_SRCATTR_ADDRESS] = NLA_POLICY_RANGE(NLA_BINARY,
- sizeof(struct in_addr),
- sizeof(struct in6_addr)),
- };
- static const struct nla_policy
- vxlan_mdbe_src_list_pol[MDBE_SRC_LIST_MAX + 1] = {
- [MDBE_SRC_LIST_ENTRY] = NLA_POLICY_NESTED(vxlan_mdbe_src_list_entry_pol),
- };
- static const struct netlink_range_validation vni_range = {
- .max = VXLAN_N_VID - 1,
- };
- static const struct nla_policy vxlan_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = {
- [MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY,
- sizeof(struct in_addr),
- sizeof(struct in6_addr)),
- [MDBE_ATTR_GROUP_MODE] = NLA_POLICY_RANGE(NLA_U8, MCAST_EXCLUDE,
- MCAST_INCLUDE),
- [MDBE_ATTR_SRC_LIST] = NLA_POLICY_NESTED(vxlan_mdbe_src_list_pol),
- [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_IFINDEX] = NLA_POLICY_MIN(NLA_S32, 1),
- [MDBE_ATTR_SRC_VNI] = NLA_POLICY_FULL_RANGE(NLA_U32, &vni_range),
- };
- static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,
- struct netlink_ext_ack *extack)
- {
- switch (proto) {
- case htons(ETH_P_IP):
- if (nla_len(attr) != sizeof(struct in_addr)) {
- NL_SET_ERR_MSG_MOD(extack, "IPv4 invalid source address length");
- return false;
- }
- if (ipv4_is_multicast(nla_get_in_addr(attr))) {
- NL_SET_ERR_MSG_MOD(extack, "IPv4 multicast source address is not allowed");
- return false;
- }
- break;
- #if IS_ENABLED(CONFIG_IPV6)
- case htons(ETH_P_IPV6): {
- struct in6_addr src;
- if (nla_len(attr) != sizeof(struct in6_addr)) {
- NL_SET_ERR_MSG_MOD(extack, "IPv6 invalid source address length");
- return false;
- }
- src = nla_get_in6_addr(attr);
- if (ipv6_addr_is_multicast(&src)) {
- NL_SET_ERR_MSG_MOD(extack, "IPv6 multicast source address is not allowed");
- return false;
- }
- break;
- }
- #endif
- default:
- NL_SET_ERR_MSG_MOD(extack, "Invalid protocol used with source address");
- return false;
- }
- return true;
- }
- static void vxlan_mdb_group_set(struct vxlan_mdb_entry_key *group,
- const struct br_mdb_entry *entry,
- const struct nlattr *source_attr)
- {
- switch (entry->addr.proto) {
- case htons(ETH_P_IP):
- group->dst.sa.sa_family = AF_INET;
- group->dst.sin.sin_addr.s_addr = entry->addr.u.ip4;
- break;
- #if IS_ENABLED(CONFIG_IPV6)
- case htons(ETH_P_IPV6):
- group->dst.sa.sa_family = AF_INET6;
- group->dst.sin6.sin6_addr = entry->addr.u.ip6;
- break;
- #endif
- }
- if (source_attr)
- vxlan_nla_get_addr(&group->src, source_attr);
- }
- static bool vxlan_mdb_is_star_g(const struct vxlan_mdb_entry_key *group)
- {
- return !vxlan_addr_any(&group->dst) && vxlan_addr_any(&group->src);
- }
- static bool vxlan_mdb_is_sg(const struct vxlan_mdb_entry_key *group)
- {
- return !vxlan_addr_any(&group->dst) && !vxlan_addr_any(&group->src);
- }
- static int vxlan_mdb_config_src_entry_init(struct vxlan_mdb_config *cfg,
- __be16 proto,
- const struct nlattr *src_entry,
- struct netlink_ext_ack *extack)
- {
- struct nlattr *tb[MDBE_SRCATTR_MAX + 1];
- struct vxlan_mdb_config_src_entry *src;
- int err;
- err = nla_parse_nested(tb, MDBE_SRCATTR_MAX, src_entry,
- vxlan_mdbe_src_list_entry_pol, extack);
- if (err)
- return err;
- if (NL_REQ_ATTR_CHECK(extack, src_entry, tb, MDBE_SRCATTR_ADDRESS))
- return -EINVAL;
- if (!vxlan_mdb_is_valid_source(tb[MDBE_SRCATTR_ADDRESS], proto,
- extack))
- return -EINVAL;
- src = kzalloc(sizeof(*src), GFP_KERNEL);
- if (!src)
- return -ENOMEM;
- err = vxlan_nla_get_addr(&src->addr, tb[MDBE_SRCATTR_ADDRESS]);
- if (err)
- goto err_free_src;
- list_add_tail(&src->node, &cfg->src_list);
- return 0;
- err_free_src:
- kfree(src);
- return err;
- }
- static void
- vxlan_mdb_config_src_entry_fini(struct vxlan_mdb_config_src_entry *src)
- {
- list_del(&src->node);
- kfree(src);
- }
- static int vxlan_mdb_config_src_list_init(struct vxlan_mdb_config *cfg,
- __be16 proto,
- const struct nlattr *src_list,
- struct netlink_ext_ack *extack)
- {
- struct vxlan_mdb_config_src_entry *src, *tmp;
- struct nlattr *src_entry;
- int rem, err;
- nla_for_each_nested(src_entry, src_list, rem) {
- err = vxlan_mdb_config_src_entry_init(cfg, proto, src_entry,
- extack);
- if (err)
- goto err_src_entry_init;
- }
- return 0;
- err_src_entry_init:
- list_for_each_entry_safe_reverse(src, tmp, &cfg->src_list, node)
- vxlan_mdb_config_src_entry_fini(src);
- return err;
- }
- static void vxlan_mdb_config_src_list_fini(struct vxlan_mdb_config *cfg)
- {
- struct vxlan_mdb_config_src_entry *src, *tmp;
- list_for_each_entry_safe_reverse(src, tmp, &cfg->src_list, node)
- vxlan_mdb_config_src_entry_fini(src);
- }
- static int vxlan_mdb_config_attrs_init(struct vxlan_mdb_config *cfg,
- const struct br_mdb_entry *entry,
- const struct nlattr *set_attrs,
- struct netlink_ext_ack *extack)
- {
- struct nlattr *mdbe_attrs[MDBE_ATTR_MAX + 1];
- int err;
- err = nla_parse_nested(mdbe_attrs, MDBE_ATTR_MAX, set_attrs,
- vxlan_mdbe_attrs_pol, extack);
- if (err)
- return err;
- if (NL_REQ_ATTR_CHECK(extack, set_attrs, mdbe_attrs, MDBE_ATTR_DST)) {
- NL_SET_ERR_MSG_MOD(extack, "Missing remote destination IP address");
- return -EINVAL;
- }
- if (mdbe_attrs[MDBE_ATTR_SOURCE] &&
- !vxlan_mdb_is_valid_source(mdbe_attrs[MDBE_ATTR_SOURCE],
- entry->addr.proto, extack))
- return -EINVAL;
- vxlan_mdb_group_set(&cfg->group, entry, mdbe_attrs[MDBE_ATTR_SOURCE]);
- /* rtnetlink code only validates that IPv4 group address is
- * multicast.
- */
- if (!vxlan_addr_is_multicast(&cfg->group.dst) &&
- !vxlan_addr_any(&cfg->group.dst)) {
- NL_SET_ERR_MSG_MOD(extack, "Group address is not multicast");
- return -EINVAL;
- }
- if (vxlan_addr_any(&cfg->group.dst) &&
- mdbe_attrs[MDBE_ATTR_SOURCE]) {
- NL_SET_ERR_MSG_MOD(extack, "Source cannot be specified for the all-zeros entry");
- return -EINVAL;
- }
- if (vxlan_mdb_is_sg(&cfg->group))
- cfg->filter_mode = MCAST_INCLUDE;
- if (mdbe_attrs[MDBE_ATTR_GROUP_MODE]) {
- if (!vxlan_mdb_is_star_g(&cfg->group)) {
- NL_SET_ERR_MSG_MOD(extack, "Filter mode can only be set for (*, G) entries");
- return -EINVAL;
- }
- cfg->filter_mode = nla_get_u8(mdbe_attrs[MDBE_ATTR_GROUP_MODE]);
- }
- if (mdbe_attrs[MDBE_ATTR_SRC_LIST]) {
- if (!vxlan_mdb_is_star_g(&cfg->group)) {
- NL_SET_ERR_MSG_MOD(extack, "Source list can only be set for (*, G) entries");
- return -EINVAL;
- }
- if (!mdbe_attrs[MDBE_ATTR_GROUP_MODE]) {
- NL_SET_ERR_MSG_MOD(extack, "Source list cannot be set without filter mode");
- return -EINVAL;
- }
- err = vxlan_mdb_config_src_list_init(cfg, entry->addr.proto,
- mdbe_attrs[MDBE_ATTR_SRC_LIST],
- extack);
- if (err)
- return err;
- }
- if (vxlan_mdb_is_star_g(&cfg->group) && list_empty(&cfg->src_list) &&
- cfg->filter_mode == MCAST_INCLUDE) {
- NL_SET_ERR_MSG_MOD(extack, "Cannot add (*, G) INCLUDE with an empty source list");
- return -EINVAL;
- }
- if (mdbe_attrs[MDBE_ATTR_RTPROT])
- cfg->rt_protocol = nla_get_u8(mdbe_attrs[MDBE_ATTR_RTPROT]);
- err = vxlan_nla_get_addr(&cfg->remote_ip, mdbe_attrs[MDBE_ATTR_DST]);
- if (err) {
- NL_SET_ERR_MSG_MOD(extack, "Invalid remote destination address");
- goto err_src_list_fini;
- }
- if (mdbe_attrs[MDBE_ATTR_DST_PORT])
- cfg->remote_port =
- cpu_to_be16(nla_get_u16(mdbe_attrs[MDBE_ATTR_DST_PORT]));
- if (mdbe_attrs[MDBE_ATTR_VNI])
- cfg->remote_vni =
- cpu_to_be32(nla_get_u32(mdbe_attrs[MDBE_ATTR_VNI]));
- if (mdbe_attrs[MDBE_ATTR_IFINDEX]) {
- cfg->remote_ifindex =
- nla_get_s32(mdbe_attrs[MDBE_ATTR_IFINDEX]);
- if (!__dev_get_by_index(cfg->vxlan->net, cfg->remote_ifindex)) {
- NL_SET_ERR_MSG_MOD(extack, "Outgoing interface not found");
- err = -EINVAL;
- goto err_src_list_fini;
- }
- }
- if (mdbe_attrs[MDBE_ATTR_SRC_VNI])
- cfg->group.vni =
- cpu_to_be32(nla_get_u32(mdbe_attrs[MDBE_ATTR_SRC_VNI]));
- return 0;
- err_src_list_fini:
- vxlan_mdb_config_src_list_fini(cfg);
- return err;
- }
- static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg,
- struct net_device *dev, struct nlattr *tb[],
- u16 nlmsg_flags,
- struct netlink_ext_ack *extack)
- {
- struct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]);
- struct vxlan_dev *vxlan = netdev_priv(dev);
- memset(cfg, 0, sizeof(*cfg));
- cfg->vxlan = vxlan;
- cfg->group.vni = vxlan->default_dst.remote_vni;
- INIT_LIST_HEAD(&cfg->src_list);
- cfg->nlflags = nlmsg_flags;
- cfg->filter_mode = MCAST_EXCLUDE;
- cfg->rt_protocol = RTPROT_STATIC;
- cfg->remote_vni = vxlan->default_dst.remote_vni;
- cfg->remote_port = vxlan->cfg.dst_port;
- if (entry->ifindex != dev->ifindex) {
- NL_SET_ERR_MSG_MOD(extack, "Port net device must be the VXLAN net device");
- return -EINVAL;
- }
- /* State is not part of the entry key and can be ignored on deletion
- * requests.
- */
- if ((nlmsg_flags & (NLM_F_CREATE | NLM_F_REPLACE)) &&
- entry->state != MDB_PERMANENT) {
- NL_SET_ERR_MSG_MOD(extack, "MDB entry must be permanent");
- return -EINVAL;
- }
- if (entry->flags) {
- NL_SET_ERR_MSG_MOD(extack, "Invalid MDB entry flags");
- return -EINVAL;
- }
- if (entry->vid) {
- NL_SET_ERR_MSG_MOD(extack, "VID must not be specified");
- return -EINVAL;
- }
- if (entry->addr.proto != htons(ETH_P_IP) &&
- entry->addr.proto != htons(ETH_P_IPV6)) {
- NL_SET_ERR_MSG_MOD(extack, "Group address must be an IPv4 / IPv6 address");
- return -EINVAL;
- }
- if (NL_REQ_ATTR_CHECK(extack, NULL, tb, MDBA_SET_ENTRY_ATTRS)) {
- NL_SET_ERR_MSG_MOD(extack, "Missing MDBA_SET_ENTRY_ATTRS attribute");
- return -EINVAL;
- }
- return vxlan_mdb_config_attrs_init(cfg, entry, tb[MDBA_SET_ENTRY_ATTRS],
- extack);
- }
- static void vxlan_mdb_config_fini(struct vxlan_mdb_config *cfg)
- {
- vxlan_mdb_config_src_list_fini(cfg);
- }
- static struct vxlan_mdb_entry *
- vxlan_mdb_entry_lookup(struct vxlan_dev *vxlan,
- const struct vxlan_mdb_entry_key *group)
- {
- return rhashtable_lookup_fast(&vxlan->mdb_tbl, group,
- vxlan_mdb_rht_params);
- }
- static struct vxlan_mdb_remote *
- vxlan_mdb_remote_lookup(const struct vxlan_mdb_entry *mdb_entry,
- const union vxlan_addr *addr)
- {
- struct vxlan_mdb_remote *remote;
- list_for_each_entry(remote, &mdb_entry->remotes, list) {
- struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
- if (vxlan_addr_equal(addr, &rd->remote_ip))
- return remote;
- }
- return NULL;
- }
- static void vxlan_mdb_rdst_free(struct rcu_head *head)
- {
- struct vxlan_rdst *rd = container_of(head, struct vxlan_rdst, rcu);
- dst_cache_destroy(&rd->dst_cache);
- kfree(rd);
- }
- static int vxlan_mdb_remote_rdst_init(const struct vxlan_mdb_config *cfg,
- struct vxlan_mdb_remote *remote)
- {
- struct vxlan_rdst *rd;
- int err;
- rd = kzalloc(sizeof(*rd), GFP_KERNEL);
- if (!rd)
- return -ENOMEM;
- err = dst_cache_init(&rd->dst_cache, GFP_KERNEL);
- if (err)
- goto err_free_rdst;
- rd->remote_ip = cfg->remote_ip;
- rd->remote_port = cfg->remote_port;
- rd->remote_vni = cfg->remote_vni;
- rd->remote_ifindex = cfg->remote_ifindex;
- rcu_assign_pointer(remote->rd, rd);
- return 0;
- err_free_rdst:
- kfree(rd);
- return err;
- }
- static void vxlan_mdb_remote_rdst_fini(struct vxlan_rdst *rd)
- {
- call_rcu(&rd->rcu, vxlan_mdb_rdst_free);
- }
- static int vxlan_mdb_remote_init(const struct vxlan_mdb_config *cfg,
- struct vxlan_mdb_remote *remote)
- {
- int err;
- err = vxlan_mdb_remote_rdst_init(cfg, remote);
- if (err)
- return err;
- remote->flags = cfg->flags;
- remote->filter_mode = cfg->filter_mode;
- remote->rt_protocol = cfg->rt_protocol;
- INIT_HLIST_HEAD(&remote->src_list);
- return 0;
- }
- static void vxlan_mdb_remote_fini(struct vxlan_dev *vxlan,
- struct vxlan_mdb_remote *remote)
- {
- WARN_ON_ONCE(!hlist_empty(&remote->src_list));
- vxlan_mdb_remote_rdst_fini(rtnl_dereference(remote->rd));
- }
- static struct vxlan_mdb_src_entry *
- vxlan_mdb_remote_src_entry_lookup(const struct vxlan_mdb_remote *remote,
- const union vxlan_addr *addr)
- {
- struct vxlan_mdb_src_entry *ent;
- hlist_for_each_entry(ent, &remote->src_list, node) {
- if (vxlan_addr_equal(&ent->addr, addr))
- return ent;
- }
- return NULL;
- }
- static struct vxlan_mdb_src_entry *
- vxlan_mdb_remote_src_entry_add(struct vxlan_mdb_remote *remote,
- const union vxlan_addr *addr)
- {
- struct vxlan_mdb_src_entry *ent;
- ent = kzalloc(sizeof(*ent), GFP_KERNEL);
- if (!ent)
- return NULL;
- ent->addr = *addr;
- hlist_add_head(&ent->node, &remote->src_list);
- return ent;
- }
- static void
- vxlan_mdb_remote_src_entry_del(struct vxlan_mdb_src_entry *ent)
- {
- hlist_del(&ent->node);
- kfree(ent);
- }
- static int
- vxlan_mdb_remote_src_fwd_add(const struct vxlan_mdb_config *cfg,
- const union vxlan_addr *addr,
- struct netlink_ext_ack *extack)
- {
- struct vxlan_mdb_config sg_cfg;
- memset(&sg_cfg, 0, sizeof(sg_cfg));
- sg_cfg.vxlan = cfg->vxlan;
- sg_cfg.group.src = *addr;
- sg_cfg.group.dst = cfg->group.dst;
- sg_cfg.group.vni = cfg->group.vni;
- INIT_LIST_HEAD(&sg_cfg.src_list);
- sg_cfg.remote_ip = cfg->remote_ip;
- sg_cfg.remote_ifindex = cfg->remote_ifindex;
- sg_cfg.remote_vni = cfg->remote_vni;
- sg_cfg.remote_port = cfg->remote_port;
- sg_cfg.nlflags = cfg->nlflags;
- sg_cfg.filter_mode = MCAST_INCLUDE;
- if (cfg->filter_mode == MCAST_EXCLUDE)
- sg_cfg.flags = VXLAN_MDB_REMOTE_F_BLOCKED;
- sg_cfg.rt_protocol = cfg->rt_protocol;
- return __vxlan_mdb_add(&sg_cfg, extack);
- }
- static void
- vxlan_mdb_remote_src_fwd_del(struct vxlan_dev *vxlan,
- const struct vxlan_mdb_entry_key *group,
- const struct vxlan_mdb_remote *remote,
- const union vxlan_addr *addr)
- {
- struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
- struct vxlan_mdb_config sg_cfg;
- memset(&sg_cfg, 0, sizeof(sg_cfg));
- sg_cfg.vxlan = vxlan;
- sg_cfg.group.src = *addr;
- sg_cfg.group.dst = group->dst;
- sg_cfg.group.vni = group->vni;
- INIT_LIST_HEAD(&sg_cfg.src_list);
- sg_cfg.remote_ip = rd->remote_ip;
- __vxlan_mdb_del(&sg_cfg, NULL);
- }
- static int
- vxlan_mdb_remote_src_add(const struct vxlan_mdb_config *cfg,
- struct vxlan_mdb_remote *remote,
- const struct vxlan_mdb_config_src_entry *src,
- struct netlink_ext_ack *extack)
- {
- struct vxlan_mdb_src_entry *ent;
- int err;
- ent = vxlan_mdb_remote_src_entry_lookup(remote, &src->addr);
- if (!ent) {
- ent = vxlan_mdb_remote_src_entry_add(remote, &src->addr);
- if (!ent)
- return -ENOMEM;
- } else if (!(cfg->nlflags & NLM_F_REPLACE)) {
- NL_SET_ERR_MSG_MOD(extack, "Source entry already exists");
- return -EEXIST;
- }
- err = vxlan_mdb_remote_src_fwd_add(cfg, &ent->addr, extack);
- if (err)
- goto err_src_del;
- /* Clear flags in case source entry was marked for deletion as part of
- * replace flow.
- */
- ent->flags = 0;
- return 0;
- err_src_del:
- vxlan_mdb_remote_src_entry_del(ent);
- return err;
- }
- static void vxlan_mdb_remote_src_del(struct vxlan_dev *vxlan,
- const struct vxlan_mdb_entry_key *group,
- const struct vxlan_mdb_remote *remote,
- struct vxlan_mdb_src_entry *ent)
- {
- vxlan_mdb_remote_src_fwd_del(vxlan, group, remote, &ent->addr);
- vxlan_mdb_remote_src_entry_del(ent);
- }
- static int vxlan_mdb_remote_srcs_add(const struct vxlan_mdb_config *cfg,
- struct vxlan_mdb_remote *remote,
- struct netlink_ext_ack *extack)
- {
- struct vxlan_mdb_config_src_entry *src;
- struct vxlan_mdb_src_entry *ent;
- struct hlist_node *tmp;
- int err;
- list_for_each_entry(src, &cfg->src_list, node) {
- err = vxlan_mdb_remote_src_add(cfg, remote, src, extack);
- if (err)
- goto err_src_del;
- }
- return 0;
- err_src_del:
- hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node)
- vxlan_mdb_remote_src_del(cfg->vxlan, &cfg->group, remote, ent);
- return err;
- }
- static void vxlan_mdb_remote_srcs_del(struct vxlan_dev *vxlan,
- const struct vxlan_mdb_entry_key *group,
- struct vxlan_mdb_remote *remote)
- {
- struct vxlan_mdb_src_entry *ent;
- struct hlist_node *tmp;
- hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node)
- vxlan_mdb_remote_src_del(vxlan, group, remote, ent);
- }
- static size_t
- vxlan_mdb_nlmsg_src_list_size(const struct vxlan_mdb_entry_key *group,
- const struct vxlan_mdb_remote *remote)
- {
- struct vxlan_mdb_src_entry *ent;
- size_t nlmsg_size;
- if (hlist_empty(&remote->src_list))
- return 0;
- /* MDBA_MDB_EATTR_SRC_LIST */
- nlmsg_size = nla_total_size(0);
- hlist_for_each_entry(ent, &remote->src_list, node) {
- /* MDBA_MDB_SRCLIST_ENTRY */
- nlmsg_size += nla_total_size(0) +
- /* MDBA_MDB_SRCATTR_ADDRESS */
- nla_total_size(vxlan_addr_size(&group->dst)) +
- /* MDBA_MDB_SRCATTR_TIMER */
- nla_total_size(sizeof(u8));
- }
- return nlmsg_size;
- }
- static size_t
- vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
- const struct vxlan_mdb_entry *mdb_entry,
- const struct vxlan_mdb_remote *remote)
- {
- const struct vxlan_mdb_entry_key *group = &mdb_entry->key;
- struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
- size_t nlmsg_size;
- /* MDBA_MDB_ENTRY_INFO */
- nlmsg_size = nla_total_size(sizeof(struct br_mdb_entry)) +
- /* MDBA_MDB_EATTR_TIMER */
- nla_total_size(sizeof(u32));
- /* MDBA_MDB_EATTR_SOURCE */
- if (vxlan_mdb_is_sg(group))
- nlmsg_size += nla_total_size(vxlan_addr_size(&group->dst));
- /* MDBA_MDB_EATTR_RTPROT */
- nlmsg_size += nla_total_size(sizeof(u8));
- /* MDBA_MDB_EATTR_SRC_LIST */
- nlmsg_size += vxlan_mdb_nlmsg_src_list_size(group, remote);
- /* MDBA_MDB_EATTR_GROUP_MODE */
- nlmsg_size += nla_total_size(sizeof(u8));
- /* MDBA_MDB_EATTR_DST */
- nlmsg_size += nla_total_size(vxlan_addr_size(&rd->remote_ip));
- /* MDBA_MDB_EATTR_DST_PORT */
- if (rd->remote_port && rd->remote_port != vxlan->cfg.dst_port)
- nlmsg_size += nla_total_size(sizeof(u16));
- /* MDBA_MDB_EATTR_VNI */
- if (rd->remote_vni != vxlan->default_dst.remote_vni)
- nlmsg_size += nla_total_size(sizeof(u32));
- /* MDBA_MDB_EATTR_IFINDEX */
- if (rd->remote_ifindex)
- nlmsg_size += nla_total_size(sizeof(u32));
- /* MDBA_MDB_EATTR_SRC_VNI */
- if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) && group->vni)
- nlmsg_size += nla_total_size(sizeof(u32));
- return nlmsg_size;
- }
- static size_t vxlan_mdb_nlmsg_size(const struct vxlan_dev *vxlan,
- const struct vxlan_mdb_entry *mdb_entry,
- const struct vxlan_mdb_remote *remote)
- {
- return NLMSG_ALIGN(sizeof(struct br_port_msg)) +
- /* MDBA_MDB */
- nla_total_size(0) +
- /* MDBA_MDB_ENTRY */
- nla_total_size(0) +
- /* Remote entry */
- vxlan_mdb_nlmsg_remote_size(vxlan, mdb_entry, remote);
- }
- static int vxlan_mdb_nlmsg_fill(const struct vxlan_dev *vxlan,
- struct sk_buff *skb,
- const struct vxlan_mdb_entry *mdb_entry,
- const struct vxlan_mdb_remote *remote,
- int type)
- {
- struct nlattr *mdb_nest, *mdb_entry_nest;
- struct br_port_msg *bpm;
- struct nlmsghdr *nlh;
- nlh = nlmsg_put(skb, 0, 0, type, sizeof(*bpm), 0);
- if (!nlh)
- return -EMSGSIZE;
- bpm = nlmsg_data(nlh);
- memset(bpm, 0, sizeof(*bpm));
- bpm->family = AF_BRIDGE;
- bpm->ifindex = vxlan->dev->ifindex;
- mdb_nest = nla_nest_start_noflag(skb, MDBA_MDB);
- if (!mdb_nest)
- goto cancel;
- mdb_entry_nest = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY);
- if (!mdb_entry_nest)
- goto cancel;
- if (vxlan_mdb_entry_info_fill(vxlan, skb, mdb_entry, remote))
- goto cancel;
- nla_nest_end(skb, mdb_entry_nest);
- nla_nest_end(skb, mdb_nest);
- nlmsg_end(skb, nlh);
- return 0;
- cancel:
- nlmsg_cancel(skb, nlh);
- return -EMSGSIZE;
- }
- static void vxlan_mdb_remote_notify(const struct vxlan_dev *vxlan,
- const struct vxlan_mdb_entry *mdb_entry,
- const struct vxlan_mdb_remote *remote,
- int type)
- {
- struct net *net = dev_net(vxlan->dev);
- struct sk_buff *skb;
- int err = -ENOBUFS;
- skb = nlmsg_new(vxlan_mdb_nlmsg_size(vxlan, mdb_entry, remote),
- GFP_KERNEL);
- if (!skb)
- goto errout;
- err = vxlan_mdb_nlmsg_fill(vxlan, skb, mdb_entry, remote, type);
- if (err) {
- kfree_skb(skb);
- goto errout;
- }
- rtnl_notify(skb, net, 0, RTNLGRP_MDB, NULL, GFP_KERNEL);
- return;
- errout:
- rtnl_set_sk_err(net, RTNLGRP_MDB, err);
- }
- static int
- vxlan_mdb_remote_srcs_replace(const struct vxlan_mdb_config *cfg,
- const struct vxlan_mdb_entry *mdb_entry,
- struct vxlan_mdb_remote *remote,
- struct netlink_ext_ack *extack)
- {
- struct vxlan_dev *vxlan = cfg->vxlan;
- struct vxlan_mdb_src_entry *ent;
- struct hlist_node *tmp;
- int err;
- hlist_for_each_entry(ent, &remote->src_list, node)
- ent->flags |= VXLAN_SGRP_F_DELETE;
- err = vxlan_mdb_remote_srcs_add(cfg, remote, extack);
- if (err)
- goto err_clear_delete;
- hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) {
- if (ent->flags & VXLAN_SGRP_F_DELETE)
- vxlan_mdb_remote_src_del(vxlan, &mdb_entry->key, remote,
- ent);
- }
- return 0;
- err_clear_delete:
- hlist_for_each_entry(ent, &remote->src_list, node)
- ent->flags &= ~VXLAN_SGRP_F_DELETE;
- return err;
- }
- static int vxlan_mdb_remote_replace(const struct vxlan_mdb_config *cfg,
- const struct vxlan_mdb_entry *mdb_entry,
- struct vxlan_mdb_remote *remote,
- struct netlink_ext_ack *extack)
- {
- struct vxlan_rdst *new_rd, *old_rd = rtnl_dereference(remote->rd);
- struct vxlan_dev *vxlan = cfg->vxlan;
- int err;
- err = vxlan_mdb_remote_rdst_init(cfg, remote);
- if (err)
- return err;
- new_rd = rtnl_dereference(remote->rd);
- err = vxlan_mdb_remote_srcs_replace(cfg, mdb_entry, remote, extack);
- if (err)
- goto err_rdst_reset;
- WRITE_ONCE(remote->flags, cfg->flags);
- WRITE_ONCE(remote->filter_mode, cfg->filter_mode);
- remote->rt_protocol = cfg->rt_protocol;
- vxlan_mdb_remote_notify(vxlan, mdb_entry, remote, RTM_NEWMDB);
- vxlan_mdb_remote_rdst_fini(old_rd);
- return 0;
- err_rdst_reset:
- rcu_assign_pointer(remote->rd, old_rd);
- vxlan_mdb_remote_rdst_fini(new_rd);
- return err;
- }
- static int vxlan_mdb_remote_add(const struct vxlan_mdb_config *cfg,
- struct vxlan_mdb_entry *mdb_entry,
- struct netlink_ext_ack *extack)
- {
- struct vxlan_mdb_remote *remote;
- int err;
- remote = vxlan_mdb_remote_lookup(mdb_entry, &cfg->remote_ip);
- if (remote) {
- if (!(cfg->nlflags & NLM_F_REPLACE)) {
- NL_SET_ERR_MSG_MOD(extack, "Replace not specified and MDB remote entry already exists");
- return -EEXIST;
- }
- return vxlan_mdb_remote_replace(cfg, mdb_entry, remote, extack);
- }
- if (!(cfg->nlflags & NLM_F_CREATE)) {
- NL_SET_ERR_MSG_MOD(extack, "Create not specified and entry does not exist");
- return -ENOENT;
- }
- remote = kzalloc(sizeof(*remote), GFP_KERNEL);
- if (!remote)
- return -ENOMEM;
- err = vxlan_mdb_remote_init(cfg, remote);
- if (err) {
- NL_SET_ERR_MSG_MOD(extack, "Failed to initialize remote MDB entry");
- goto err_free_remote;
- }
- err = vxlan_mdb_remote_srcs_add(cfg, remote, extack);
- if (err)
- goto err_remote_fini;
- list_add_rcu(&remote->list, &mdb_entry->remotes);
- vxlan_mdb_remote_notify(cfg->vxlan, mdb_entry, remote, RTM_NEWMDB);
- return 0;
- err_remote_fini:
- vxlan_mdb_remote_fini(cfg->vxlan, remote);
- err_free_remote:
- kfree(remote);
- return err;
- }
- static void vxlan_mdb_remote_del(struct vxlan_dev *vxlan,
- struct vxlan_mdb_entry *mdb_entry,
- struct vxlan_mdb_remote *remote)
- {
- vxlan_mdb_remote_notify(vxlan, mdb_entry, remote, RTM_DELMDB);
- list_del_rcu(&remote->list);
- vxlan_mdb_remote_srcs_del(vxlan, &mdb_entry->key, remote);
- vxlan_mdb_remote_fini(vxlan, remote);
- kfree_rcu(remote, rcu);
- }
- static struct vxlan_mdb_entry *
- vxlan_mdb_entry_get(struct vxlan_dev *vxlan,
- const struct vxlan_mdb_entry_key *group)
- {
- struct vxlan_mdb_entry *mdb_entry;
- int err;
- mdb_entry = vxlan_mdb_entry_lookup(vxlan, group);
- if (mdb_entry)
- return mdb_entry;
- mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL);
- if (!mdb_entry)
- return ERR_PTR(-ENOMEM);
- INIT_LIST_HEAD(&mdb_entry->remotes);
- memcpy(&mdb_entry->key, group, sizeof(mdb_entry->key));
- hlist_add_head(&mdb_entry->mdb_node, &vxlan->mdb_list);
- err = rhashtable_lookup_insert_fast(&vxlan->mdb_tbl,
- &mdb_entry->rhnode,
- vxlan_mdb_rht_params);
- if (err)
- goto err_free_entry;
- if (hlist_is_singular_node(&mdb_entry->mdb_node, &vxlan->mdb_list))
- vxlan->cfg.flags |= VXLAN_F_MDB;
- return mdb_entry;
- err_free_entry:
- hlist_del(&mdb_entry->mdb_node);
- kfree(mdb_entry);
- return ERR_PTR(err);
- }
- static void vxlan_mdb_entry_put(struct vxlan_dev *vxlan,
- struct vxlan_mdb_entry *mdb_entry)
- {
- if (!list_empty(&mdb_entry->remotes))
- return;
- if (hlist_is_singular_node(&mdb_entry->mdb_node, &vxlan->mdb_list))
- vxlan->cfg.flags &= ~VXLAN_F_MDB;
- rhashtable_remove_fast(&vxlan->mdb_tbl, &mdb_entry->rhnode,
- vxlan_mdb_rht_params);
- hlist_del(&mdb_entry->mdb_node);
- kfree_rcu(mdb_entry, rcu);
- }
- static int __vxlan_mdb_add(const struct vxlan_mdb_config *cfg,
- struct netlink_ext_ack *extack)
- {
- struct vxlan_dev *vxlan = cfg->vxlan;
- struct vxlan_mdb_entry *mdb_entry;
- int err;
- mdb_entry = vxlan_mdb_entry_get(vxlan, &cfg->group);
- if (IS_ERR(mdb_entry))
- return PTR_ERR(mdb_entry);
- err = vxlan_mdb_remote_add(cfg, mdb_entry, extack);
- if (err)
- goto err_entry_put;
- vxlan->mdb_seq++;
- return 0;
- err_entry_put:
- vxlan_mdb_entry_put(vxlan, mdb_entry);
- return err;
- }
- static int __vxlan_mdb_del(const struct vxlan_mdb_config *cfg,
- struct netlink_ext_ack *extack)
- {
- struct vxlan_dev *vxlan = cfg->vxlan;
- struct vxlan_mdb_entry *mdb_entry;
- struct vxlan_mdb_remote *remote;
- mdb_entry = vxlan_mdb_entry_lookup(vxlan, &cfg->group);
- if (!mdb_entry) {
- NL_SET_ERR_MSG_MOD(extack, "Did not find MDB entry");
- return -ENOENT;
- }
- remote = vxlan_mdb_remote_lookup(mdb_entry, &cfg->remote_ip);
- if (!remote) {
- NL_SET_ERR_MSG_MOD(extack, "Did not find MDB remote entry");
- return -ENOENT;
- }
- vxlan_mdb_remote_del(vxlan, mdb_entry, remote);
- vxlan_mdb_entry_put(vxlan, mdb_entry);
- vxlan->mdb_seq++;
- return 0;
- }
- int vxlan_mdb_add(struct net_device *dev, struct nlattr *tb[], u16 nlmsg_flags,
- struct netlink_ext_ack *extack)
- {
- struct vxlan_mdb_config cfg;
- int err;
- ASSERT_RTNL();
- err = vxlan_mdb_config_init(&cfg, dev, tb, nlmsg_flags, extack);
- if (err)
- return err;
- err = __vxlan_mdb_add(&cfg, extack);
- vxlan_mdb_config_fini(&cfg);
- return err;
- }
- int vxlan_mdb_del(struct net_device *dev, struct nlattr *tb[],
- struct netlink_ext_ack *extack)
- {
- struct vxlan_mdb_config cfg;
- int err;
- ASSERT_RTNL();
- err = vxlan_mdb_config_init(&cfg, dev, tb, 0, extack);
- if (err)
- return err;
- err = __vxlan_mdb_del(&cfg, extack);
- vxlan_mdb_config_fini(&cfg);
- 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),
- sizeof(struct in6_addr)),
- [MDBE_ATTR_SRC_VNI] = NLA_POLICY_FULL_RANGE(NLA_U32, &vni_range),
- };
- static int vxlan_mdb_get_parse(struct net_device *dev, struct nlattr *tb[],
- struct vxlan_mdb_entry_key *group,
- struct netlink_ext_ack *extack)
- {
- struct br_mdb_entry *entry = nla_data(tb[MDBA_GET_ENTRY]);
- struct nlattr *mdbe_attrs[MDBE_ATTR_MAX + 1];
- struct vxlan_dev *vxlan = netdev_priv(dev);
- int err;
- memset(group, 0, sizeof(*group));
- group->vni = vxlan->default_dst.remote_vni;
- if (!tb[MDBA_GET_ENTRY_ATTRS]) {
- vxlan_mdb_group_set(group, entry, NULL);
- return 0;
- }
- err = nla_parse_nested(mdbe_attrs, MDBE_ATTR_MAX,
- tb[MDBA_GET_ENTRY_ATTRS],
- vxlan_mdbe_attrs_get_pol, extack);
- if (err)
- return err;
- if (mdbe_attrs[MDBE_ATTR_SOURCE] &&
- !vxlan_mdb_is_valid_source(mdbe_attrs[MDBE_ATTR_SOURCE],
- entry->addr.proto, extack))
- return -EINVAL;
- vxlan_mdb_group_set(group, entry, mdbe_attrs[MDBE_ATTR_SOURCE]);
- if (mdbe_attrs[MDBE_ATTR_SRC_VNI])
- group->vni =
- cpu_to_be32(nla_get_u32(mdbe_attrs[MDBE_ATTR_SRC_VNI]));
- return 0;
- }
- static struct sk_buff *
- vxlan_mdb_get_reply_alloc(const struct vxlan_dev *vxlan,
- const struct vxlan_mdb_entry *mdb_entry)
- {
- struct vxlan_mdb_remote *remote;
- size_t nlmsg_size;
- nlmsg_size = NLMSG_ALIGN(sizeof(struct br_port_msg)) +
- /* MDBA_MDB */
- nla_total_size(0) +
- /* MDBA_MDB_ENTRY */
- nla_total_size(0);
- list_for_each_entry(remote, &mdb_entry->remotes, list)
- nlmsg_size += vxlan_mdb_nlmsg_remote_size(vxlan, mdb_entry,
- remote);
- return nlmsg_new(nlmsg_size, GFP_KERNEL);
- }
- static int
- vxlan_mdb_get_reply_fill(const struct vxlan_dev *vxlan,
- struct sk_buff *skb,
- const struct vxlan_mdb_entry *mdb_entry,
- u32 portid, u32 seq)
- {
- struct nlattr *mdb_nest, *mdb_entry_nest;
- struct vxlan_mdb_remote *remote;
- struct br_port_msg *bpm;
- struct nlmsghdr *nlh;
- int err;
- nlh = nlmsg_put(skb, portid, seq, RTM_NEWMDB, sizeof(*bpm), 0);
- if (!nlh)
- return -EMSGSIZE;
- bpm = nlmsg_data(nlh);
- memset(bpm, 0, sizeof(*bpm));
- bpm->family = AF_BRIDGE;
- bpm->ifindex = vxlan->dev->ifindex;
- mdb_nest = nla_nest_start_noflag(skb, MDBA_MDB);
- if (!mdb_nest) {
- err = -EMSGSIZE;
- goto cancel;
- }
- mdb_entry_nest = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY);
- if (!mdb_entry_nest) {
- err = -EMSGSIZE;
- goto cancel;
- }
- list_for_each_entry(remote, &mdb_entry->remotes, list) {
- err = vxlan_mdb_entry_info_fill(vxlan, skb, mdb_entry, remote);
- if (err)
- goto cancel;
- }
- nla_nest_end(skb, mdb_entry_nest);
- nla_nest_end(skb, mdb_nest);
- nlmsg_end(skb, nlh);
- return 0;
- cancel:
- nlmsg_cancel(skb, nlh);
- return err;
- }
- int vxlan_mdb_get(struct net_device *dev, struct nlattr *tb[], u32 portid,
- u32 seq, struct netlink_ext_ack *extack)
- {
- struct vxlan_dev *vxlan = netdev_priv(dev);
- struct vxlan_mdb_entry *mdb_entry;
- struct vxlan_mdb_entry_key group;
- struct sk_buff *skb;
- int err;
- ASSERT_RTNL();
- err = vxlan_mdb_get_parse(dev, tb, &group, extack);
- if (err)
- return err;
- mdb_entry = vxlan_mdb_entry_lookup(vxlan, &group);
- if (!mdb_entry) {
- NL_SET_ERR_MSG_MOD(extack, "MDB entry not found");
- return -ENOENT;
- }
- skb = vxlan_mdb_get_reply_alloc(vxlan, mdb_entry);
- if (!skb)
- return -ENOMEM;
- err = vxlan_mdb_get_reply_fill(vxlan, skb, mdb_entry, portid, seq);
- if (err) {
- NL_SET_ERR_MSG_MOD(extack, "Failed to fill MDB get reply");
- goto free;
- }
- return rtnl_unicast(skb, dev_net(dev), portid);
- free:
- kfree_skb(skb);
- return err;
- }
- struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
- struct sk_buff *skb,
- __be32 src_vni)
- {
- struct vxlan_mdb_entry *mdb_entry;
- struct vxlan_mdb_entry_key group;
- if (!is_multicast_ether_addr(eth_hdr(skb)->h_dest) ||
- is_broadcast_ether_addr(eth_hdr(skb)->h_dest))
- return NULL;
- /* When not in collect metadata mode, 'src_vni' is zero, but MDB
- * entries are stored with the VNI of the VXLAN device.
- */
- if (!(vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA))
- src_vni = vxlan->default_dst.remote_vni;
- memset(&group, 0, sizeof(group));
- group.vni = src_vni;
- switch (skb->protocol) {
- case htons(ETH_P_IP):
- if (!pskb_may_pull(skb, sizeof(struct iphdr)))
- return NULL;
- group.dst.sa.sa_family = AF_INET;
- group.dst.sin.sin_addr.s_addr = ip_hdr(skb)->daddr;
- group.src.sa.sa_family = AF_INET;
- group.src.sin.sin_addr.s_addr = ip_hdr(skb)->saddr;
- break;
- #if IS_ENABLED(CONFIG_IPV6)
- case htons(ETH_P_IPV6):
- if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
- return NULL;
- group.dst.sa.sa_family = AF_INET6;
- group.dst.sin6.sin6_addr = ipv6_hdr(skb)->daddr;
- group.src.sa.sa_family = AF_INET6;
- group.src.sin6.sin6_addr = ipv6_hdr(skb)->saddr;
- break;
- #endif
- default:
- return NULL;
- }
- mdb_entry = vxlan_mdb_entry_lookup(vxlan, &group);
- if (mdb_entry)
- return mdb_entry;
- memset(&group.src, 0, sizeof(group.src));
- mdb_entry = vxlan_mdb_entry_lookup(vxlan, &group);
- if (mdb_entry)
- return mdb_entry;
- /* No (S, G) or (*, G) found. Look up the all-zeros entry, but only if
- * the destination IP address is not link-local multicast since we want
- * to transmit such traffic together with broadcast and unknown unicast
- * traffic.
- */
- switch (skb->protocol) {
- case htons(ETH_P_IP):
- if (ipv4_is_local_multicast(group.dst.sin.sin_addr.s_addr))
- return NULL;
- group.dst.sin.sin_addr.s_addr = 0;
- break;
- #if IS_ENABLED(CONFIG_IPV6)
- case htons(ETH_P_IPV6):
- if (ipv6_addr_type(&group.dst.sin6.sin6_addr) &
- IPV6_ADDR_LINKLOCAL)
- return NULL;
- memset(&group.dst.sin6.sin6_addr, 0,
- sizeof(group.dst.sin6.sin6_addr));
- break;
- #endif
- default:
- return NULL;
- }
- return vxlan_mdb_entry_lookup(vxlan, &group);
- }
- netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,
- const struct vxlan_mdb_entry *mdb_entry,
- struct sk_buff *skb)
- {
- struct vxlan_mdb_remote *remote, *fremote = NULL;
- __be32 src_vni = mdb_entry->key.vni;
- list_for_each_entry_rcu(remote, &mdb_entry->remotes, list) {
- struct sk_buff *skb1;
- if ((vxlan_mdb_is_star_g(&mdb_entry->key) &&
- READ_ONCE(remote->filter_mode) == MCAST_INCLUDE) ||
- (READ_ONCE(remote->flags) & VXLAN_MDB_REMOTE_F_BLOCKED))
- continue;
- if (!fremote) {
- fremote = remote;
- continue;
- }
- skb1 = skb_clone(skb, GFP_ATOMIC);
- if (skb1)
- vxlan_xmit_one(skb1, vxlan->dev, src_vni,
- rcu_dereference(remote->rd), false);
- }
- if (fremote)
- vxlan_xmit_one(skb, vxlan->dev, src_vni,
- rcu_dereference(fremote->rd), false);
- else
- kfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);
- return NETDEV_TX_OK;
- }
- static void vxlan_mdb_check_empty(void *ptr, void *arg)
- {
- WARN_ON_ONCE(1);
- }
- int vxlan_mdb_init(struct vxlan_dev *vxlan)
- {
- int err;
- err = rhashtable_init(&vxlan->mdb_tbl, &vxlan_mdb_rht_params);
- if (err)
- return err;
- INIT_HLIST_HEAD(&vxlan->mdb_list);
- return 0;
- }
- void vxlan_mdb_fini(struct vxlan_dev *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);
- }
|