Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions modules/infra/api/gr_infra.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ enum gr_infra_requests : uint32_t {
GR_PACKET_TRACE_SET,
GR_AFFINITY_CPU_GET,
GR_AFFINITY_CPU_SET,
GR_IFACE_MAC_ADD,
GR_IFACE_MAC_DEL,
GR_IFACE_MAC_LIST,
GR_IFACE_MAC_SET,
};

enum gr_infra_events : uint32_t {
Expand Down Expand Up @@ -312,6 +316,44 @@ struct gr_iface_list_req {

GR_REQ_STREAM(GR_IFACE_LIST, struct gr_iface_list_req, struct gr_iface);

// Add a secondary MAC address to an interface.
struct gr_iface_mac_add_req {
uint16_t iface_id;
struct rte_ether_addr mac;
};

GR_REQ(GR_IFACE_MAC_ADD, struct gr_iface_mac_add_req, struct gr_empty);

// Remove a secondary MAC address from an interface.
struct gr_iface_mac_del_req {
uint16_t iface_id;
struct rte_ether_addr mac;
};

GR_REQ(GR_IFACE_MAC_DEL, struct gr_iface_mac_del_req, struct gr_empty);

// List MAC addresses on an interface.
struct gr_iface_mac_list_req {
uint16_t iface_id; // GR_IFACE_ID_UNDEF for all interfaces.
};

struct gr_iface_mac {
uint16_t iface_id;
uint16_t refcnt;
bool primary;
struct rte_ether_addr mac;
};

GR_REQ_STREAM(GR_IFACE_MAC_LIST, struct gr_iface_mac_list_req, struct gr_iface_mac);

// Set the primary MAC address of an interface.
struct gr_iface_mac_set_req {
uint16_t iface_id;
struct rte_ether_addr mac;
};

GR_REQ(GR_IFACE_MAC_SET, struct gr_iface_mac_set_req, struct gr_empty);

// Modify an existing interface.
// MTU changes on parent interfaces propagate to VLAN sub-interfaces.
struct gr_iface_set_req {
Expand Down
85 changes: 85 additions & 0 deletions modules/infra/api/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,87 @@ static struct api_out iface_list(const void *request, struct api_ctx *ctx) {
return api_out(ret, 0, NULL);
}

static struct api_out iface_mac_add(const void *request, struct api_ctx *) {
const struct gr_iface_mac_add_req *req = request;
struct iface *iface;
int ret;

if ((iface = iface_from_id(req->iface_id)) == NULL)
return api_out(ENODEV, 0, NULL);

ret = iface_add_eth_addr(iface, &req->mac);
if (ret < 0)
return api_out(errno, 0, NULL);

return api_out(0, 0, NULL);
}

static struct api_out iface_mac_del(const void *request, struct api_ctx *) {
const struct gr_iface_mac_del_req *req = request;
struct iface *iface;
int ret;

if ((iface = iface_from_id(req->iface_id)) == NULL)
return api_out(ENODEV, 0, NULL);

ret = iface_del_eth_addr(iface, &req->mac);
if (ret < 0)
return api_out(errno, 0, NULL);

return api_out(0, 0, NULL);
}

static struct api_out iface_mac_list(const void *request, struct api_ctx *ctx) {
const struct gr_iface_mac_list_req *req = request;
const struct iface *iface = NULL;
int ret = 0;

while ((iface = iface_next(GR_IFACE_TYPE_UNDEF, iface)) != NULL) {
if (req->iface_id != GR_IFACE_ID_UNDEF && iface->id != req->iface_id)
continue;

// Send primary MAC address first
struct rte_ether_addr primary_mac;
if (iface_get_eth_addr(iface, &primary_mac) == 0) {
struct gr_iface_mac mac;

mac.iface_id = iface->id;
mac.refcnt = 0;
mac.primary = true;
mac.mac = primary_mac;
api_send(ctx, sizeof(mac), &mac);
}

// Send secondary MAC addresses
vec_foreach_ref (struct iface_mac *m, iface->macs) {
struct gr_iface_mac mac;

mac.iface_id = iface->id;
mac.refcnt = m->refcnt;
mac.primary = false;
mac.mac = m->mac;
api_send(ctx, sizeof(mac), &mac);
}
}

return api_out(ret, 0, NULL);
}

static struct api_out iface_mac_set(const void *request, struct api_ctx *) {
const struct gr_iface_mac_set_req *req = request;
struct iface *iface;
int ret;

if ((iface = iface_from_id(req->iface_id)) == NULL)
return api_out(ENODEV, 0, NULL);

ret = iface_set_eth_addr(iface, &req->mac);
if (ret < 0)
return api_out(errno, 0, NULL);

return api_out(0, 0, NULL);
}

static struct api_out iface_set(const void *request, struct api_ctx *) {
const struct gr_iface_set_req *req = request;
int ret;
Expand Down Expand Up @@ -232,6 +313,10 @@ RTE_INIT(infra_api_init) {
api_handler(GR_IFACE_DEL, iface_del);
api_handler(GR_IFACE_GET, iface_get);
api_handler(GR_IFACE_LIST, iface_list);
api_handler(GR_IFACE_MAC_ADD, iface_mac_add);
api_handler(GR_IFACE_MAC_DEL, iface_mac_del);
api_handler(GR_IFACE_MAC_LIST, iface_mac_list);
api_handler(GR_IFACE_MAC_SET, iface_mac_set);
api_handler(GR_IFACE_SET, iface_set);
event_serializer(GR_EVENT_IFACE_ADD, iface_event_serialize);
event_serializer(GR_EVENT_IFACE_POST_ADD, iface_event_serialize);
Expand Down
162 changes: 162 additions & 0 deletions modules/infra/cli/mac.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 David Marchand

#include "cli.h"
#include "cli_iface.h"
#include "display.h"

#include <gr_api.h>
#include <gr_infra.h>
#include <gr_net_types.h>

#include <ecoli.h>

#include <errno.h>

static cmd_status_t mac_add(struct gr_api_client *c, const struct ec_pnode *p) {
struct gr_iface_mac_add_req req = {0};

if (arg_iface(c, p, "IFACE", GR_IFACE_TYPE_UNDEF, &req.iface_id) < 0)
return CMD_ERROR;

if (arg_eth_addr(p, "MAC", &req.mac) < 0)
return CMD_ERROR;

if (gr_api_client_send_recv(c, GR_IFACE_MAC_ADD, sizeof(req), &req, NULL) < 0)
return CMD_ERROR;

return CMD_SUCCESS;
}

static cmd_status_t mac_del(struct gr_api_client *c, const struct ec_pnode *p) {
struct gr_iface_mac_del_req req = {0};

if (arg_iface(c, p, "IFACE", GR_IFACE_TYPE_UNDEF, &req.iface_id) < 0)
return CMD_ERROR;

if (arg_eth_addr(p, "MAC", &req.mac) < 0)
return CMD_ERROR;

if (gr_api_client_send_recv(c, GR_IFACE_MAC_DEL, sizeof(req), &req, NULL) < 0)
return CMD_ERROR;

return CMD_SUCCESS;
}

static cmd_status_t mac_list(struct gr_api_client *c, const struct ec_pnode *p) {
struct gr_iface_mac_list_req req = {.iface_id = GR_IFACE_ID_UNDEF};
const struct gr_iface_mac *mac;
int ret;

if (arg_str(p, "IFACE") != NULL) {
if (arg_iface(c, p, "IFACE", GR_IFACE_TYPE_UNDEF, &req.iface_id) < 0)
return CMD_ERROR;
}

struct gr_table *table = gr_table_new();
gr_table_column(table, "IFACE", GR_DISP_LEFT);
gr_table_column(table, "MAC", GR_DISP_LEFT);
gr_table_column(table, "REFCNT", GR_DISP_RIGHT | GR_DISP_INT);

gr_api_client_stream_foreach (mac, ret, c, GR_IFACE_MAC_LIST, sizeof(req), &req) {
gr_table_cell(table, 0, "%s", iface_name_from_id(c, mac->iface_id));
gr_table_cell(table, 1, ETH_F, &mac->mac);
if (mac->primary)
gr_table_cell(table, 2, "-");
else
gr_table_cell(table, 2, "%u", mac->refcnt);
if (gr_table_print_row(table) < 0)
break;
}

gr_table_free(table);

return ret < 0 ? CMD_ERROR : CMD_SUCCESS;
}

static cmd_status_t mac_set(struct gr_api_client *c, const struct ec_pnode *p) {
struct gr_iface_mac_set_req req = {0};

if (arg_iface(c, p, "IFACE", GR_IFACE_TYPE_UNDEF, &req.iface_id) < 0)
return CMD_ERROR;

if (arg_eth_addr(p, "MAC", &req.mac) < 0)
return CMD_ERROR;

if (gr_api_client_send_recv(c, GR_IFACE_MAC_SET, sizeof(req), &req, NULL) < 0)
return CMD_ERROR;

return CMD_SUCCESS;
}

#define MAC_CTX(root) CLI_CONTEXT(root, CTX_ARG("mac", "MAC address management."))

static int ctx_init(struct ec_node *root) {
int ret;

ret = CLI_COMMAND(
MAC_CTX(root),
"add MAC iface IFACE",
mac_add,
"Add a secondary MAC address to an interface.",
with_help("MAC address.", ec_node_re("MAC", ETH_ADDR_RE)),
with_help(
"Interface name.",
ec_node_dyn("IFACE", complete_iface_names, INT2PTR(GR_IFACE_TYPE_UNDEF))
)
);
if (ret < 0)
return ret;

ret = CLI_COMMAND(
MAC_CTX(root),
"del MAC iface IFACE",
mac_del,
"Remove a secondary MAC address from an interface.",
with_help("MAC address.", ec_node_re("MAC", ETH_ADDR_RE)),
with_help(
"Interface name.",
ec_node_dyn("IFACE", complete_iface_names, INT2PTR(GR_IFACE_TYPE_UNDEF))
)
);
if (ret < 0)
return ret;

ret = CLI_COMMAND(
MAC_CTX(root),
"set MAC iface IFACE",
mac_set,
"Set the primary MAC address of an interface.",
with_help("MAC address.", ec_node_re("MAC", ETH_ADDR_RE)),
with_help(
"Interface name.",
ec_node_dyn("IFACE", complete_iface_names, INT2PTR(GR_IFACE_TYPE_UNDEF))
)
);
if (ret < 0)
return ret;

ret = CLI_COMMAND(
MAC_CTX(root),
"[show] [iface IFACE]",
mac_list,
"Display MAC addresses.",
with_help(
"Interface name.",
ec_node_dyn("IFACE", complete_iface_names, INT2PTR(GR_IFACE_TYPE_UNDEF))
)
);
if (ret < 0)
return ret;

return 0;
}

static struct cli_context ctx = {
.name = "mac",
.init = ctx_init,
};

static void __attribute__((constructor, used)) init(void) {
cli_context_register(&ctx);
}
1 change: 1 addition & 0 deletions modules/infra/cli/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ cli_src += files(
'graph.c',
'icmp.c',
'iface.c',
'mac.c',
'vrf.c',
'nexthop.c',
'port.c',
Expand Down
34 changes: 8 additions & 26 deletions modules/infra/control/bond.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,32 +40,16 @@ bond_all_member_del_mac(const struct iface_info_bond *bond, const struct rte_eth
return 0;
}

static int bond_mac_add(struct iface *iface, const struct rte_ether_addr *mac) {
static int bond_mac_add(struct iface *iface, struct iface_mac *m) {
struct iface_info_bond *bond = iface_info_bond(iface);
int ret;

// Add MAC address to all member ports
if ((ret = bond_all_member_add_mac(bond, mac)) < 0)
return ret;

vec_add(bond->extra_macs, *mac);

return 0;
return bond_all_member_add_mac(bond, &m->mac);
}

static int bond_mac_del(struct iface *iface, const struct rte_ether_addr *mac) {
static int bond_mac_del(struct iface *iface, struct iface_mac *m) {
struct iface_info_bond *bond = iface_info_bond(iface);

// Remove MAC address from all member ports
bond_all_member_del_mac(bond, mac);

for (unsigned i = 0; i < vec_len(bond->extra_macs); i++) {
if (rte_is_same_ether_addr(&bond->extra_macs[i], mac)) {
vec_del(bond->extra_macs, i);
break;
}
}

bond_all_member_del_mac(bond, &m->mac);
return 0;
}

Expand Down Expand Up @@ -173,8 +157,8 @@ static int bond_attach_member(struct iface *iface, struct iface *member) {
if (bond->n_members == ARRAY_DIM(bond->members))
return errno_set(EUSERS);

vec_foreach_ref (struct rte_ether_addr *mac, bond->extra_macs) {
if (iface_add_eth_addr(member, mac) < 0)
vec_foreach_ref (struct iface_mac *m, iface->macs) {
if (iface_add_eth_addr(member, &m->mac) < 0)
return errno_log(errno, "iface_add_eth_addr(member)");
}

Expand Down Expand Up @@ -223,8 +207,8 @@ static int bond_detach_member(struct iface *iface, struct iface *member) {
bond->n_members--;
if (bond->primary_member >= bond->n_members)
bond->primary_member--;
vec_foreach_ref (struct rte_ether_addr *mac, bond->extra_macs) {
if (iface_del_eth_addr(member, mac) < 0 && errno != ENOENT) {
vec_foreach_ref (struct iface_mac *m, iface->macs) {
if (iface_del_eth_addr(member, &m->mac) < 0 && errno != ENOENT) {
LOG(WARNING,
"failed to unconfigure mac address on member %s: %s",
member->name,
Expand Down Expand Up @@ -412,8 +396,6 @@ static int bond_fini(struct iface *iface) {
event_push(GR_EVENT_IFACE_POST_RECONFIG, member);
}

vec_free(bond->extra_macs);

return 0;
}

Expand Down
Loading
Loading