Skip to content

Commit 0fc1cd2

Browse files
committed
modules: add static ipv6 forwarding support
This is a first draft of IPv6 forwarding support. It only supports static next hops. This patch addresses part of the following RFC: RFC 8200: Internet Protocol, Version 6 (IPv6) Specification https://www.rfc-editor.org/rfc/rfc8200 Closes: https://github.com/rjarry/grout/issues/8 Signed-off-by: Robin Jarry <rjarry@redhat.com>
1 parent cf84f67 commit 0fc1cd2

19 files changed

Lines changed: 1946 additions & 0 deletions

modules/ip6/api/gr_ip6.h

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2024 Robin Jarry
3+
4+
#ifndef _GR_IP6_MSG
5+
#define _GR_IP6_MSG
6+
7+
#include <gr_api.h>
8+
#include <gr_bitops.h>
9+
#include <gr_net_types.h>
10+
11+
#include <rte_ether.h>
12+
#include <rte_ip6.h>
13+
14+
#include <stdint.h>
15+
16+
struct gr_ip6_ifaddr {
17+
uint16_t iface_id;
18+
struct ip6_net addr;
19+
};
20+
21+
#define GR_IP6_NH_F_PENDING GR_BIT16(0) // NDP probe sent
22+
#define GR_IP6_NH_F_REACHABLE GR_BIT16(1) // NDP reply received
23+
#define GR_IP6_NH_F_STALE GR_BIT16(2) // Reachable lifetime expired, need NDP refresh
24+
#define GR_IP6_NH_F_FAILED GR_BIT16(3) // All NDP probes sent without reply
25+
#define GR_IP6_NH_F_STATIC GR_BIT16(4) // Configured by user
26+
#define GR_IP6_NH_F_LOCAL GR_BIT16(5) // Local address
27+
#define GR_IP6_NH_F_GATEWAY GR_BIT16(6) // Gateway route
28+
#define GR_IP6_NH_F_LINK GR_BIT16(7) // Connected link route
29+
#define GR_IP6_NH_F_MCAST GR_BIT16(8) // Multicast address
30+
typedef uint16_t gr_ip6_nh_flags_t;
31+
32+
static inline const char *gr_ip6_nh_f_name(const gr_ip6_nh_flags_t flag) {
33+
switch (flag) {
34+
case GR_IP6_NH_F_PENDING:
35+
return "pending";
36+
case GR_IP6_NH_F_REACHABLE:
37+
return "reachable";
38+
case GR_IP6_NH_F_STALE:
39+
return "stale";
40+
case GR_IP6_NH_F_FAILED:
41+
return "failed";
42+
case GR_IP6_NH_F_STATIC:
43+
return "static";
44+
case GR_IP6_NH_F_LOCAL:
45+
return "local";
46+
case GR_IP6_NH_F_GATEWAY:
47+
return "gateway";
48+
case GR_IP6_NH_F_LINK:
49+
return "link";
50+
case GR_IP6_NH_F_MCAST:
51+
return "multicast";
52+
}
53+
return "";
54+
}
55+
56+
#define GR_VRF_ID_ALL UINT16_MAX
57+
58+
struct gr_ip6_nh {
59+
struct rte_ipv6_addr host;
60+
struct rte_ether_addr mac;
61+
uint16_t vrf_id;
62+
uint16_t iface_id;
63+
gr_ip6_nh_flags_t flags;
64+
};
65+
66+
struct gr_ip6_route {
67+
struct ip6_net dest;
68+
struct rte_ipv6_addr nh;
69+
};
70+
71+
#define GR_IP6_MODULE 0xfeed
72+
73+
// next hops ///////////////////////////////////////////////////////////////////
74+
75+
#define GR_IP6_NH_ADD REQUEST_TYPE(GR_IP6_MODULE, 0x0001)
76+
77+
struct gr_ip6_nh_add_req {
78+
struct gr_ip6_nh nh;
79+
uint8_t exist_ok;
80+
};
81+
82+
// struct gr_ip6_nh_add_resp { };
83+
84+
#define GR_IP6_NH_DEL REQUEST_TYPE(GR_IP6_MODULE, 0x0002)
85+
86+
struct gr_ip6_nh_del_req {
87+
uint16_t vrf_id;
88+
struct rte_ipv6_addr host;
89+
uint8_t missing_ok;
90+
};
91+
92+
// struct gr_ip6_nh_del_resp { };
93+
94+
#define GR_IP6_NH_LIST REQUEST_TYPE(GR_IP6_MODULE, 0x0003)
95+
96+
struct gr_ip6_nh_list_req {
97+
uint16_t vrf_id;
98+
};
99+
100+
struct gr_ip6_nh_list_resp {
101+
uint16_t n_nhs;
102+
struct gr_ip6_nh nhs[/* n_nhs */];
103+
};
104+
105+
// routes //////////////////////////////////////////////////////////////////////
106+
107+
#define GR_IP6_ROUTE_ADD REQUEST_TYPE(GR_IP6_MODULE, 0x0010)
108+
109+
struct gr_ip6_route_add_req {
110+
uint16_t vrf_id;
111+
struct ip6_net dest;
112+
struct rte_ipv6_addr nh;
113+
uint8_t exist_ok;
114+
};
115+
116+
// struct gr_ip6_route_add_resp { };
117+
118+
#define GR_IP6_ROUTE_DEL REQUEST_TYPE(GR_IP6_MODULE, 0x0011)
119+
120+
struct gr_ip6_route_del_req {
121+
uint16_t vrf_id;
122+
struct ip6_net dest;
123+
uint8_t missing_ok;
124+
};
125+
126+
// struct gr_ip6_route_del_resp { };
127+
128+
#define GR_IP6_ROUTE_GET REQUEST_TYPE(GR_IP6_MODULE, 0x0012)
129+
130+
struct gr_ip6_route_get_req {
131+
uint16_t vrf_id;
132+
struct rte_ipv6_addr dest;
133+
};
134+
135+
struct gr_ip6_route_get_resp {
136+
struct gr_ip6_nh nh;
137+
};
138+
139+
#define GR_IP6_ROUTE_LIST REQUEST_TYPE(GR_IP6_MODULE, 0x0013)
140+
141+
struct gr_ip6_route_list_req {
142+
uint16_t vrf_id;
143+
};
144+
145+
struct gr_ip6_route_list_resp {
146+
uint16_t n_routes;
147+
struct gr_ip6_route routes[/* n_routes */];
148+
};
149+
150+
// addresses ///////////////////////////////////////////////////////////////////
151+
152+
#define GR_IP6_ADDR_ADD REQUEST_TYPE(GR_IP6_MODULE, 0x0021)
153+
154+
struct gr_ip6_addr_add_req {
155+
struct gr_ip6_ifaddr addr;
156+
uint8_t exist_ok;
157+
};
158+
159+
// struct gr_ip6_addr_add_resp { };
160+
161+
#define GR_IP6_ADDR_DEL REQUEST_TYPE(GR_IP6_MODULE, 0x0022)
162+
163+
struct gr_ip6_addr_del_req {
164+
struct gr_ip6_ifaddr addr;
165+
uint8_t missing_ok;
166+
};
167+
168+
// struct gr_ip6_addr_del_resp { };
169+
170+
#define GR_IP6_ADDR_LIST REQUEST_TYPE(GR_IP6_MODULE, 0x0023)
171+
172+
struct gr_ip6_addr_list_req {
173+
uint16_t vrf_id;
174+
};
175+
176+
struct gr_ip6_addr_list_resp {
177+
uint16_t n_addrs;
178+
struct gr_ip6_ifaddr addrs[/* n_addrs */];
179+
};
180+
181+
#endif

modules/ip6/api/meson.build

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
# Copyright (c) 2024 Robin Jarry
3+
4+
api_headers += files('gr_ip6.h')
5+
inc += include_directories('.')
6+
cli_inc += include_directories('.')

modules/ip6/cli/address.c

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2024 Robin Jarry
3+
4+
#include "ip.h"
5+
6+
#include <gr_api.h>
7+
#include <gr_cli.h>
8+
#include <gr_cli_iface.h>
9+
#include <gr_ip6.h>
10+
#include <gr_net_types.h>
11+
#include <gr_table.h>
12+
13+
#include <ecoli.h>
14+
#include <libsmartcols.h>
15+
16+
#include <stdint.h>
17+
18+
static cmd_status_t addr_add(const struct gr_api_client *c, const struct ec_pnode *p) {
19+
struct gr_ip6_addr_add_req req = {.exist_ok = true};
20+
struct gr_iface iface;
21+
22+
if (ip6_net_parse(arg_str(p, "IP6_NET"), &req.addr.addr, false) < 0)
23+
return CMD_ERROR;
24+
if (iface_from_name(c, arg_str(p, "IFACE"), &iface) < 0)
25+
return CMD_ERROR;
26+
req.addr.iface_id = iface.id;
27+
28+
if (gr_api_client_send_recv(c, GR_IP6_ADDR_ADD, sizeof(req), &req, NULL) < 0)
29+
return CMD_ERROR;
30+
31+
return CMD_SUCCESS;
32+
}
33+
34+
static cmd_status_t addr_del(const struct gr_api_client *c, const struct ec_pnode *p) {
35+
struct gr_ip6_addr_del_req req = {.missing_ok = true};
36+
struct gr_iface iface;
37+
38+
if (ip6_net_parse(arg_str(p, "IP6_NET"), &req.addr.addr, false) < 0)
39+
return CMD_ERROR;
40+
if (iface_from_name(c, arg_str(p, "IFACE"), &iface) < 0)
41+
return CMD_ERROR;
42+
req.addr.iface_id = iface.id;
43+
44+
if (gr_api_client_send_recv(c, GR_IP6_ADDR_DEL, sizeof(req), &req, NULL) < 0)
45+
return CMD_ERROR;
46+
47+
return CMD_SUCCESS;
48+
}
49+
50+
static cmd_status_t addr_list(const struct gr_api_client *c, const struct ec_pnode *p) {
51+
struct libscols_table *table = scols_new_table();
52+
const struct gr_ip6_addr_list_resp *resp;
53+
struct gr_ip6_addr_list_req req = {0};
54+
struct gr_iface iface;
55+
56+
void *resp_ptr = NULL;
57+
char buf[BUFSIZ];
58+
59+
(void)p;
60+
61+
if (table == NULL)
62+
return CMD_ERROR;
63+
64+
if (arg_u16(p, "VRF", &req.vrf_id) < 0 && errno != ENOENT) {
65+
scols_unref_table(table);
66+
return CMD_ERROR;
67+
}
68+
69+
if (gr_api_client_send_recv(c, GR_IP6_ADDR_LIST, sizeof(req), &req, &resp_ptr) < 0) {
70+
scols_unref_table(table);
71+
return CMD_ERROR;
72+
}
73+
74+
resp = resp_ptr;
75+
76+
scols_table_new_column(table, "IFACE", 0, 0);
77+
scols_table_new_column(table, "ADDRESS", 0, 0);
78+
scols_table_set_column_separator(table, " ");
79+
80+
for (size_t i = 0; i < resp->n_addrs; i++) {
81+
struct libscols_line *line = scols_table_new_line(table, NULL);
82+
const struct gr_ip6_ifaddr *addr = &resp->addrs[i];
83+
ip6_net_format(&addr->addr, buf, sizeof(buf));
84+
if (iface_from_id(c, addr->iface_id, &iface) == 0)
85+
scols_line_sprintf(line, 0, "%s", iface.name);
86+
else
87+
scols_line_sprintf(line, 0, "%u", addr->iface_id);
88+
scols_line_sprintf(line, 1, "%s", buf);
89+
}
90+
91+
scols_print_table(table);
92+
scols_unref_table(table);
93+
free(resp_ptr);
94+
95+
return CMD_SUCCESS;
96+
}
97+
98+
static int ctx_init(struct ec_node *root) {
99+
int ret;
100+
101+
ret = CLI_COMMAND(
102+
IP6_ADD_CTX(root),
103+
"address IP6_NET iface IFACE",
104+
addr_add,
105+
"Add an IPv6 address to an interface.",
106+
with_help("IPv6 address with prefix length.", ec_node_re("IP6_NET", IPV6_NET_RE)),
107+
with_help("Interface name.", ec_node_dyn("IFACE", complete_iface_names, NULL))
108+
);
109+
if (ret < 0)
110+
return ret;
111+
ret = CLI_COMMAND(
112+
IP6_DEL_CTX(root),
113+
"address IP6_NET iface IFACE",
114+
addr_del,
115+
"Remove an IPv6 address from an interface.",
116+
with_help("IPv6 address with prefix length.", ec_node_re("IP6_NET", IPV6_NET_RE)),
117+
with_help("Interface name.", ec_node_dyn("IFACE", complete_iface_names, NULL))
118+
);
119+
if (ret < 0)
120+
return ret;
121+
ret = CLI_COMMAND(
122+
IP6_SHOW_CTX(root),
123+
"address [vrf VRF]",
124+
addr_list,
125+
"Display all IPv6 addresses.",
126+
with_help("L3 addressing domain ID.", ec_node_uint("VRF", 0, UINT16_MAX - 1, 10))
127+
);
128+
if (ret < 0)
129+
return ret;
130+
131+
return 0;
132+
}
133+
134+
static struct gr_cli_context ctx = {
135+
.name = "ipv6 address",
136+
.init = ctx_init,
137+
};
138+
139+
static void __attribute__((constructor, used)) init(void) {
140+
register_context(&ctx);
141+
}

modules/ip6/cli/ip.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2024 Robin Jarry
3+
4+
#ifndef _GR_CLI_IP
5+
#define _GR_CLI_IP
6+
7+
#include <gr_cli.h>
8+
9+
#define IP6_ADD_CTX(root) CLI_CONTEXT(root, CTX_ADD, CTX_ARG("ip6", "Create IPv6 stack elements."))
10+
#define IP6_DEL_CTX(root) CLI_CONTEXT(root, CTX_DEL, CTX_ARG("ip6", "Delete IPv6 stack elements."))
11+
#define IP6_SHOW_CTX(root) CLI_CONTEXT(root, CTX_SHOW, CTX_ARG("ip6", "Show IPv6 stack details."))
12+
13+
#endif

modules/ip6/cli/meson.build

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
# Copyright (c) 2024 Robin Jarry
3+
4+
cli_src += files(
5+
'address.c',
6+
'nexthop.c',
7+
'route.c',
8+
)

0 commit comments

Comments
 (0)