Skip to content

Commit 06550e7

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 b151e13 commit 06550e7

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;
@@ -756,6 +831,26 @@ static int nexthop_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
756831
nexthop->ce_mask |= NH_ATTR_GROUP_TYPE;
757832
}
758833

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

966+
if (nh->ce_mask & NH_ATTR_ENCAP && nh->nh_encap)
967+
nh_encap_dump(nh->nh_encap, dp);
968+
871969
if (nh->ce_mask & NH_ATTR_FLAG_BLACKHOLE)
872970
nl_dump(dp, " blackhole");
873971

@@ -928,6 +1026,8 @@ static uint64_t nh_compare(struct nl_object *a, struct nl_object *b,
9281026
diff |= _DIFF(NH_ATTR_RES_UNBALANCED_TIMER,
9291027
src->res_grp_unbalanced_timer !=
9301028
dst->res_grp_unbalanced_timer);
1029+
diff |= _DIFF(NH_ATTR_ENCAP,
1030+
nh_encap_compare(src->nh_encap, dst->nh_encap));
9311031
#undef _DIFF
9321032

9331033
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";
@@ -516,6 +583,51 @@ START_TEST(test_api_set_get_all)
516583
}
517584
END_TEST
518585

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

525637
/* Comprehensive API setter/getter test (userspace only) */
526638
tcase_add_test(tc_api, test_api_set_get_all);
639+
/* Userspace encap tests */
640+
tcase_add_test(tc_api, test_api_encap_mpls_set_get);
527641
suite_add_tcase(suite, tc_api);
528642

529643
/* Kernel round-trip – needs private netns */
@@ -536,6 +650,8 @@ Suite *make_nl_route_nh_suite(void)
536650
tcase_add_test(tc_kernel, test_kernel_roundtrip_oif_only);
537651
tcase_add_test(tc_kernel, test_kernel_roundtrip_group_mpath);
538652
tcase_add_test(tc_kernel, test_kernel_roundtrip_group_resilient);
653+
/* Encap (MPLS) on rtnl_nh */
654+
tcase_add_test(tc_kernel, test_kernel_roundtrip_encap_mpls);
539655
/* Negative tests: kernel should reject invalid nexthops */
540656
tcase_add_test(tc_kernel, test_kernel_negative_mismatched_gw_family);
541657
tcase_add_test(tc_kernel, test_kernel_negative_group_without_entries);

0 commit comments

Comments
 (0)