Skip to content

Commit ff1eac8

Browse files
committed
[nh-encap] Allow adding a nh_encap to a rtnl_nh
A regular "nexthop object" à la `ip nexthop` can have an associated encapsulation. So, add the code to support that. Signed-off-by: Christoph Paasch <[email protected]>
1 parent d235012 commit ff1eac8

File tree

4 files changed

+223
-0
lines changed

4 files changed

+223
-0
lines changed

include/netlink/route/nh.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ extern int rtnl_nh_set_res_group_unbalanced_timer(struct rtnl_nh *,
7272
extern int rtnl_nh_get_res_group_unbalanced_timer(struct rtnl_nh *,
7373
uint32_t *out_value);
7474

75+
/* lwtunnel encapsulation */
76+
struct rtnl_nh_encap;
77+
extern int rtnl_nh_set_encap(struct rtnl_nh *, struct rtnl_nh_encap *);
78+
extern struct rtnl_nh_encap *rtnl_nh_get_encap(struct rtnl_nh *);
79+
7580
#ifdef __cplusplus
7681
}
7782
#endif

lib/route/nh.c

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include "nl-default.h"
77

88
#include <linux/nexthop.h>
9+
#include <linux/lwtunnel.h>
10+
#include <linux/mpls_iptunnel.h>
911

1012
#include <netlink/route/nh.h>
1113
#include <netlink/hashtable.h>
@@ -29,6 +31,7 @@ struct rtnl_nh {
2931
nl_nh_group_t *nh_group;
3032
uint32_t nh_oif;
3133
struct nl_addr *nh_gateway;
34+
struct rtnl_nh_encap *nh_encap;
3235

3336
/* Resilient nexthop group parameters */
3437
uint16_t res_grp_buckets;
@@ -50,6 +53,7 @@ struct rtnl_nh {
5053
#define NH_ATTR_RES_BUCKETS (1 << 10)
5154
#define NH_ATTR_RES_IDLE_TIMER (1 << 11)
5255
#define NH_ATTR_RES_UNBALANCED_TIMER (1 << 12)
56+
#define NH_ATTR_ENCAP (1 << 13)
5357
/** @endcond */
5458

5559
struct nla_policy rtnl_nh_policy[NHA_MAX + 1] = {
@@ -60,6 +64,8 @@ struct nla_policy rtnl_nh_policy[NHA_MAX + 1] = {
6064
[NHA_BLACKHOLE] = { .type = NLA_UNSPEC },
6165
[NHA_OIF] = { .type = NLA_U32 },
6266
[NHA_RES_GROUP] = { .type = NLA_NESTED },
67+
[NHA_ENCAP] = { .type = NLA_NESTED },
68+
[NHA_ENCAP_TYPE] = { .type = NLA_U16 },
6369
};
6470

6571
static struct nl_cache_ops rtnl_nh_ops;
@@ -155,6 +161,13 @@ static int nh_clone(struct nl_object *_src, struct nl_object *_dst)
155161
dst->res_grp_unbalanced_timer = src->res_grp_unbalanced_timer;
156162
dst->ce_mask = src->ce_mask;
157163

164+
if (src->nh_encap) {
165+
dst->nh_encap = rtnl_nh_encap_clone(src->nh_encap);
166+
if (!dst->nh_encap)
167+
return -NLE_NOMEM;
168+
dst->ce_mask |= NH_ATTR_ENCAP;
169+
}
170+
158171
if (src->nh_gateway) {
159172
dst->nh_gateway = nl_addr_clone(src->nh_gateway);
160173
if (!dst->nh_gateway) {
@@ -176,6 +189,13 @@ static void nh_free(struct nl_object *obj)
176189
struct rtnl_nh *nh = nl_object_priv(obj);
177190
nl_addr_put(nh->nh_gateway);
178191

192+
if (nh->nh_encap) {
193+
if (nh->nh_encap->ops && nh->nh_encap->ops->destructor)
194+
nh->nh_encap->ops->destructor(nh->nh_encap->priv);
195+
free(nh->nh_encap->priv);
196+
free(nh->nh_encap);
197+
}
198+
179199
if (nh->nh_group)
180200
rtnl_nh_grp_put(nh->nh_group);
181201
}
@@ -237,6 +257,40 @@ struct nl_addr *rtnl_nh_get_gateway(struct rtnl_nh *nexthop)
237257
return nexthop->nh_gateway;
238258
}
239259

260+
int rtnl_nh_set_encap(struct rtnl_nh *nh, struct rtnl_nh_encap *encap)
261+
{
262+
if (!nh)
263+
return -NLE_INVAL;
264+
265+
if (encap && !encap->ops)
266+
return -NLE_INVAL;
267+
268+
if (nh->nh_encap) {
269+
if (nh->nh_encap->ops && nh->nh_encap->ops->destructor)
270+
nh->nh_encap->ops->destructor(nh->nh_encap->priv);
271+
free(nh->nh_encap->priv);
272+
free(nh->nh_encap);
273+
}
274+
275+
if (encap) {
276+
nh->nh_encap = encap;
277+
nh->ce_mask |= NH_ATTR_ENCAP;
278+
} else {
279+
nh->nh_encap = NULL;
280+
nh->ce_mask &= ~NH_ATTR_ENCAP;
281+
}
282+
283+
return 0;
284+
}
285+
286+
struct rtnl_nh_encap *rtnl_nh_get_encap(struct rtnl_nh *nh)
287+
{
288+
if (!nh || !(nh->ce_mask & NH_ATTR_ENCAP))
289+
return NULL;
290+
291+
return nh->nh_encap;
292+
}
293+
240294
int rtnl_nh_set_fdb(struct rtnl_nh *nexthop, int value)
241295
{
242296
if (value)
@@ -547,6 +601,27 @@ static int rtnl_nh_build_msg(struct nl_msg *msg, struct rtnl_nh *nh)
547601
if (nh->ce_mask & NH_ATTR_FLAG_BLACKHOLE)
548602
NLA_PUT_FLAG(msg, NHA_BLACKHOLE);
549603

604+
if (nh->ce_mask & NH_ATTR_ENCAP) {
605+
struct nlattr *encap;
606+
607+
if (!nh->nh_encap || !nh->nh_encap->ops) {
608+
return -NLE_INVAL;
609+
}
610+
NLA_PUT_U16(msg, NHA_ENCAP_TYPE, nh->nh_encap->ops->encap_type);
611+
612+
encap = nla_nest_start(msg, NHA_ENCAP);
613+
if (!encap)
614+
goto nla_put_failure;
615+
616+
if (nh->nh_encap->ops->build_msg) {
617+
int err = nh->nh_encap->ops->build_msg(
618+
msg, nh->nh_encap->priv);
619+
if (err < 0)
620+
return err;
621+
}
622+
nla_nest_end(msg, encap);
623+
}
624+
550625
/* Nexthop group */
551626
if (nh->ce_mask & NH_ATTR_GROUP) {
552627
struct nexthop_grp *grp;
@@ -759,6 +834,26 @@ static int nexthop_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
759834
nexthop->ce_mask |= NH_ATTR_GROUP_TYPE;
760835
}
761836

837+
if (tb[NHA_ENCAP] && tb[NHA_ENCAP_TYPE]) {
838+
int err2;
839+
struct rtnl_nh_encap *nh_encap = NULL;
840+
841+
err2 = nh_encap_parse_msg(tb[NHA_ENCAP], tb[NHA_ENCAP_TYPE],
842+
&nh_encap);
843+
if (err2 == 0) {
844+
err2 = rtnl_nh_set_encap(nexthop, nh_encap);
845+
if (err2 < 0) {
846+
rtnl_nh_encap_free(nh_encap);
847+
return err2;
848+
}
849+
nexthop->ce_mask |= NH_ATTR_ENCAP;
850+
} else if (err2 != -NLE_MSGTYPE_NOSUPPORT) {
851+
return err2;
852+
}
853+
/* If NOSUPPORT, silently ignore to keep parity with previous
854+
* behavior where only MPLS was supported. */
855+
}
856+
762857
if (tb[NHA_BLACKHOLE]) {
763858
nexthop->ce_mask |= NH_ATTR_FLAG_BLACKHOLE;
764859
}
@@ -871,6 +966,9 @@ static void nh_dump_line(struct nl_object *obj, struct nl_dump_params *dp)
871966
nl_dump(dp, " via %s",
872967
nl_addr2str(nh->nh_gateway, buf, sizeof(buf)));
873968

969+
if (nh->ce_mask & NH_ATTR_ENCAP && nh->nh_encap)
970+
nh_encap_dump(nh->nh_encap, dp);
971+
874972
if (nh->ce_mask & NH_ATTR_FLAG_BLACKHOLE)
875973
nl_dump(dp, " blackhole");
876974

@@ -931,6 +1029,8 @@ static uint64_t nh_compare(struct nl_object *a, struct nl_object *b,
9311029
diff |= _DIFF(NH_ATTR_RES_UNBALANCED_TIMER,
9321030
src->res_grp_unbalanced_timer !=
9331031
dst->res_grp_unbalanced_timer);
1032+
diff |= _DIFF(NH_ATTR_ENCAP,
1033+
nh_encap_compare(src->nh_encap, dst->nh_encap));
9341034
#undef _DIFF
9351035

9361036
return diff;

libnl-route-3.sym

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,8 @@ global:
13911391
rtnl_nh_set_res_group_bucket_size;
13921392
rtnl_nh_set_res_group_idle_timer;
13931393
rtnl_nh_set_res_group_unbalanced_timer;
1394+
rtnl_nh_set_encap;
1395+
rtnl_nh_get_encap;
13941396
rtnl_route_nh_set_encap;
13951397
rtnl_route_nh_get_encap;
13961398
} libnl_3_11;

tests/cksuite-route-nh.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <netlink/route/nh.h>
1919
#include <netlink/route/link.h>
2020
#include <netlink/route/addr.h>
21+
#include <netlink/route/nexthop.h>
2122

2223
#include "cksuite-all.h"
2324

@@ -75,6 +76,72 @@ START_TEST(test_kernel_roundtrip_basic_v4)
7576
}
7677
END_TEST
7778

79+
/* Kernel round-trip tests for MPLS encap on rtnl_nh */
80+
81+
START_TEST(test_kernel_roundtrip_encap_mpls)
82+
{
83+
const char *IFNAME_DUMMY = "nh-dummy-encap0";
84+
_nltst_auto_delete_link const char *auto_del_dummy = NULL;
85+
_nl_auto_nl_socket struct nl_sock *sk = NULL;
86+
_nl_auto_nl_cache struct nl_cache *cache = NULL;
87+
_nl_auto_rtnl_nh struct rtnl_nh *nh = NULL;
88+
struct rtnl_nh *knh;
89+
struct rtnl_nh_encap *kencap;
90+
_nl_auto_nl_addr struct nl_addr *gw4 = NULL;
91+
_nl_auto_nl_addr struct nl_addr *labels = NULL;
92+
struct rtnl_nh_encap *encap = NULL;
93+
int ifindex_dummy;
94+
95+
if (_nltst_skip_no_netns())
96+
return;
97+
98+
sk = _nltst_socket(NETLINK_ROUTE);
99+
100+
/* Create underlay */
101+
auto_del_dummy = IFNAME_DUMMY;
102+
_nltst_add_link(sk, IFNAME_DUMMY, "dummy", &ifindex_dummy);
103+
_nltst_link_up(sk, IFNAME_DUMMY);
104+
_nltst_addr4_add(sk, ifindex_dummy, "192.0.2.2", 24);
105+
106+
/* Build nexthop: v4 gw over dummy with MPLS encap */
107+
nh = rtnl_nh_alloc();
108+
ck_assert_ptr_nonnull(nh);
109+
ck_assert_int_eq(rtnl_nh_set_id(nh, 3101), 0);
110+
111+
/* Surprisingly the kernel accepts a nexthop with encap and a gw */
112+
ck_assert_int_eq(nl_addr_parse("192.0.2.1", AF_INET, &gw4), 0);
113+
ck_assert_int_eq(rtnl_nh_set_gateway(nh, gw4), 0);
114+
115+
encap = rtnl_nh_encap_alloc();
116+
ck_assert_ptr_nonnull(encap);
117+
ck_assert_int_eq(nl_addr_parse("100", AF_MPLS, &labels), 0);
118+
ck_assert_int_eq(rtnl_nh_encap_mpls(encap, labels, 64), 0);
119+
ck_assert_int_eq(rtnl_nh_set_encap(nh, encap), 0);
120+
121+
/* Fails - we need a family & oif*/
122+
ck_assert_int_eq(rtnl_nh_add(sk, nh, NLM_F_CREATE), -NLE_INVAL);
123+
124+
/* Fails, we need a family */
125+
ck_assert_int_eq(rtnl_nh_set_oif(nh, (uint32_t)ifindex_dummy), 0);
126+
ck_assert_int_eq(rtnl_nh_add(sk, nh, NLM_F_CREATE), -NLE_INVAL);
127+
128+
ck_assert_int_eq(rtnl_nh_set_family(nh, AF_INET), 0);
129+
ck_assert_int_eq(rtnl_nh_add(sk, nh, NLM_F_CREATE), 0);
130+
131+
/* Query and verify */
132+
ck_assert_int_eq(rtnl_nh_alloc_cache(sk, AF_UNSPEC, &cache), 0);
133+
knh = rtnl_nh_get(cache, 3101);
134+
ck_assert_ptr_nonnull(knh);
135+
ck_assert_int_eq(rtnl_nh_get_id(knh), 3101);
136+
ck_assert_int_eq(rtnl_nh_get_oif(knh), ifindex_dummy);
137+
138+
kencap = rtnl_nh_get_encap(knh);
139+
ck_assert_ptr_nonnull(kencap);
140+
ck_assert_ptr_nonnull(rtnl_nh_get_encap_mpls_dst(kencap));
141+
ck_assert_uint_eq(rtnl_nh_get_encap_mpls_ttl(kencap), 64);
142+
}
143+
END_TEST
144+
78145
START_TEST(test_kernel_negative_mismatched_gw_family)
79146
{
80147
const char *IFNAME_DUMMY = "nh-dummy-neg0";
@@ -518,6 +585,51 @@ START_TEST(test_api_set_get_all)
518585
}
519586
END_TEST
520587

588+
/* Userspace tests for MPLS encap on rtnl_nh */
589+
590+
START_TEST(test_api_encap_mpls_set_get)
591+
{
592+
_nl_auto_rtnl_nh struct rtnl_nh *nh = NULL;
593+
struct rtnl_nh_encap *encap = NULL;
594+
struct rtnl_nh_encap *got = NULL;
595+
_nl_auto_nl_addr struct nl_addr *labels = NULL;
596+
597+
/* Allocate nh and an encap container */
598+
nh = rtnl_nh_alloc();
599+
ck_assert_ptr_nonnull(nh);
600+
601+
/* Negative: NULL nh */
602+
encap = rtnl_nh_encap_alloc();
603+
ck_assert_ptr_nonnull(encap);
604+
ck_assert_int_eq(rtnl_nh_set_encap(NULL, encap), -NLE_INVAL);
605+
606+
/* Invalid encap (no type set) can be assigned in userspace, but getters
607+
* should reflect absence of MPLS specifics.
608+
*/
609+
ck_assert_int_eq(rtnl_nh_set_encap(nh, encap), -NLE_INVAL);
610+
ck_assert_ptr_eq(rtnl_nh_get_encap_mpls_dst(rtnl_nh_get_encap(nh)),
611+
NULL);
612+
ck_assert_uint_eq(rtnl_nh_get_encap_mpls_ttl(rtnl_nh_get_encap(nh)), 0);
613+
614+
/* Now build a valid MPLS encap: push label 100 with TTL 64 */
615+
ck_assert_int_eq(nl_addr_parse("100", AF_MPLS, &labels), 0);
616+
ck_assert_int_eq(rtnl_nh_encap_mpls(encap, labels, 64), 0);
617+
618+
/* Attach and retrieve */
619+
ck_assert_int_eq(rtnl_nh_set_encap(nh, encap), 0);
620+
got = rtnl_nh_get_encap(nh);
621+
ck_assert_ptr_nonnull(got);
622+
623+
/* Access MPLS-specific getters */
624+
ck_assert_ptr_nonnull(rtnl_nh_get_encap_mpls_dst(got));
625+
ck_assert_uint_eq(rtnl_nh_get_encap_mpls_ttl(got), 64);
626+
627+
/* Clear encap */
628+
ck_assert_int_eq(rtnl_nh_set_encap(nh, NULL), 0);
629+
ck_assert_ptr_eq(rtnl_nh_get_encap(nh), NULL);
630+
}
631+
END_TEST
632+
521633
Suite *make_nl_route_nh_suite(void)
522634
{
523635
Suite *suite = suite_create("route-nh");
@@ -526,6 +638,8 @@ Suite *make_nl_route_nh_suite(void)
526638

527639
/* Comprehensive API setter/getter test (userspace only) */
528640
tcase_add_test(tc_api, test_api_set_get_all);
641+
/* Userspace encap tests */
642+
tcase_add_test(tc_api, test_api_encap_mpls_set_get);
529643
suite_add_tcase(suite, tc_api);
530644

531645
/* Kernel round-trip – needs private netns */
@@ -538,6 +652,8 @@ Suite *make_nl_route_nh_suite(void)
538652
tcase_add_test(tc_kernel, test_kernel_roundtrip_oif_only);
539653
tcase_add_test(tc_kernel, test_kernel_roundtrip_group_mpath);
540654
tcase_add_test(tc_kernel, test_kernel_roundtrip_group_resilient);
655+
/* Encap (MPLS) on rtnl_nh */
656+
tcase_add_test(tc_kernel, test_kernel_roundtrip_encap_mpls);
541657
/* Negative tests: kernel should reject invalid nexthops */
542658
tcase_add_test(tc_kernel, test_kernel_negative_mismatched_gw_family);
543659
tcase_add_test(tc_kernel, test_kernel_negative_group_without_entries);

0 commit comments

Comments
 (0)