Skip to content
Merged
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
764 changes: 400 additions & 364 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.
49 changes: 42 additions & 7 deletions frr/rt_grout.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,12 @@ static int grout_gr_nexthop_to_frr_nexthop(
nexthop_add_srv6_seg6(nh, (void *)sr6->seglist, sr6->n_seglist, encap_behavior);
break;
}
case GR_NH_T_GROUP:
nh->ifindex = gr_nh->iface_id;
nh->vrf_id = gr_nh->vrf_id;
*nh_family = AF_UNSPEC;
nh->weight = 1;
break;
default:
gr_log_err(
"sync %s nexthops from grout not supported", gr_nh_type_name(gr_nh->type)
Expand Down Expand Up @@ -572,12 +578,44 @@ enum zebra_dplane_result grout_add_del_route(struct zebra_dplane_ctx *ctx) {
return ZEBRA_DPLANE_REQUEST_SUCCESS;
}

static enum zebra_dplane_result grout_add_nexthop_group(struct zebra_dplane_ctx *ctx) {
enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_SUCCESS;
uint32_t nh_id = dplane_ctx_get_nhe_id(ctx);
struct gr_nexthop_info_group *group;
struct gr_nh_add_req *req = NULL;
size_t len;

len = sizeof(*req) + sizeof(*group)
+ dplane_ctx_get_nhe_nh_grp_count(ctx) * sizeof(group->members[0]);
if ((req = calloc(1, len)) == NULL)
return ZEBRA_DPLANE_REQUEST_FAILURE;

group = (struct gr_nexthop_info_group *)req->nh.info;
group->n_members = dplane_ctx_get_nhe_nh_grp_count(ctx);

req->exist_ok = true;
req->nh.nh_id = nh_id;
req->nh.type = GR_NH_T_GROUP;
req->nh.origin = zebra2origin(dplane_ctx_get_nhe_type(ctx));

const struct nh_grp *nhs = dplane_ctx_get_nhe_nh_grp(ctx);
for (size_t i = 0; i < group->n_members; i++) {
group->members[i].nh_id = nhs[i].id;
group->members[i].weight = nhs[i].weight;
}

if (grout_client_send_recv(GR_NH_ADD, len, req, NULL) < 0)
ret = ZEBRA_DPLANE_REQUEST_FAILURE;

free(req);
return ret;
}

static enum zebra_dplane_result grout_del_nexthop(uint32_t nh_id) {
struct gr_nh_del_req req = {.missing_ok = true, .nh_id = nh_id};

if (grout_client_send_recv(GR_NH_DEL, sizeof(req), &req, NULL) < 0)
return ZEBRA_DPLANE_REQUEST_FAILURE;

return ZEBRA_DPLANE_REQUEST_SUCCESS;
}

Expand Down Expand Up @@ -762,15 +800,12 @@ enum zebra_dplane_result grout_add_del_nexthop(struct zebra_dplane_ctx *ctx) {
return ZEBRA_DPLANE_REQUEST_FAILURE;
}

if (dplane_ctx_get_nhe_nh_grp_count(ctx)) {
// next group are not supported in grout
gr_log_err("impossible to add/del nexthop grout %u (nhg not supported)", nh_id);
return ZEBRA_DPLANE_REQUEST_FAILURE;
}

if (dplane_ctx_get_op(ctx) == DPLANE_OP_NH_DELETE)
return grout_del_nexthop(nh_id);

if (dplane_ctx_get_nhe_nh_grp_count(ctx))
return grout_add_nexthop_group(ctx);

return grout_add_nexthop(nh_id, origin, dplane_ctx_get_nhe_ng(ctx)->nexthop);
}

Expand Down
14 changes: 14 additions & 0 deletions modules/infra/api/gr_nexthop.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ typedef enum : uint8_t {
GR_NH_T_DNAT,
GR_NH_T_BLACKHOLE,
GR_NH_T_REJECT,
GR_NH_T_GROUP, // ECMP
#define GR_NH_T_ALL UINT8_C(0xff)
} gr_nh_type_t;

Expand Down Expand Up @@ -99,6 +100,17 @@ struct gr_nexthop_info_l3 {
struct rte_ether_addr mac; //!< link-layer address
};

// Info for GR_NH_T_GROUP nexthops
struct gr_nexthop_group_member {
uint32_t nh_id;
uint32_t weight;
};

struct gr_nexthop_info_group {
uint32_t n_members;
struct gr_nexthop_group_member members[];
};

//! Nexthop structure exposed to the API.
struct gr_nexthop {
BASE(gr_nexthop_base);
Expand Down Expand Up @@ -166,6 +178,8 @@ static inline const char *gr_nh_type_name(const gr_nh_type_t type) {
return "blackhole";
case GR_NH_T_REJECT:
return "reject";
case GR_NH_T_GROUP:
return "group";
}
return "?";
}
Expand Down
86 changes: 86 additions & 0 deletions modules/infra/cli/nexthop.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,19 @@ static ssize_t format_nexthop_info_void(char *, size_t, const void *) {
return 0;
}

static ssize_t format_nexthop_info_group(char *buf, size_t len, const void *info) {
const struct gr_nexthop_info_group *grp = info;
ssize_t n = 0;

for (uint32_t i = 0; i < grp->n_members; i++)
SAFE_BUF(
snprintf, len, "id(%u/%u) ", grp->members[i].nh_id, grp->members[i].weight
);
return n;
err:
return -errno;
}

static struct cli_nexthop_formatter blackhole_formatter = {
.name = "blackhole",
.type = GR_NH_T_BLACKHOLE,
Expand All @@ -123,6 +136,12 @@ static struct cli_nexthop_formatter reject_formatter = {
.format = format_nexthop_info_void,
};

static struct cli_nexthop_formatter group_formatter = {
.name = "group",
.type = GR_NH_T_GROUP,
.format = format_nexthop_info_group,
};

static int complete_nh_types(
struct gr_api_client *,
const struct ec_node *node,
Expand Down Expand Up @@ -284,6 +303,54 @@ static cmd_status_t nh_del(struct gr_api_client *c, const struct ec_pnode *p) {
return CMD_SUCCESS;
}

static cmd_status_t nh_group_add(struct gr_api_client *c, const struct ec_pnode *p) {
struct gr_nexthop_info_group *group;
struct gr_nh_add_req *req = NULL;
const struct ec_pnode *n = NULL;
cmd_status_t ret = CMD_ERROR;
uint32_t n_members = 0;
size_t len;

while ((n = ec_pnode_find_next(p, n, "MEMBER", false)) != NULL) {
n_members++;
}
n = NULL;

len = sizeof(*req) + sizeof(*group) + n_members * sizeof(group->members[0]);
if ((req = calloc(1, len)) == NULL) {
errno = ENOMEM;
goto out;
}

req->exist_ok = true;
req->nh.type = GR_NH_T_GROUP;
req->nh.origin = GR_NH_ORIGIN_USER;

if (arg_u32(p, "ID", &req->nh.nh_id) < 0 && errno != ENOENT)
goto out;

group = (struct gr_nexthop_info_group *)req->nh.info;

while ((n = ec_pnode_find_next(p, n, "MEMBER", false)) != NULL) {
if (arg_u32(n, "NHID", &group->members[group->n_members].nh_id) < 0)
goto out;
if (arg_u32(n, "WEIGHT", &group->members[group->n_members].weight) < 0) {
if (errno == ENOENT)
group->members[group->n_members].weight = 1;
else
goto out;
}
group->n_members++;
}

if (gr_api_client_send_recv(c, GR_NH_ADD, len, req, NULL) < 0)
goto out;
ret = CMD_SUCCESS;
out:
free(req);
return ret;
}

static cmd_status_t nh_list(struct gr_api_client *c, const struct ec_pnode *p) {
struct gr_nh_list_req req = {.vrf_id = GR_VRF_ID_ALL, .type = GR_NH_T_ALL};
const struct gr_nexthop *nh;
Expand Down Expand Up @@ -414,6 +481,24 @@ static int ctx_init(struct ec_node *root) {
with_help("Blackhole nexthop.", ec_node_str("blackhole", "blackhole")),
with_help("Reject nexthop sending ICMP UNREACH.", ec_node_str("reject", "reject"))
);
if (ret < 0)
return ret;
ret = CLI_COMMAND(
NEXTHOP_ADD_CTX(root),
"group [(id ID)] (member MEMBER)*",
nh_group_add,
"Add a new nexthop group.",
with_help("Nexthop ID.", ec_node_uint("ID", 1, UINT32_MAX - 1, 10)),
with_help(
"Nexthop member ID with relative weight.",
EC_NODE_CMD(
"MEMBER",
"NHID [weight WEIGHT]",
ec_node_uint("NHID", 1, UINT32_MAX - 1, 10),
ec_node_uint("WEIGHT", 1, UINT32_MAX - 1, 10)
)
)
);
if (ret < 0)
return ret;
ret = CLI_COMMAND(
Expand Down Expand Up @@ -488,4 +573,5 @@ static void __attribute__((constructor, used)) init(void) {
cli_nexthop_formatter_register(&l3_formatter);
cli_nexthop_formatter_register(&blackhole_formatter);
cli_nexthop_formatter_register(&reject_formatter);
cli_nexthop_formatter_register(&group_formatter);
}
10 changes: 10 additions & 0 deletions modules/infra/control/gr_nh_control.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ struct hoplist {
gr_vec struct nexthop **nh;
};

struct nh_group_member {
struct nexthop *nh;
uint32_t weight;
};

GR_NH_TYPE_INFO(GR_NH_T_GROUP, nexthop_info_group, {
uint32_t n_members;
struct nh_group_member *members;
});

// Lookup a nexthop from the global pool that matches the specified criteria.
struct nexthop *
nexthop_lookup(addr_family_t af, uint16_t vrf_id, uint16_t iface_id, const void *addr);
Expand Down
Loading