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}
7677END_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+
78145START_TEST (test_kernel_negative_mismatched_gw_family )
79146{
80147 const char * IFNAME_DUMMY = "nh-dummy-neg0" ;
@@ -518,6 +585,52 @@ START_TEST(test_api_set_get_all)
518585}
519586END_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 )),
613+ - NLE_INVAL );
614+
615+ /* Now build a valid MPLS encap: push label 100 with TTL 64 */
616+ ck_assert_int_eq (nl_addr_parse ("100" , AF_MPLS , & labels ), 0 );
617+ ck_assert_int_eq (rtnl_nh_encap_mpls (encap , labels , 64 ), 0 );
618+
619+ /* Attach and retrieve */
620+ ck_assert_int_eq (rtnl_nh_set_encap (nh , encap ), 0 );
621+ got = rtnl_nh_get_encap (nh );
622+ ck_assert_ptr_nonnull (got );
623+
624+ /* Access MPLS-specific getters */
625+ ck_assert_ptr_nonnull (rtnl_nh_get_encap_mpls_dst (got ));
626+ ck_assert_uint_eq (rtnl_nh_get_encap_mpls_ttl (got ), 64 );
627+
628+ /* Clear encap */
629+ ck_assert_int_eq (rtnl_nh_set_encap (nh , NULL ), 0 );
630+ ck_assert_ptr_eq (rtnl_nh_get_encap (nh ), NULL );
631+ }
632+ END_TEST
633+
521634Suite * make_nl_route_nh_suite (void )
522635{
523636 Suite * suite = suite_create ("route-nh" );
@@ -526,6 +639,8 @@ Suite *make_nl_route_nh_suite(void)
526639
527640 /* Comprehensive API setter/getter test (userspace only) */
528641 tcase_add_test (tc_api , test_api_set_get_all );
642+ /* Userspace encap tests */
643+ tcase_add_test (tc_api , test_api_encap_mpls_set_get );
529644 suite_add_tcase (suite , tc_api );
530645
531646 /* Kernel round-trip – needs private netns */
@@ -538,6 +653,8 @@ Suite *make_nl_route_nh_suite(void)
538653 tcase_add_test (tc_kernel , test_kernel_roundtrip_oif_only );
539654 tcase_add_test (tc_kernel , test_kernel_roundtrip_group_mpath );
540655 tcase_add_test (tc_kernel , test_kernel_roundtrip_group_resilient );
656+ /* Encap (MPLS) on rtnl_nh */
657+ tcase_add_test (tc_kernel , test_kernel_roundtrip_encap_mpls );
541658 /* Negative tests: kernel should reject invalid nexthops */
542659 tcase_add_test (tc_kernel , test_kernel_negative_mismatched_gw_family );
543660 tcase_add_test (tc_kernel , test_kernel_negative_group_without_entries );
0 commit comments