Skip to content

Commit

Permalink
infra/port: implement unicast & multicast l2 filtering
Browse files Browse the repository at this point in the history
- 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 <cfontain@redhat.com>
  • Loading branch information
christophefontaine committed Jul 8, 2024
1 parent e7e19ba commit 9eadec9
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 1 deletion.
10 changes: 9 additions & 1 deletion modules/infra/api/gr_infra.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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));
Expand Down
16 changes: 16 additions & 0 deletions modules/infra/cli/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 26 additions & 0 deletions modules/infra/control/gr_port.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,30 @@

#include <gr_iface.h>

#include <rte_ethdev.h>
#include <rte_ether.h>
#include <rte_mempool.h>

#include <stdint.h>
#include <sys/queue.h>

#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;
Expand All @@ -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);
Expand All @@ -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
161 changes: 161 additions & 0 deletions modules/infra/control/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
Expand Down Expand Up @@ -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 = {
Expand All @@ -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);
}

0 comments on commit 9eadec9

Please sign in to comment.