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
24 changes: 18 additions & 6 deletions frr/rt_grout.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ static int
grout_gr_nexthop_to_frr_nexthop(struct gr_nexthop *gr_nh, struct nexthop *nh, int *nh_family) {
size_t sz;

if (gr_nh->type == GR_NH_T_BLACKHOLE || gr_nh->type == GR_NH_T_REJECT) {
nh->vrf_id = gr_nh->vrf_id;
nh->type = NEXTHOP_TYPE_BLACKHOLE;
nh->bh_type = gr_nh->type == GR_NH_T_REJECT ? BLACKHOLE_REJECT : BLACKHOLE_NULL;
*nh_family = AF_UNSPEC;
nh->weight = 1;
return 0;
}

if (gr_nh->type != GR_NH_T_L3) {
gr_log_err("sync nexthop not L3 from grout is not supported");
return -1;
Expand Down Expand Up @@ -781,10 +790,6 @@ enum zebra_dplane_result grout_add_del_nexthop(struct zebra_dplane_ctx *ctx) {
}

nh = dplane_ctx_get_nhe_ng(ctx)->nexthop;
if (nh->type == NEXTHOP_TYPE_BLACKHOLE) {
gr_log_err("impossible to add/del blackhole nexthop (not supported)");
return ZEBRA_DPLANE_REQUEST_FAILURE;
}
if (nh->nh_srv6) {
gr_log_err("impossible to add/del srv6 nexthop (not supported)");
return ZEBRA_DPLANE_REQUEST_SUCCESS;
Expand Down Expand Up @@ -820,11 +825,12 @@ enum zebra_dplane_result grout_add_del_nexthop(struct zebra_dplane_ctx *ctx) {
else
gr_nh->af = GR_AF_IP6;

if (!nh->ifindex) {
if (nh->type != NEXTHOP_TYPE_BLACKHOLE && !nh->ifindex) {
gr_log_err("impossible to add/del nexthop in grout that does not have an ifindex");
return ZEBRA_DPLANE_REQUEST_FAILURE;
} else {
gr_nh->iface_id = nh->ifindex;
}
gr_nh->iface_id = nh->ifindex;

switch (nh->type) {
case NEXTHOP_TYPE_IPV4:
Expand All @@ -843,6 +849,12 @@ enum zebra_dplane_result grout_add_del_nexthop(struct zebra_dplane_ctx *ctx) {
gr_nh->af = GR_AF_UNSPEC;
gr_log_debug("add nexthop id %u with ifindex %u", nh_id, gr_nh->iface_id);
break;
case NEXTHOP_TYPE_BLACKHOLE:
gr_nh->type = nh->bh_type == BLACKHOLE_REJECT ? GR_NH_T_REJECT : GR_NH_T_BLACKHOLE;
gr_nh->af = GR_AF_UNSPEC;
gr_nh->iface_id = GR_IFACE_ID_UNDEF;
gr_log_debug("add nexthop id %u with type %s", nh_id, gr_nh_type_name(gr_nh));
break;
default:
gr_log_err("impossible to add nexthop %u (type %u not supported)", nh_id, nh->type);
return ZEBRA_DPLANE_REQUEST_FAILURE;
Expand Down
6 changes: 6 additions & 0 deletions modules/infra/api/gr_nexthop.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ typedef enum : uint8_t {
GR_NH_T_SR6_OUTPUT,
GR_NH_T_SR6_LOCAL,
GR_NH_T_DNAT,
GR_NH_T_BLACKHOLE,
GR_NH_T_REJECT,
} gr_nh_type_t;

// Route install origin values shared by IPv4 and IPv6.
Expand Down Expand Up @@ -149,6 +151,10 @@ static inline const char *gr_nh_type_name(const struct gr_nexthop *nh) {
return "SRv6-local";
case GR_NH_T_DNAT:
return "DNAT";
case GR_NH_T_BLACKHOLE:
return "blackhole";
case GR_NH_T_REJECT:
return "reject";
}
return "?";
}
Expand Down
84 changes: 56 additions & 28 deletions modules/infra/api/nexthop.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,50 +39,77 @@ static struct gr_api_handler config_set_handler = {
.callback = nh_config_set,
};

static struct api_out nh_add(const void *request, void ** /*response*/) {
const struct gr_nh_add_req *req = request;
const struct nexthop_af_ops *ops;
struct gr_nexthop base = req->nh;
struct nexthop *nh = NULL;
static int nh_add_blackhole(struct gr_nexthop *base) {
base->state = GR_NH_S_REACHABLE;
base->flags |= GR_NH_F_STATIC;
base->af = GR_AF_UNSPEC;
base->iface_id = GR_IFACE_ID_UNDEF;
return 0;
}

static int nh_add_l3(struct gr_nexthop *base) {
struct iface *iface;
int ret;

base.flags = 0;
switch (base.af) {
switch (base->af) {
case GR_AF_IP4:
if (base.ipv4 == 0)
return api_out(EDESTADDRREQ, 0);

if (base->ipv4 == 0)
return -EDESTADDRREQ;
break;
case GR_AF_IP6:
if (rte_ipv6_addr_is_unspec(&base.ipv6))
return api_out(EDESTADDRREQ, 0);
if (rte_ipv6_addr_is_unspec(&base->ipv6))
return -EDESTADDRREQ;

break;
case GR_AF_UNSPEC:
if (base.ipv4 || !rte_ipv6_addr_is_unspec(&base.ipv6))
return api_out(EINVAL, 0);
if (base->ipv4 || !rte_ipv6_addr_is_unspec(&base->ipv6))
return -EINVAL;

base.flags |= GR_NH_F_LINK | GR_NH_F_STATIC;
base->flags |= GR_NH_F_LINK | GR_NH_F_STATIC;
break;
default:
return api_out(ENOPROTOOPT, 0);
return -ENOPROTOOPT;
}

iface = iface_from_id(base.iface_id);
iface = iface_from_id(base->iface_id);
if (iface == NULL)
return api_out(errno, 0);
return -errno;

base->vrf_id = iface->vrf_id;
base->state = GR_NH_S_NEW;
if (!rte_is_zero_ether_addr(&base->mac)) {
if (base->af == GR_AF_UNSPEC)
return -EINVAL;

base->state = GR_NH_S_REACHABLE;
base->flags |= GR_NH_F_STATIC;
}
return 0;
}

base.vrf_id = iface->vrf_id;
base.type = GR_NH_T_L3;
base.state = GR_NH_S_NEW;
if (!rte_is_zero_ether_addr(&base.mac)) {
if (base.af == GR_AF_UNSPEC)
return api_out(EINVAL, 0);
static struct api_out nh_add(const void *request, void ** /*response*/) {
const struct gr_nh_add_req *req = request;
const struct nexthop_af_ops *ops;
struct gr_nexthop base = req->nh;
struct nexthop *nh = NULL;
int ret;

base.state = GR_NH_S_REACHABLE;
base.flags |= GR_NH_F_STATIC;
base.flags = 0;
switch (base.type) {
case GR_NH_T_BLACKHOLE:
case GR_NH_T_REJECT:
ret = nh_add_blackhole(&base);
break;
case GR_NH_T_L3:
case GR_NH_T_SR6_OUTPUT:
case GR_NH_T_SR6_LOCAL:
case GR_NH_T_DNAT:
ret = nh_add_l3(&base);
break;
default:
return api_out(EINVAL, 0);
}
if (ret < 0)
return api_out(-ret, 0);

if (base.nh_id != GR_NH_ID_UNSET)
nh = nexthop_lookup_by_id(base.nh_id);
Expand Down Expand Up @@ -151,7 +178,8 @@ static struct api_out nh_del(const void *request, void ** /*response*/) {
return api_out(ENOENT, 0);
}

if (nh->type != GR_NH_T_L3 || (nh->flags & addr_flags) == addr_flags || nh->ref_count > 1)
if ((nh->type != GR_NH_T_L3 && nh->type != GR_NH_T_BLACKHOLE && nh->type != GR_NH_T_REJECT)
|| (nh->flags & addr_flags) == addr_flags || nh->ref_count > 1)
return api_out(EBUSY, 0);

ops = nexthop_af_ops_get(nh->af);
Expand Down
24 changes: 20 additions & 4 deletions modules/infra/cli/nexthop.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,26 @@ static cmd_status_t show_config(const struct gr_api_client *c, const struct ec_p
}

static cmd_status_t nh_add(const struct gr_api_client *c, const struct ec_pnode *p) {
struct gr_nh_add_req req = {.exist_ok = true, .nh.origin = GR_NH_ORIGIN_USER};
struct gr_nh_add_req req = {
.exist_ok = true,
.nh.origin = GR_NH_ORIGIN_USER,
.nh.type = GR_NH_T_L3,
};
struct gr_iface iface;

if (arg_u32(p, "ID", &req.nh.nh_id) < 0 && errno != ENOENT)
return CMD_ERROR;

if (arg_str(p, "blackhole") != NULL) {
req.nh.type = GR_NH_T_BLACKHOLE;
goto send;
}

if (arg_str(p, "reject") != NULL) {
req.nh.type = GR_NH_T_REJECT;
goto send;
}

switch (arg_ip4(p, "IP", &req.nh.ipv4)) {
case 0:
req.nh.af = GR_AF_IP4;
Expand All @@ -85,7 +99,7 @@ static cmd_status_t nh_add(const struct gr_api_client *c, const struct ec_pnode

if (arg_eth_addr(p, "MAC", &req.nh.mac) < 0 && errno != ENOENT)
return CMD_ERROR;

send:
if (gr_api_client_send_recv(c, GR_NH_ADD, sizeof(req), &req, NULL) < 0)
return CMD_ERROR;

Expand Down Expand Up @@ -245,13 +259,15 @@ static int ctx_init(struct ec_node *root) {

ret = CLI_COMMAND(
CLI_CONTEXT(root, CTX_ADD),
"nexthop [id ID] [address IP] iface IFACE [mac MAC]",
"nexthop [id ID] ([address IP] iface IFACE [mac MAC])|blackhole|reject",
nh_add,
"Add a new next hop.",
with_help("IPv4/6 address.", ec_node_re("IP", IP_ANY_RE)),
with_help("Ethernet address.", ec_node_re("MAC", ETH_ADDR_RE)),
with_help("Nexthop ID.", ec_node_uint("ID", 1, UINT32_MAX - 1, 10)),
with_help("Output interface.", ec_node_dyn("IFACE", complete_iface_names, NULL))
with_help("Output interface.", ec_node_dyn("IFACE", complete_iface_names, NULL)),
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;
Expand Down
4 changes: 4 additions & 0 deletions modules/infra/control/nexthop.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ void nexthop_type_ops_register(gr_nh_type_t type, const struct nexthop_type_ops
case GR_NH_T_SR6_OUTPUT:
case GR_NH_T_SR6_LOCAL:
case GR_NH_T_DNAT:
case GR_NH_T_BLACKHOLE:
case GR_NH_T_REJECT:
if (ops == NULL || (ops->free == NULL && ops->equal == NULL))
ABORT("invalid type ops");
if (type_ops[type] != NULL)
Expand All @@ -336,6 +338,8 @@ struct nexthop *nexthop_new(const struct gr_nexthop *base) {
case GR_NH_T_SR6_OUTPUT:
case GR_NH_T_SR6_LOCAL:
case GR_NH_T_DNAT:
case GR_NH_T_BLACKHOLE:
case GR_NH_T_REJECT:
break;
default:
ABORT("invalid nexthop type %hhu", base->type);
Expand Down
33 changes: 19 additions & 14 deletions modules/ip/cli/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,25 @@ static cmd_status_t route4_list(const struct gr_api_client *c, const struct ec_p
struct gr_iface iface;
scols_line_sprintf(line, 0, "%u", route->vrf_id);
scols_line_sprintf(line, 1, IP4_F "/%hhu", &route->dest.ip, route->dest.prefixlen);
switch (route->nh.af) {
case GR_AF_UNSPEC:
if (iface_from_id(c, route->nh.iface_id, &iface) < 0)
scols_line_sprintf(line, 2, "%u", route->nh.iface_id);
else
scols_line_sprintf(line, 2, "%s", iface.name);
break;
case GR_AF_IP4:
scols_line_sprintf(line, 2, IP4_F, &route->nh.ipv4);
break;
case GR_AF_IP6:
scols_line_sprintf(line, 2, IP6_F, &route->nh.ipv6);
break;
}
if (route->nh.type == GR_NH_T_BLACKHOLE)
scols_line_sprintf(line, 2, "blackhole");
else if (route->nh.type == GR_NH_T_REJECT)
scols_line_sprintf(line, 2, "reject");
else
switch (route->nh.af) {
case GR_AF_UNSPEC:
if (iface_from_id(c, route->nh.iface_id, &iface) < 0)
scols_line_sprintf(line, 2, "%u", route->nh.iface_id);
else
scols_line_sprintf(line, 2, "%s", iface.name);
break;
case GR_AF_IP4:
scols_line_sprintf(line, 2, IP4_F, &route->nh.ipv4);
break;
case GR_AF_IP6:
scols_line_sprintf(line, 2, IP6_F, &route->nh.ipv6);
break;
}
scols_line_sprintf(line, 3, "%s", gr_nh_origin_name(route->origin));
if (route->nh.nh_id != GR_NH_ID_UNSET)
scols_line_sprintf(line, 4, "%u", route->nh.nh_id);
Expand Down
4 changes: 3 additions & 1 deletion modules/ip/control/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,9 @@ static struct api_out route4_del(const void *request, void ** /*response*/) {
int ret;

nh = rib4_lookup(req->vrf_id, req->dest.ip);
ret = rib4_delete(req->vrf_id, req->dest.ip, req->dest.prefixlen, GR_NH_T_L3);
ret = rib4_delete(
req->vrf_id, req->dest.ip, req->dest.prefixlen, nh ? nh->type : GR_NH_T_L3
);
if (ret == -ENOENT && req->missing_ok)
ret = 0;

Expand Down
14 changes: 14 additions & 0 deletions modules/ip/datapath/arp_output_request.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

enum {
OUTPUT = 0,
BLACKHOLE,
REJECT,
ERROR,
EDGE_COUNT,
};
Expand Down Expand Up @@ -61,6 +63,16 @@ static uint16_t arp_output_request_process(
for (unsigned i = 0; i < n_objs; i++) {
mbuf = objs[i];
nh = control_input_mbuf_data(mbuf)->data;

if (nh->type == GR_NH_T_BLACKHOLE) {
edge = BLACKHOLE;
goto next;
}
if (nh->type == GR_NH_T_REJECT) {
edge = REJECT;
goto next;
}

local = addr4_get_preferred(nh->iface_id, nh->ipv4);

if (local == NULL) {
Expand Down Expand Up @@ -122,6 +134,8 @@ static struct rte_node_register arp_output_request_node = {
.nb_edges = EDGE_COUNT,
.next_nodes = {
[OUTPUT] = "eth_output",
[BLACKHOLE] = "ip_blackhole",
[REJECT] = "ip_admin_prohibited",
[ERROR] = "arp_output_error",
},
};
Expand Down
15 changes: 15 additions & 0 deletions modules/ip/datapath/icmp_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

enum {
OUTPUT = 0,
BLACKHOLE,
REJECT,
NO_HEADROOM,
NO_ROUTE,
EDGE_COUNT,
Expand Down Expand Up @@ -51,11 +53,21 @@ icmp_output_process(struct rte_graph *graph, struct rte_node *node, void **objs,
edge = NO_ROUTE;
goto next;
}
if (nh->type == GR_NH_T_BLACKHOLE) {
edge = BLACKHOLE;
goto next;
} else if (nh->type == GR_NH_T_REJECT) {
edge = REJECT;
goto next;
}
o = ip_output_mbuf_data(mbuf);
o->nh = nh;
o->iface = NULL;
edge = OUTPUT;
next:
if (gr_mbuf_is_traced(mbuf))
gr_mbuf_trace_add(mbuf, node, 0);

rte_node_enqueue_x1(graph, node, edge, mbuf);
}

Expand All @@ -70,6 +82,8 @@ static struct rte_node_register icmp_output_node = {
.nb_edges = EDGE_COUNT,
.next_nodes = {
[OUTPUT] = "ip_output",
[BLACKHOLE] = "ip_blackhole",
[REJECT] = "ip_admin_prohibited",
[NO_HEADROOM] = "error_no_headroom",
[NO_ROUTE] = "icmp_output_no_route",
},
Expand All @@ -82,3 +96,4 @@ static struct gr_node_info icmp_output_info = {
GR_NODE_REGISTER(icmp_output_info);

GR_DROP_REGISTER(icmp_output_no_route);
GR_DROP_REGISTER(ip_admin_prohibited);
Loading