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" ;
@@ -516,6 +583,51 @@ START_TEST(test_api_set_get_all)
516583}
517584END_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+
519631Suite * 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