Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Router advertisement & Router sollicitation #126

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
554 changes: 271 additions & 283 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.
6 changes: 6 additions & 0 deletions modules/infra/control/loopback.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ static void iface_loopback_poll(evutil_socket_t, short reason, void *ev_iface) {

// packet sent from linux tun iface, no need to compute checksum;
mbuf->ol_flags = RTE_MBUF_F_RX_IP_CKSUM_GOOD;

// We can't call rte_net_get_ptype directly as we do not have an ethernet frame.
// An option would be to prepend/adjust every buffer, but let's set directly
// the information we need instead.
mbuf->packet_type = data[0] == 6 ? RTE_PTYPE_L3_IPV6 : RTE_PTYPE_L3_IPV4;

// Emulate ethernet input, required by ip(6)_input
e = eth_input_mbuf_data(mbuf);
e->iface = iface;
Expand Down
37 changes: 37 additions & 0 deletions modules/ip6/api/gr_ip6.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,41 @@ struct gr_ip6_addr_list_resp {
struct gr_ip6_ifaddr addrs[/* n_addrs */];
};

// router advertisement ////////////////////////////////////////////////////////
#define GR_IP6_IFACE_RA_SET REQUEST_TYPE(GR_IP6_MODULE, 0x0030)
struct gr_ip6_ra_set_req {
uint16_t iface_id;
uint16_t set_interval : 1;
uint16_t set_lifetime : 1;
uint16_t set_preference : 1;

uint16_t interval;
uint16_t lifetime;
uint16_t preference;
};
// struct gr_ip6_ra_set_resp { };

#define GR_IP6_IFACE_RA_CLEAR REQUEST_TYPE(GR_IP6_MODULE, 0x0031)
struct gr_ip6_ra_clear_req {
uint16_t iface_id;
};
// struct gr_ip6_ra_clear_resp { };

#define GR_IP6_IFACE_RA_SHOW REQUEST_TYPE(GR_IP6_MODULE, 0x0032)
struct gr_ip6_ra_show_req {
uint16_t iface_id;
};

struct gr_ip6_ra_conf {
bool enabled;
uint16_t iface_id;
uint16_t interval;
uint16_t lifetime;
uint16_t preference;
};

struct gr_ip6_ra_show_resp {
uint16_t n_ras;
struct gr_ip6_ra_conf ras[];
};
#endif
3 changes: 3 additions & 0 deletions modules/ip6/cli/ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@
#define IP6_ADD_CTX(root) CLI_CONTEXT(root, CTX_ADD, CTX_ARG("ip6", "Create IPv6 stack elements."))
#define IP6_DEL_CTX(root) CLI_CONTEXT(root, CTX_DEL, CTX_ARG("ip6", "Delete IPv6 stack elements."))
#define IP6_SHOW_CTX(root) CLI_CONTEXT(root, CTX_SHOW, CTX_ARG("ip6", "Show IPv6 stack details."))
#define IP6_SET_CTX(root) CLI_CONTEXT(root, CTX_SET, CTX_ARG("ip6", "Set IPv6 stack elements."))
#define IP6_CLEAR_CTX(root) \
CLI_CONTEXT(root, CTX_CLEAR, CTX_ARG("ip6", "Clear IPv6 stack elements."))

#endif
1 change: 1 addition & 0 deletions modules/ip6/cli/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ cli_src += files(
'address.c',
'nexthop.c',
'route.c',
'router_advertisement.c',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe s/router_advertisement.c/ra.c/ or at least router_advert.c?

)
140 changes: 140 additions & 0 deletions modules/ip6/cli/router_advertisement.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Christophe Fontaine

#include "ip.h"

#include <gr_api.h>
#include <gr_cli.h>
#include <gr_cli_iface.h>
#include <gr_ip6.h>
#include <gr_net_types.h>
#include <gr_table.h>

#include <ecoli.h>
#include <libsmartcols.h>

#include <errno.h>

static cmd_status_t ra_show(const struct gr_api_client *c, const struct ec_pnode *p) {
struct libscols_table *table = scols_new_table();
struct gr_ip6_ra_show_resp *resp;
struct gr_ip6_ra_show_req req;
struct gr_iface iface;
void *resp_ptr = NULL;

if (!iface_from_name(c, arg_str(p, "IFACE"), &iface))
req.iface_id = iface.id;
else
req.iface_id = 0;

if (gr_api_client_send_recv(c, GR_IP6_IFACE_RA_SHOW, sizeof(req), &req, &resp_ptr) < 0)
return CMD_ERROR;
resp = resp_ptr;

scols_table_new_column(table, "IFACE", 0, 0);
scols_table_new_column(table, "RA", 0, 0);
scols_table_new_column(table, "interval", 0, 0);
scols_table_new_column(table, "lifetime", 0, 0);
scols_table_new_column(table, "preference", 0, 0);
scols_table_set_column_separator(table, " ");

for (uint16_t i = 0; i < resp->n_ras; i++) {
struct libscols_line *line = scols_table_new_line(table, NULL);
if (iface_from_id(c, resp->ras[i].iface_id, &iface) == 0)
scols_line_sprintf(line, 0, "%s", iface.name);
else
scols_line_sprintf(line, 0, "%u", resp->ras[i].iface_id);
scols_line_sprintf(line, 1, "%u", resp->ras[i].enabled);
scols_line_sprintf(line, 2, "%u", resp->ras[i].interval);
scols_line_sprintf(line, 3, "%u", resp->ras[i].lifetime);
scols_line_sprintf(line, 4, "%u", resp->ras[i].preference);
}

scols_print_table(table);
scols_unref_table(table);
free(resp_ptr);
return CMD_SUCCESS;
}

static cmd_status_t ra_set(const struct gr_api_client *c, const struct ec_pnode *p) {
struct gr_ip6_ra_set_req req = {0};
struct gr_iface iface;

if (iface_from_name(c, arg_str(p, "IFACE"), &iface) < 0)
return CMD_ERROR;

req.iface_id = iface.id;
if (!arg_u16(p, "IT", &req.interval))
req.set_interval = 1;

if (!arg_u16(p, "LT", &req.lifetime))
req.set_lifetime = 1;

if (!arg_u16(p, "PREF", &req.preference))
req.set_preference = 1;

if (gr_api_client_send_recv(c, GR_IP6_IFACE_RA_SET, sizeof(req), &req, NULL) < 0)
return CMD_ERROR;
return CMD_SUCCESS;
}

static cmd_status_t ra_clear(const struct gr_api_client *c, const struct ec_pnode *p) {
struct gr_ip6_ra_clear_req req;
struct gr_iface iface;

if (iface_from_name(c, arg_str(p, "IFACE"), &iface) < 0)
return CMD_ERROR;

req.iface_id = iface.id;
if (gr_api_client_send_recv(c, GR_IP6_IFACE_RA_CLEAR, sizeof(req), &req, NULL) < 0)
return CMD_ERROR;
return CMD_SUCCESS;
}

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

ret = CLI_COMMAND(
IP6_SHOW_CTX(root),
"router-advertisment [IFACE]",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/advertisment/advertisement/

But I think I prefer router-advert. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even shorter ra ? Do you think it may be possible to have "short commands" in the CLI ?
So we could use both ra and router-advertisement ? (or to even accept partial commands ?)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you can have token alternatives separated by pipes (without spaces)

"ra|router-advert|... [IFACE]"

No partial marching though. You need to explicitly list all alternatives to allow libecoli to build a token graph.

ra_show,
"Show router advertisement configuration",
with_help("Interface name.", ec_node_dyn("IFACE", complete_iface_names, NULL))
);
if (ret < 0)
return ret;

ret = CLI_COMMAND(
IP6_SET_CTX(root),
"router-advertisment IFACE [interval IT] [lifetime LT] [preference PREF]",
ra_set,
"Set router advertisement parameters",
with_help("Interface name.", ec_node_dyn("IFACE", complete_iface_names, NULL)),
with_help("Interval", ec_node_uint("IT", 0, UINT16_MAX - 1, 10)),
with_help("Life time", ec_node_uint("LT", 0, UINT16_MAX - 1, 10)),
with_help("preference", ec_node_uint("PREF", 0, UINT16_MAX - 1, 10))
);
if (ret < 0)
return ret;

ret = CLI_COMMAND(
IP6_CLEAR_CTX(root),
"router-advertisement IFACE",
ra_clear,
"Disable router advertisement and reset parameters",
with_help("Interface name.", ec_node_dyn("IFACE", complete_iface_names, NULL))
);
if (ret < 0)
return ret;

return 0;
}

static struct gr_cli_context ctx = {
.name = "ipv6 router-advertisement",
.init = ctx_init,
};

static void __attribute__((constructor, used)) init(void) {
register_context(&ctx);
}
1 change: 1 addition & 0 deletions modules/ip6/control/gr_ip6_control.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct nexthop *ip6_nexthop_new(uint16_t vrf_id, uint16_t iface_id, const struct

void ip6_nexthop_unreachable_cb(struct rte_mbuf *m);
void ndp_probe_input_cb(struct rte_mbuf *m);
void ndp_router_sollicit_input_cb(struct rte_mbuf *m);

int ip6_route_insert(uint16_t vrf_id, uint16_t iface_id, const struct rte_ipv6_addr *, uint8_t prefixlen, struct nexthop *);
int ip6_route_delete(
Expand Down
1 change: 1 addition & 0 deletions modules/ip6/control/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ src += files(
'address.c',
'nexthop.c',
'route.c',
'router_advertisement.c',
)
inc += include_directories('.')
Loading