From 9eadec9f866e1bb9e313cfe332af4893eeb7f227 Mon Sep 17 00:00:00 2001 From: Christophe Fontaine Date: Sat, 6 Jul 2024 12:38:17 +0000 Subject: [PATCH] infra/port: implement unicast & multicast l2 filtering - wrappers to call rte_eth_dev_mac_addr_[add|remove] - Add API to filter multicast addresses on ports resolves #27 Signed-off-by: Christophe Fontaine --- modules/infra/api/gr_infra.h | 10 +- modules/infra/cli/port.c | 16 ++++ modules/infra/control/gr_port.h | 26 ++++++ modules/infra/control/port.c | 161 ++++++++++++++++++++++++++++++++ 4 files changed, 212 insertions(+), 1 deletion(-) diff --git a/modules/infra/api/gr_infra.h b/modules/infra/api/gr_infra.h index 3a5409ba..8153e7ef 100644 --- a/modules/infra/api/gr_infra.h +++ b/modules/infra/api/gr_infra.h @@ -42,7 +42,7 @@ struct gr_iface { uint16_t vrf_id; // L3 addressing and routing domain #define GR_IFACE_NAME_SIZE 64 char name[GR_IFACE_NAME_SIZE]; // Interface name (utf-8 encoded, nul terminated). - uint8_t info[256]; // Type specific interface info. + uint8_t info[2048]; // Type specific interface info. }; // Port reconfig attributes @@ -51,6 +51,10 @@ struct gr_iface { #define GR_PORT_SET_Q_SIZE GR_BIT64(34) #define GR_PORT_SET_MAC GR_BIT64(35) +struct gr_port_mac_addr { + uint16_t refcnt; + struct eth_addr mac; +}; // Info for GR_IFACE_TYPE_PORT interfaces struct gr_iface_info_port { #define GR_PORT_DEVARGS_SIZE 64 @@ -60,6 +64,10 @@ struct gr_iface_info_port { uint16_t rxq_size; uint16_t txq_size; struct eth_addr mac; +#define GR_PORT_UCAST_ADDR_SIZE 128 + struct eth_addr unicast[GR_PORT_UCAST_ADDR_SIZE]; +#define GR_PORT_MCAST_ADDR_SIZE 32 + struct gr_port_mac_addr multicast[GR_PORT_MCAST_ADDR_SIZE]; }; static_assert(sizeof(struct gr_iface_info_port) <= MEMBER_SIZE(struct gr_iface, info)); diff --git a/modules/infra/cli/port.c b/modules/infra/cli/port.c index ac0c65fa..3b9bc7e5 100644 --- a/modules/infra/cli/port.c +++ b/modules/infra/cli/port.c @@ -17,12 +17,28 @@ static void port_show(const struct gr_api_client *, const struct gr_iface *iface) { const struct gr_iface_info_port *port = (const struct gr_iface_info_port *)iface->info; + struct eth_addr empty_mac = {0}; + printf("devargs: %s\n", port->devargs); printf("mac: " ETH_ADDR_FMT "\n", ETH_BYTES_SPLIT(port->mac.bytes)); printf("n_rxq: %u\n", port->n_rxq); printf("n_txq: %u\n", port->n_txq); printf("rxq_size: %u\n", port->rxq_size); printf("txq_size: %u\n", port->txq_size); + + for (int i = 0; i < GR_PORT_MCAST_ADDR_SIZE; i++) { + if (memcmp(port->unicast[i].bytes, empty_mac.bytes, sizeof(empty_mac))) { + printf("additional mac: " ETH_ADDR_FMT "\n", + ETH_BYTES_SPLIT(port->unicast[i].bytes)); + } + } + for (int i = 0; i < GR_PORT_MCAST_ADDR_SIZE; i++) { + if (port->multicast[i].refcnt) { + printf("mcast mac: " ETH_ADDR_FMT " used by %d nodes\n", + ETH_BYTES_SPLIT(port->multicast[i].mac.bytes), + port->multicast[i].refcnt); + } + } } static void diff --git a/modules/infra/control/gr_port.h b/modules/infra/control/gr_port.h index b2c94c3c..74faa9f0 100644 --- a/modules/infra/control/gr_port.h +++ b/modules/infra/control/gr_port.h @@ -6,12 +6,30 @@ #include +#include #include #include #include #include +#define GR_PORT_MCAST_ADDR_MAX 32 + +enum { + PORT_FLAG_PROMISC_REQUESTED_BY_USER = GR_BIT16(0), + PORT_FLAG_PROMISC_DEVICE_LIMIT = GR_BIT16(1), + PORT_FLAG_PROMISC_TOO_MANY_UNICAST = GR_BIT16(2), + + PORT_FLAG_ALLMULTI_REQUESTED_BY_USER = GR_BIT16(3), + PORT_FLAG_ALLMULTI_DEVICE_LIMIT = GR_BIT16(4), + PORT_FLAG_ALLMULTI_TOO_MANY_MCAST = GR_BIT16(5), +}; + +struct port_mac_addr { + uint16_t refcnt; + struct rte_ether_addr mac; +}; + struct __rte_aligned(alignof(void *)) iface_info_port { uint16_t port_id; uint8_t n_rxq; @@ -22,6 +40,9 @@ struct __rte_aligned(alignof(void *)) iface_info_port { struct rte_ether_addr mac; struct rte_mempool *pool; char *devargs; + uint16_t flags; + struct rte_ether_addr unicast[RTE_ETH_NUM_RECEIVE_MAC_ADDR]; + struct port_mac_addr multicast[GR_PORT_MCAST_ADDR_MAX]; }; uint32_t port_get_rxq_buffer_us(uint16_t port_id, uint16_t rxq_id); @@ -35,4 +56,9 @@ int iface_port_reconfig( ); const struct iface *port_get_iface(uint16_t port_id); +int gr_port_mcast_addr_add(struct iface_info_port *p, struct rte_ether_addr *mcast_addr); +int gr_port_mcast_addr_del(struct iface_info_port *p, struct rte_ether_addr *mcast_addr); + +int gr_port_unicast_addr_add(struct iface_info_port *p, struct rte_ether_addr *mcast_addr); +int gr_port_unicast_addr_del(struct iface_info_port *p, struct rte_ether_addr *mcast_addr); #endif diff --git a/modules/infra/control/port.c b/modules/infra/control/port.c index d5a987ba..0e2aceb1 100644 --- a/modules/infra/control/port.c +++ b/modules/infra/control/port.c @@ -409,6 +409,9 @@ static int iface_port_init(struct iface *iface, const void *api_info) { if (port->devargs == NULL) goto fail; + memset(port->unicast, 0, sizeof(port->unicast)); + memset(port->multicast, 0, sizeof(port->multicast)); + ret = iface_port_reconfig( iface, IFACE_SET_ALL, iface->flags, iface->mtu, iface->vrf_id, api_info ); @@ -444,6 +447,16 @@ static void port_to_api(void *info, const struct iface *iface) { api->n_txq = port->n_txq; api->rxq_size = port->rxq_size; api->txq_size = port->txq_size; + + memcpy(api->multicast, port->multicast, sizeof(api->multicast)); + + rte_eth_macaddrs_get( + port->port_id, + (struct rte_ether_addr *)port->unicast, + sizeof(port->unicast) / sizeof(port->unicast[0]) + ); + + memcpy(api->unicast, port->unicast, sizeof(api->unicast)); } static struct iface_type iface_type_port = { @@ -457,6 +470,154 @@ static struct iface_type iface_type_port = { .to_api = port_to_api, }; +static int mcast_count(struct iface_info_port *p) { + int count = 0; + for (int i = 0; i < GR_PORT_MCAST_ADDR_MAX; i++) { + if (p->multicast[i].refcnt) + count++; + } + return count; +} + +int gr_port_mcast_addr_add(struct iface_info_port *p, struct rte_ether_addr *mcast_addr) { + struct rte_ether_addr mac[GR_PORT_MCAST_ADDR_MAX]; + uint16_t count = 0; + int ret; + int i; + + if (p->flags & PORT_FLAG_ALLMULTI_TOO_MANY_MCAST) { + // allmulti is already enabled, internal mcast addr list overflow + return 0; + } + + if (mcast_count(p) == GR_PORT_MCAST_ADDR_MAX) { + p->flags |= PORT_FLAG_ALLMULTI_TOO_MANY_MCAST; + goto enable_allmulti; + } + + for (i = 0; i < GR_PORT_MCAST_ADDR_MAX; i++) { + if (rte_is_same_ether_addr(mcast_addr, &p->multicast[i].mac)) { + LOG(DEBUG, "multicast address already filtered by port %d", p->port_id); + p->multicast[i].refcnt++; + return 0; + } + } + + for (i = 0; i < GR_PORT_MCAST_ADDR_MAX; i++) { + if (p->multicast[i].refcnt == 0) { + memcpy(&p->multicast[i].mac, mcast_addr, sizeof(struct rte_ether_addr)); + p->multicast[i].refcnt = 1; + } + } + + for (i = 0; i < mcast_count(p); i++) { + memcpy(&mac[i], &p->multicast[i].mac, sizeof(struct rte_ether_addr)); + } + + ret = rte_eth_dev_set_mc_addr_list(p->port_id, mac, count); + + if (ret == 0) { + return 0; + } else if (ret == -ENOSPC) { + LOG(INFO, + "Enabling mc addr list: no space left on iface %s.", + port_get_iface(p->port_id)->name); + p->flags |= PORT_FLAG_ALLMULTI_DEVICE_LIMIT; + } else if (ret == -ENOTSUP) { + LOG(INFO, + "Enabling mc addr list not supported for iface %s", + port_get_iface(p->port_id)->name); + p->flags |= PORT_FLAG_ALLMULTI_TOO_MANY_MCAST; + } + +enable_allmulti: + return rte_eth_allmulticast_enable(p->port_id); +} + +static int mac_refcnt_qsort(const void *p1, const void *p2) { + const struct port_mac_addr *m1 = p1; + const struct port_mac_addr *m2 = p2; + + return m1->refcnt - m2->refcnt; +} + +int gr_port_mcast_addr_del(struct iface_info_port *p, struct rte_ether_addr *mcast_addr) { + struct rte_ether_addr mac[GR_PORT_MCAST_ADDR_MAX]; + int i; + + if (p->flags & PORT_FLAG_ALLMULTI_TOO_MANY_MCAST) { + // can't disable all multi as we weren't able to keep track of requests; + return 0; + } + + for (i = 0; i < GR_PORT_MCAST_ADDR_MAX; i++) { + if (rte_is_same_ether_addr(mcast_addr, &p->multicast[i].mac)) { + p->multicast[i].refcnt--; + if (p->multicast[i].refcnt == 0) { + memset(&p->multicast[i].mac, 0, sizeof(struct rte_ether_addr)); + } + } + } + + if (i == GR_PORT_MCAST_ADDR_MAX) { + LOG(INFO, + "multicast addr wasn't allowed for iface %s", + port_get_iface(p->port_id)->name); + return -ENODATA; + } + + qsort(p, mcast_count(p), sizeof(p->multicast[0]), mac_refcnt_qsort); + + p->flags &= ~PORT_FLAG_ALLMULTI_DEVICE_LIMIT; + for (i = 0; i < mcast_count(p); i++) { + memcpy(&mac[i], &p->multicast[i].mac, sizeof(struct rte_ether_addr)); + } + + if (rte_eth_dev_set_mc_addr_list(p->port_id, mac, mcast_count(p)) < 0) { + LOG(ERR, + "enabling mc addr list failed on iface %s.", + port_get_iface(p->port_id)->name); + p->flags |= PORT_FLAG_ALLMULTI_DEVICE_LIMIT; + return rte_eth_allmulticast_enable(p->port_id); + } + + if (p->flags & ~(PORT_FLAG_ALLMULTI_REQUESTED_BY_USER | PORT_FLAG_ALLMULTI_DEVICE_LIMIT)) { + rte_eth_allmulticast_disable(p->port_id); + } + + return 0; +} + +int gr_port_unicast_addr_add(struct iface_info_port *p, struct rte_ether_addr *ucast_addr) { + int ret; + + if (p->flags & PORT_FLAG_PROMISC_TOO_MANY_UNICAST || p->flags & PORT_FLAG_PROMISC_DEVICE_LIMIT) + return 0; + + ret = rte_eth_dev_mac_addr_add(p->port_id, ucast_addr, 0); + switch (ret) { + case 0: + return 0; + break; + case -ENOTSUP: + p->flags |= PORT_FLAG_PROMISC_TOO_MANY_UNICAST; + break; + case -ENOSPC: + p->flags |= PORT_FLAG_PROMISC_DEVICE_LIMIT; + break; + default: + return ret; + break; + } + + return rte_eth_promiscuous_enable(p->port_id); +} + +int gr_port_unicast_addr_del(struct iface_info_port *p, struct rte_ether_addr *ucast_addr) { + int ret = rte_eth_dev_mac_addr_remove(p->port_id, ucast_addr); + return ret; +} + RTE_INIT(port_constructor) { iface_type_register(&iface_type_port); }