Commit 1274e1cc4226 ("vxlan: ecmp support for mac fdb entries") lets a single inner MAC be reached through a group of remote VTEPs, with the kernel flow-hashing across the group members. Each member carries its own remote IP, but the UDP destination port is always taken from the VXLAN device (vxlan->cfg.dst_port) and cannot be set per member. Some deployments pack several receivers behind one underlay IP and tell them apart by UDP port, so they need a per-nexthop destination port to spread flows across (IP, port) tuples rather than IP alone. Add a netlink attribute NHA_DST_PORT (__be16, mirroring NDA_PORT) that carries an optional UDP destination port on an fdb nexthop. It is only accepted together with NHA_FDB and NHA_GATEWAY; it is stored in struct nh_info and echoed back on dump. The attribute is named generically rather than fdb-specific so it can be reused should another nexthop type ever need a destination port. This patch is control-plane plumbing only; the VXLAN datapath is wired up in a follow-up patch, so behaviour is unchanged for now. Signed-off-by: Jack Ma --- include/net/nexthop.h | 2 ++ include/uapi/linux/nexthop.h | 3 +++ net/ipv4/nexthop.c | 20 +++++++++++++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/net/nexthop.h b/include/net/nexthop.h index 572e69cda4766..7673aaeff3e28 100644 --- a/include/net/nexthop.h +++ b/include/net/nexthop.h @@ -28,6 +28,7 @@ struct nh_config { u8 nh_protocol; u8 nh_blackhole; u8 nh_fdb; + __be16 nh_dst_port; u32 nh_flags; int nh_ifindex; @@ -63,6 +64,7 @@ struct nh_info { u8 family; bool reject_nh; bool fdb_nh; + __be16 dst_port; union { struct fib_nh_common fib_nhc; diff --git a/include/uapi/linux/nexthop.h b/include/uapi/linux/nexthop.h index bc49baf4a267f..59cf1cee93bd7 100644 --- a/include/uapi/linux/nexthop.h +++ b/include/uapi/linux/nexthop.h @@ -83,6 +83,9 @@ enum { /* u32; read-only; whether any driver collects HW stats */ NHA_HW_STATS_USED, + /* be16; UDP destination port for an fdb nexthop (e.g. VXLAN) */ + NHA_DST_PORT, + __NHA_MAX, }; diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index 6205bd57aa852..0021380b8c1de 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -39,6 +39,7 @@ static const struct nla_policy rtm_nh_policy_new[] = { [NHA_ENCAP_TYPE] = { .type = NLA_U16 }, [NHA_ENCAP] = { .type = NLA_NESTED }, [NHA_FDB] = { .type = NLA_FLAG }, + [NHA_DST_PORT] = NLA_POLICY_MIN(NLA_BE16, 1), [NHA_RES_GROUP] = { .type = NLA_NESTED }, [NHA_HW_STATS_ENABLE] = NLA_POLICY_MAX(NLA_U32, true), }; @@ -956,6 +957,9 @@ static int nh_fill_node(struct sk_buff *skb, struct nexthop *nh, } else if (nhi->fdb_nh) { if (nla_put_flag(skb, NHA_FDB)) goto nla_put_failure; + if (nhi->dst_port && + nla_put_be16(skb, NHA_DST_PORT, nhi->dst_port)) + goto nla_put_failure; } else { const struct net_device *dev; @@ -1055,6 +1059,9 @@ static size_t nh_nlmsg_size_single(struct nexthop *nh) break; } + if (nhi->dst_port) + sz += nla_total_size(2); /* NHA_DST_PORT */ + if (nhi->fib_nhc.nhc_lwtstate) { sz += lwtunnel_get_encap_size(nhi->fib_nhc.nhc_lwtstate); sz += nla_total_size(2); /* NHA_ENCAP_TYPE */ @@ -2956,8 +2963,10 @@ static struct nexthop *nexthop_create(struct net *net, struct nh_config *cfg, nhi->family = cfg->nh_family; nhi->fib_nhc.nhc_scope = RT_SCOPE_LINK; - if (cfg->nh_fdb) + if (cfg->nh_fdb) { nhi->fdb_nh = 1; + nhi->dst_port = cfg->nh_dst_port; + } if (cfg->nh_blackhole) { nhi->reject_nh = 1; @@ -3147,6 +3156,15 @@ static int rtm_to_nh_config(struct net *net, struct sk_buff *skb, cfg->nh_fdb = nla_get_flag(tb[NHA_FDB]); } + if (tb[NHA_DST_PORT]) { + if (!tb[NHA_FDB] || !tb[NHA_GATEWAY]) { + NL_SET_ERR_MSG(extack, + "Destination port can only be set on fdb nexthops that have a gateway"); + goto out; + } + cfg->nh_dst_port = nla_get_be16(tb[NHA_DST_PORT]); + } + if (tb[NHA_GROUP]) { if (nhm->nh_family != AF_UNSPEC) { NL_SET_ERR_MSG(extack, "Invalid family for group"); -- 2.43.0