Path: blob/main/tests/sys/netlink/test_rtnl_route.c
282976 views
/*1* Copyright (c) 2026 Pouria Mousavizadeh Tehrani <[email protected]>2*3* SPDX-License-Identifier: BSD-2-Clause4*/56#include <sys/param.h>7#include <sys/module.h>8#include <sys/types.h>9#include <sys/socket.h>10#include <netinet/in.h>11#include <arpa/inet.h>1213#include <netlink/netlink.h>14#include <netlink/netlink_route.h>15#include "netlink/netlink_snl.h"16#include <netlink/netlink_snl_route.h>17#include <netlink/netlink_snl_route_compat.h>18#include <netlink/netlink_snl_route_parsers.h>1920#include <errno.h>21#include <unistd.h>22#include <time.h>2324#include <atf-c.h>2526static struct rtmsg *27prepare_rtm_by_dst(struct snl_writer *nw, char *dst)28{29struct rtmsg *rtm;30struct in_addr in_dst;3132inet_pton(AF_INET, dst, &in_dst);33rtm = snl_reserve_msg_object(nw, struct rtmsg);34if (rtm == NULL)35return (NULL);3637rtm->rtm_family = AF_INET;38rtm->rtm_protocol = RTPROT_STATIC;39rtm->rtm_type = RTN_UNICAST;40rtm->rtm_dst_len = 24;41rtm->rtm_flags = RTF_GATEWAY;42snl_add_msg_attr_ip4(nw, RTA_DST, &in_dst);4344return (rtm);45}4647static void48cleanup_route_by_dst(struct snl_state *ss, struct snl_writer *nw, char *dst)49{50struct nlmsghdr *hdr, *rx_hdr;51struct snl_errmsg_data e = {};5253/* Delete route */54snl_init_writer(ss, nw);55ATF_REQUIRE((hdr = snl_create_msg_request(nw, RTM_DELROUTE)) != NULL);56ATF_REQUIRE(prepare_rtm_by_dst(nw, dst) != NULL);57ATF_REQUIRE((hdr = snl_finalize_msg(nw)) != NULL);58ATF_REQUIRE(snl_send_message(ss, hdr));59ATF_REQUIRE((rx_hdr = snl_read_reply(ss, hdr->nlmsg_seq)) != NULL);60}6162ATF_TC(rtnl_nhgrp);63ATF_TC_HEAD(rtnl_nhgrp, tc)64{65atf_tc_set_md_var(tc, "descr", "test nexthop group using netlink");66atf_tc_set_md_var(tc, "require.user", "root");67atf_tc_set_md_var(tc, "require.kmods", "netlink");68}6970ATF_TC_BODY(rtnl_nhgrp, tc)71{72struct snl_state ss;73struct snl_writer nw;74struct nlmsghdr *hdr, *rx_hdr;75struct in_addr gw1, gw2;76struct snl_errmsg_data e = {};77struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };78struct rtmsg *rtm;79struct rtnexthop *rtnh;80int off, off2;8182ATF_REQUIRE_MSG(snl_init(&ss, NETLINK_ROUTE), "snl_init() failed");8384inet_pton(AF_INET, "127.0.0.1", &gw1);85inet_pton(AF_INET, "127.0.0.2", &gw2);8687/* Create new multipath route */88snl_init_writer(&ss, &nw);89ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWROUTE)) != NULL);90hdr->nlmsg_flags |= NLM_F_CREATE;91ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "192.0.2.0")) != NULL);9293off = snl_add_msg_attr_nested(&nw, RTA_MULTIPATH);94/* first nexthop */95off2 = snl_get_msg_offset(&nw);96rtnh = snl_reserve_msg_object(&nw, struct rtnexthop);97rtnh->rtnh_flags = 0;98rtnh->rtnh_hops = 1;99rtnh->rtnh_ifindex = 0;100snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw1);101rtnh = snl_restore_msg_offset(&nw, off2, struct rtnexthop);102rtnh->rtnh_len = snl_get_msg_offset(&nw) - off2;103104/* second nexthop */105off2 = snl_get_msg_offset(&nw);106rtnh = snl_reserve_msg_object(&nw, struct rtnexthop);107rtnh->rtnh_flags = 0;108rtnh->rtnh_hops = 1;109rtnh->rtnh_ifindex = 0;110snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw2);111rtnh = snl_restore_msg_offset(&nw, off2, struct rtnexthop);112rtnh->rtnh_len = snl_get_msg_offset(&nw) - off2;113114snl_end_attr_nested(&nw, off);115116ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);117ATF_REQUIRE(snl_send_message(&ss, hdr));118ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);119ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));120ATF_REQUIRE_INTEQ(e.error, 0);121122/* Get route and check for its nexthop group */123snl_init_writer(&ss, &nw);124ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_GETROUTE)) != NULL);125ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "192.0.2.0")) != NULL);126ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);127ATF_REQUIRE(snl_send_message(&ss, hdr));128ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);129ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r));130ATF_CHECK(r.rta_knh_id != 0);131ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 2);132133cleanup_route_by_dst(&ss, &nw, "192.0.2.0");134}135136ATF_TC(rtnl_nhop_merge);137ATF_TC_HEAD(rtnl_nhop_merge, tc)138{139atf_tc_set_md_var(tc, "descr", "test merge of two independent nexthop using netlink");140atf_tc_set_md_var(tc, "require.user", "root");141atf_tc_set_md_var(tc, "require.kmods", "netlink");142}143144ATF_TC_BODY(rtnl_nhop_merge, tc)145{146struct snl_state ss;147struct snl_writer nw;148struct nlmsghdr *hdr, *rx_hdr;149struct in_addr gw1, gw2;150struct snl_errmsg_data e = {};151struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };152struct rtmsg *rtm;153struct rtnexthop *rtnh;154155ATF_REQUIRE_MSG(snl_init(&ss, NETLINK_ROUTE), "snl_init() failed");156157inet_pton(AF_INET, "127.0.1.1", &gw1);158inet_pton(AF_INET, "127.0.1.2", &gw2);159160/* Create new route with single nhop */161snl_init_writer(&ss, &nw);162ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWROUTE)) != NULL);163hdr->nlmsg_flags |= NLM_F_CREATE;164ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "198.51.100.0")) != NULL);165snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw1);166ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);167ATF_REQUIRE(snl_send_message(&ss, hdr));168ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);169ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));170ATF_REQUIRE_INTEQ(e.error, 0);171172/* Get route and verify it's NOT a nexthop group */173snl_init_writer(&ss, &nw);174ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_GETROUTE)) != NULL);175ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "198.51.100.0")) != NULL);176ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);177ATF_REQUIRE(snl_send_message(&ss, hdr));178ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);179ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r));180ATF_CHECK(r.rta_knh_id != 0);181ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 0);182183/* Append anoher nhop */184snl_init_writer(&ss, &nw);185ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWROUTE)) != NULL);186hdr->nlmsg_flags |= NLM_F_APPEND;187ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "198.51.100.0")) != NULL);188snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw2);189ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);190ATF_REQUIRE(snl_send_message(&ss, hdr));191ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);192ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));193ATF_REQUIRE_INTEQ(e.error, 0);194195/* Get route and verify it became a nexthop group */196snl_init_writer(&ss, &nw);197ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_GETROUTE)) != NULL);198ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "198.51.100.0")) != NULL);199ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);200ATF_REQUIRE(snl_send_message(&ss, hdr));201ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);202ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r));203ATF_CHECK(r.rta_knh_id != 0);204ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 2);205206cleanup_route_by_dst(&ss, &nw, "198.51.100.0");207}208209ATF_TC(rtnl_nhgrp_expire);210ATF_TC_HEAD(rtnl_nhgrp_expire, tc)211{212atf_tc_set_md_var(tc, "descr", "test nhop expiration of a member inside nhgrp using netlink");213atf_tc_set_md_var(tc, "require.user", "root");214atf_tc_set_md_var(tc, "require.kmods", "netlink");215}216217ATF_TC_BODY(rtnl_nhgrp_expire, tc)218{219struct snl_state ss;220struct snl_writer nw;221struct nlmsghdr *hdr, *rx_hdr;222struct in_addr gw1, gw2, gw3;223struct snl_errmsg_data e = {};224struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };225struct rtmsg *rtm;226struct rtnexthop *rtnh;227struct timespec ts;228int off, off2;229230ATF_REQUIRE_MSG(snl_init(&ss, NETLINK_ROUTE), "snl_init() failed");231232inet_pton(AF_INET, "127.0.2.1", &gw1);233inet_pton(AF_INET, "127.0.2.2", &gw2);234inet_pton(AF_INET, "127.0.2.3", &gw3);235236/* create new multipath route */237snl_init_writer(&ss, &nw);238ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWROUTE)) != NULL);239hdr->nlmsg_flags |= NLM_F_CREATE;240ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "203.0.113.0")) != NULL);241242off = snl_add_msg_attr_nested(&nw, RTA_MULTIPATH);243/* first nexthop */244off2 = snl_get_msg_offset(&nw);245rtnh = snl_reserve_msg_object(&nw, struct rtnexthop);246rtnh->rtnh_flags = 0;247rtnh->rtnh_hops = 1;248rtnh->rtnh_ifindex = 0;249snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw1);250rtnh = snl_restore_msg_offset(&nw, off2, struct rtnexthop);251rtnh->rtnh_len = snl_get_msg_offset(&nw) - off2;252253/* second nexthop */254off2 = snl_get_msg_offset(&nw);255rtnh = snl_reserve_msg_object(&nw, struct rtnexthop);256rtnh->rtnh_flags = 0;257rtnh->rtnh_hops = 1;258rtnh->rtnh_ifindex = 0;259snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw2);260rtnh = snl_restore_msg_offset(&nw, off2, struct rtnexthop);261rtnh->rtnh_len = snl_get_msg_offset(&nw) - off2;262263snl_end_attr_nested(&nw, off);264265ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);266ATF_REQUIRE(snl_send_message(&ss, hdr));267ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);268ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));269ATF_REQUIRE_INTEQ(e.error, 0);270271/* append anoher nhop with expiration time */272snl_init_writer(&ss, &nw);273ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWROUTE)) != NULL);274hdr->nlmsg_flags |= NLM_F_APPEND;275ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "203.0.113.0")) != NULL);276snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw3);277/* expire after 1 seconds */278clock_gettime(CLOCK_REALTIME_FAST, &ts);279snl_add_msg_attr_u32(&nw, RTA_EXPIRES, ts.tv_sec + 1);280ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);281ATF_REQUIRE(snl_send_message(&ss, hdr));282ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);283ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));284ATF_REQUIRE_INTEQ(e.error, 0);285286/* get route and check for number of nhops */287snl_init_writer(&ss, &nw);288ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_GETROUTE)) != NULL);289ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "203.0.113.0")) != NULL);290ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);291ATF_REQUIRE(snl_send_message(&ss, hdr));292ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);293ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r));294ATF_CHECK(r.rta_knh_id != 0);295ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 3);296297/* wait for 2 seconds and try again */298sleep(2);299300/* get route and check for number of nhops */301snl_init_writer(&ss, &nw);302ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_GETROUTE)) != NULL);303ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "203.0.113.0")) != NULL);304ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);305ATF_REQUIRE(snl_send_message(&ss, hdr));306ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);307ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r));308ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 2);309310cleanup_route_by_dst(&ss, &nw, "203.0.113.0");311}312313ATF_TC(rtnl_nhgrp_big_nhops);314ATF_TC_HEAD(rtnl_nhgrp_big_nhops, tc)315{316atf_tc_set_md_var(tc, "descr", "test RTA_MULTIPATH with too many nhops using netlink");317atf_tc_set_md_var(tc, "require.user", "root");318atf_tc_set_md_var(tc, "require.kmods", "netlink");319}320321ATF_TC_BODY(rtnl_nhgrp_big_nhops, tc)322{323struct snl_state ss;324struct snl_writer nw;325struct nlmsghdr *hdr, *rx_hdr;326struct in_addr gw;327struct snl_errmsg_data e = {};328struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };329struct rtmsg *rtm;330struct rtnexthop *rtnh;331int nhop, max_nhop, off, off2;332333max_nhop = 25;334ATF_REQUIRE_MSG(snl_init(&ss, NETLINK_ROUTE), "snl_init() failed");335336inet_pton(AF_INET, "127.0.2.1", &gw);337338/* create new multipath route */339snl_init_writer(&ss, &nw);340ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWROUTE)) != NULL);341hdr->nlmsg_flags |= NLM_F_CREATE;342ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "203.0.113.0")) != NULL);343344off = snl_add_msg_attr_nested(&nw, RTA_MULTIPATH);345for (nhop = 0; nhop < max_nhop; nhop++) {346off2 = snl_get_msg_offset(&nw);347rtnh = snl_reserve_msg_object(&nw, struct rtnexthop);348rtnh->rtnh_flags = 0;349rtnh->rtnh_hops = nhop + 1;350rtnh->rtnh_ifindex = 0;351snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw);352rtnh = snl_restore_msg_offset(&nw, off2, struct rtnexthop);353rtnh->rtnh_len = snl_get_msg_offset(&nw) - off2;354}355snl_end_attr_nested(&nw, off);356357ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);358ATF_REQUIRE(snl_send_message(&ss, hdr));359ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);360ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));361ATF_REQUIRE_INTEQ(e.error, ENOMEM);362363cleanup_route_by_dst(&ss, &nw, "203.0.113.0");364}365366367ATF_TP_ADD_TCS(tp)368{369ATF_TP_ADD_TC(tp, rtnl_nhgrp);370ATF_TP_ADD_TC(tp, rtnl_nhgrp_expire);371ATF_TP_ADD_TC(tp, rtnl_nhop_merge);372ATF_TP_ADD_TC(tp, rtnl_nhgrp_big_nhops);373374return (atf_no_error());375}376377378