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,56 @@ 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+ /* This will free encap */
605+ ck_assert_int_eq (rtnl_nh_set_encap (NULL , encap ), - NLE_INVAL );
606+
607+ encap = rtnl_nh_encap_alloc ();
608+ ck_assert_ptr_nonnull (encap );
609+
610+ /* "empty" encap (no type set) cannot be assigned. */
611+ ck_assert_int_eq (rtnl_nh_set_encap (nh , encap ), - NLE_INVAL );
612+ ck_assert_ptr_eq (rtnl_nh_get_encap_mpls_dst (rtnl_nh_get_encap (nh )),
613+ NULL );
614+ ck_assert_uint_eq (rtnl_nh_get_encap_mpls_ttl (rtnl_nh_get_encap (nh )),
615+ - NLE_INVAL );
616+
617+ encap = rtnl_nh_encap_alloc ();
618+ ck_assert_ptr_nonnull (encap );
619+ /* Now build a valid MPLS encap: push label 100 with TTL 64 */
620+ ck_assert_int_eq (nl_addr_parse ("100" , AF_MPLS , & labels ), 0 );
621+ ck_assert_int_eq (rtnl_nh_encap_mpls (encap , labels , 64 ), 0 );
622+
623+ /* Attach and retrieve */
624+ ck_assert_int_eq (rtnl_nh_set_encap (nh , encap ), 0 );
625+ got = rtnl_nh_get_encap (nh );
626+ ck_assert_ptr_nonnull (got );
627+
628+ /* Access MPLS-specific getters */
629+ ck_assert_ptr_nonnull (rtnl_nh_get_encap_mpls_dst (got ));
630+ ck_assert_uint_eq (rtnl_nh_get_encap_mpls_ttl (got ), 64 );
631+
632+ /* Clear encap */
633+ ck_assert_int_eq (rtnl_nh_set_encap (nh , NULL ), 0 );
634+ ck_assert_ptr_eq (rtnl_nh_get_encap (nh ), NULL );
635+ }
636+ END_TEST
637+
521638Suite * make_nl_route_nh_suite (void )
522639{
523640 Suite * suite = suite_create ("route-nh" );
@@ -526,6 +643,8 @@ Suite *make_nl_route_nh_suite(void)
526643
527644 /* Comprehensive API setter/getter test (userspace only) */
528645 tcase_add_test (tc_api , test_api_set_get_all );
646+ /* Userspace encap tests */
647+ tcase_add_test (tc_api , test_api_encap_mpls_set_get );
529648 suite_add_tcase (suite , tc_api );
530649
531650 /* Kernel round-trip – needs private netns */
@@ -538,6 +657,8 @@ Suite *make_nl_route_nh_suite(void)
538657 tcase_add_test (tc_kernel , test_kernel_roundtrip_oif_only );
539658 tcase_add_test (tc_kernel , test_kernel_roundtrip_group_mpath );
540659 tcase_add_test (tc_kernel , test_kernel_roundtrip_group_resilient );
660+ /* Encap (MPLS) on rtnl_nh */
661+ tcase_add_test (tc_kernel , test_kernel_roundtrip_encap_mpls );
541662 /* Negative tests: kernel should reject invalid nexthops */
542663 tcase_add_test (tc_kernel , test_kernel_negative_mismatched_gw_family );
543664 tcase_add_test (tc_kernel , test_kernel_negative_group_without_entries );
0 commit comments