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,51 @@ 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 )), 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+
521633Suite * 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