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
984 changes: 510 additions & 474 deletions docs/graph.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions modules/infra/api/gr_infra.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,34 @@ struct gr_iface_info_vlan {
// Bond operational modes
typedef enum : uint8_t {
GR_BOND_MODE_ACTIVE_BACKUP = 1,
GR_BOND_MODE_LACP,
} gr_bond_mode_t;

static inline char *gr_bond_mode_name(gr_bond_mode_t mode) {
switch (mode) {
case GR_BOND_MODE_ACTIVE_BACKUP:
return "active-backup";
case GR_BOND_MODE_LACP:
return "lacp";
}
return "?";
}

// Bond balancing algorithms
typedef enum : uint8_t {
GR_BOND_ALGO_RSS = 1,
GR_BOND_ALGO_L2,
GR_BOND_ALGO_L3_L4,
} gr_bond_algo_t;

static inline char *gr_bond_algo_name(gr_bond_algo_t algo) {
switch (algo) {
case GR_BOND_ALGO_RSS:
return "rss";
case GR_BOND_ALGO_L2:
return "l2";
case GR_BOND_ALGO_L3_L4:
return "l3+l4";
}
return "?";
}
Expand All @@ -137,6 +159,7 @@ static inline char *gr_bond_mode_name(gr_bond_mode_t mode) {
#define GR_BOND_SET_MEMBERS GR_BIT64(33)
#define GR_BOND_SET_PRIMARY GR_BIT64(34)
#define GR_BOND_SET_MAC GR_BIT64(35)
#define GR_BOND_SET_ALGO GR_BIT64(36)

struct gr_bond_member {
uint16_t iface_id;
Expand All @@ -146,6 +169,7 @@ struct gr_bond_member {
// Info for GR_IFACE_TYPE_BOND interfaces
struct gr_iface_info_bond {
gr_bond_mode_t mode;
gr_bond_algo_t algo; // Only for LACP
struct rte_ether_addr mac;

uint8_t primary_member;
Expand Down
60 changes: 58 additions & 2 deletions modules/infra/cli/bond.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ static void bond_show(struct gr_api_client *c, const struct gr_iface *iface) {
const struct gr_iface_info_bond *bond = PAYLOAD(iface);

printf("mode: %s\n", gr_bond_mode_name(bond->mode));
if (bond->mode == GR_BOND_MODE_LACP)
printf("algo: %s\n", gr_bond_algo_name(bond->algo));
printf("mac: " ETH_F "\n", &bond->mac);
printf("members:\n");
for (uint8_t i = 0; i < bond->n_members; i++) {
Expand Down Expand Up @@ -71,6 +73,9 @@ bond_list_info(struct gr_api_client *c, const struct gr_iface *iface, char *buf,
else
SAFE_BUF(snprintf, len, " primary=%s", i->name);
break;
case GR_BOND_MODE_LACP:
SAFE_BUF(snprintf, len, " algo=%s", gr_bond_algo_name(bond->algo));
break;
}

err:
Expand All @@ -88,9 +93,29 @@ static int bond_mode_from_str(const char *str, gr_bond_mode_t *mode) {
*mode = GR_BOND_MODE_ACTIVE_BACKUP;
return 0;
}
if (strcmp(str, "lacp") == 0) {
*mode = GR_BOND_MODE_LACP;
return 0;
}
return errno_set(EPROTONOSUPPORT);
}

static int bond_algo_from_str(const char *str, gr_bond_algo_t *algo) {
if (strcmp(str, "rss") == 0) {
*algo = GR_BOND_ALGO_RSS;
return 0;
}
if (strcmp(str, "l2") == 0) {
*algo = GR_BOND_ALGO_L2;
return 0;
}
if (strcmp(str, "l3+l4") == 0) {
*algo = GR_BOND_ALGO_L3_L4;
return 0;
}
return errno_set(ESOCKTNOSUPPORT);
}

static uint64_t parse_bond_args(
struct gr_api_client *c,
const struct ec_pnode *p,
Expand All @@ -109,6 +134,16 @@ static uint64_t parse_bond_args(
set_attrs |= GR_BOND_SET_MODE;
}

if ((str = arg_str(p, "ALGO")) != NULL) {
if (bond->mode != GR_BOND_MODE_LACP) {
errno = EPROTOTYPE;
goto err;
}
if (bond_algo_from_str(str, &bond->algo) < 0)
goto err;
set_attrs |= GR_BOND_SET_ALGO;
}

if (arg_str(p, "MEMBER") != NULL) {
const struct ec_pnode *m = NULL;
bond->n_members = 0;
Expand All @@ -131,6 +166,10 @@ static uint64_t parse_bond_args(
}

if ((str = arg_str(p, "PRIMARY")) != NULL) {
if (bond->mode != GR_BOND_MODE_ACTIVE_BACKUP) {
errno = EPROTOTYPE;
goto err;
}
struct gr_iface *primary = iface_from_name(c, str);
if (primary == NULL)
goto err;
Expand Down Expand Up @@ -214,20 +253,37 @@ static cmd_status_t bond_set(struct gr_api_client *c, const struct ec_pnode *p)
return ret;
}

#define BOND_ATTRS_CMD IFACE_ATTRS_CMD ",(primary PRIMARY),(mac MAC)"
#define BOND_ATTRS_CMD IFACE_ATTRS_CMD ",((primary PRIMARY)|(balance ALGO)),(mac MAC)"
#define BOND_ATTRS_ARGS \
IFACE_ATTRS_ARGS, \
with_help( \
"Bond mode.", \
EC_NODE_OR( \
"MODE", \
with_help("Active backup mode.", ec_node_str("", "active-backup")) \
with_help( \
"Active backup mode.", ec_node_str("", "active-backup") \
), \
with_help("LACP mode.", ec_node_str("", "lacp")) \
) \
), \
with_help( \
"Primary member.", \
ec_node_dyn("PRIMARY", complete_iface_names, INT2PTR(GR_IFACE_TYPE_PORT)) \
), \
with_help( \
"Balancing algorithm.", \
EC_NODE_OR( \
"ALGO", \
with_help("Reuse hardware RSS hash.", ec_node_str("", "rss")), \
with_help( \
"Hash based on Ethernet and VLAN.", ec_node_str("", "l2") \
), \
with_help( \
"Hash based on IP/IPv6 and TCP/UDP.", \
ec_node_str("", "l3+l4") \
) \
) \
), \
with_help("Set the bond MAC address.", ec_node_re("MAC", ETH_ADDR_RE))

static int ctx_init(struct ec_node *root) {
Expand Down
91 changes: 79 additions & 12 deletions modules/infra/control/bond.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright (c) 2025 Robin Jarry

#include <gr_bond.h>
#include <gr_eth.h>
#include <gr_event.h>
#include <gr_infra.h>
#include <gr_log.h>
Expand Down Expand Up @@ -190,6 +191,8 @@ static int bond_init_new_members(const struct iface *iface, const struct gr_ifac
}

LOG(DEBUG, "adding %s to bond %s", member->name, iface->name);
if (iface_add_eth_addr(member->id, &LACP_DST_MAC) < 0)
return errno_log(errno, "iface_add_eth_addr(lacp)");
port = iface_info_port(member);
port->bond_iface_id = iface->id;
skip:;
Expand Down Expand Up @@ -225,17 +228,24 @@ static void bond_fini_old_members(const struct iface *iface, const struct gr_ifa
member->name,
strerror(errno));
}
if (iface_del_eth_addr(member->id, &LACP_DST_MAC) < 0) {
LOG(WARNING,
"failed to unconfigure mac address on member %s: %s",
member->name,
strerror(errno));
}

port = iface_info_port(member);
port->bond_iface_id = GR_IFACE_ID_UNDEF;
memset(&bond->members[i], 0, sizeof(bond->members[i]));
skip:;
}
}

static void bond_update_active_members(struct iface *iface) {
void bond_update_active_members(struct iface *iface) {
struct iface_info_bond *bond = iface_info_bond(iface);
uint8_t n_active_members = 0;
const struct iface *member;
uint8_t *active_ids = NULL;

switch (bond->mode) {
case GR_BOND_MODE_ACTIVE_BACKUP:
Expand All @@ -250,7 +260,7 @@ static void bond_update_active_members(struct iface *iface) {
for (uint8_t i = 0; i < bond->n_members; i++) {
member = bond->members[i].iface;
if (i == active_member) {
n_active_members = 1;
gr_vec_add(active_ids, i);
LOG(INFO,
"bond %s active member is now %s",
iface->name,
Expand All @@ -260,11 +270,66 @@ static void bond_update_active_members(struct iface *iface) {
}
bond->active_member = active_member;
break;
case GR_BOND_MODE_LACP:
for (uint8_t i = 0; i < bond->n_members; i++) {
struct bond_member *member = &bond->members[i];
const struct iface_info_port *port = iface_info_port(member->iface);

// The port_number must *never* be zero,
// otherwise some switches reject the LACP packets.
// Use a 1-based port_number.
member->local.port_number = rte_cpu_to_be_16(i + 1);
member->local.port_priority = RTE_BE16(0x8000);
member->local.system_priority = RTE_BE16(0x8000);
member->local.system_mac = bond->mac;
// Key based on port speed (in Mb/s): simplified encoding for aggregation
// Ports with same speed can aggregate together
member->local.key = rte_cpu_to_be_16(port->link_speed);
if (member->last_rx == 0) {
member->local.state = LACP_STATE_ACTIVE | LACP_STATE_AGGREGATABLE
| LACP_STATE_FAST | LACP_STATE_DEFAULTED
| LACP_STATE_EXPIRED;
member->active = false;
member->need_to_transmit = true;
member->next_tx = 0;
LOG(DEBUG,
"bond %s member %s reset local state",
iface->name,
member->iface->name);
}

// Add to active members if link is up and LACP member is valid
if ((member->iface->flags & GR_IFACE_F_UP)
&& (member->iface->state & GR_IFACE_S_RUNNING) && member->active) {
LOG(DEBUG,
"bond %s member %s active",
iface->name,
member->iface->name);
gr_vec_add(active_ids, i);
}
}
break;
}
if (n_active_members > 0)
iface->state |= GR_IFACE_S_RUNNING;
else
iface->state &= ~GR_IFACE_S_RUNNING;

if (gr_vec_len(active_ids) > 0) {
for (unsigned i = 0; i < ARRAY_DIM(bond->redirection_table); i++) {
bond->redirection_table[i] = active_ids[i % gr_vec_len(active_ids)];
}
if (!(iface->state & GR_IFACE_S_RUNNING)) {
iface->state |= GR_IFACE_S_RUNNING;
if (iface->flags & GR_IFACE_F_UP) {
gr_event_push(GR_EVENT_IFACE_STATUS_UP, iface);
}
}
} else {
memset(bond->redirection_table, UINT8_MAX, sizeof(bond->redirection_table));
if (iface->state & GR_IFACE_S_RUNNING) {
iface->state &= ~GR_IFACE_S_RUNNING;
gr_event_push(GR_EVENT_IFACE_STATUS_DOWN, iface);
}
}

gr_vec_free(active_ids);
}

static int bond_reconfig(
Expand All @@ -279,6 +344,9 @@ static int bond_reconfig(
if (set_attrs & GR_BOND_SET_MODE)
bond->mode = api->mode;

if (set_attrs & GR_BOND_SET_ALGO)
bond->algo = api->algo ?: GR_BOND_ALGO_RSS;

if (set_attrs & GR_BOND_SET_PRIMARY) {
uint8_t n_members = (set_attrs & GR_BOND_SET_MEMBERS) ?
api->n_members :
Expand Down Expand Up @@ -342,6 +410,7 @@ static void bond_to_api(void *info, const struct iface *iface) {
struct gr_iface_info_bond *api = info;

api->mode = bond->mode;
api->algo = bond->algo;
api->mac = bond->mac;
api->n_members = bond->n_members;
api->primary_member = bond->primary_member;
Expand All @@ -351,6 +420,9 @@ static void bond_to_api(void *info, const struct iface *iface) {
case GR_BOND_MODE_ACTIVE_BACKUP:
api->members[i].active = i == bond->active_member;
break;
case GR_BOND_MODE_LACP:
api->members[i].active = bond->members[i].active;
break;
}
}
}
Expand Down Expand Up @@ -393,11 +465,6 @@ static void bond_event(uint32_t, const void *obj) {
assert(b->type == GR_IFACE_TYPE_BOND);

bond_update_active_members(b);

if (b->state & GR_IFACE_S_RUNNING && b->flags & GR_IFACE_F_UP)
gr_event_push(GR_EVENT_IFACE_STATUS_UP, b);
else
gr_event_push(GR_EVENT_IFACE_STATUS_DOWN, b);
}

static struct gr_event_subscription bond_event_handler = {
Expand Down
13 changes: 13 additions & 0 deletions modules/infra/control/gr_bond.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <gr_iface.h>
#include <gr_infra.h>
#include <gr_lacp.h>
#include <gr_vec.h>

#include <stdint.h>
Expand All @@ -13,10 +14,18 @@

struct bond_member {
const struct iface *iface;
bool active;
bool need_to_transmit; // Need to send immediately
clock_t next_tx; // Next time we need to send a LACP packet
clock_t last_rx; // Last time we received a LACP packet
// For direct inclusion in LACP packets
struct lacp_participant local;
struct lacp_participant remote;
};

GR_IFACE_INFO(GR_IFACE_TYPE_BOND, iface_info_bond, {
gr_bond_mode_t mode;
gr_bond_algo_t algo;
struct rte_ether_addr mac;

uint8_t primary_member;
Expand All @@ -25,4 +34,8 @@ GR_IFACE_INFO(GR_IFACE_TYPE_BOND, iface_info_bond, {
struct bond_member members[MEMBERS_MAX_LEN];

gr_vec struct rte_ether_addr *extra_macs;

uint8_t redirection_table[256];
});

void bond_update_active_members(struct iface *);
Loading