~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/bpf/prog_tests/lwt_reroute.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
  2 
  3 /*
  4  * Test suite of lwt BPF programs that reroutes packets
  5  *   The file tests focus not only if these programs work as expected normally,
  6  *   but also if they can handle abnormal situations gracefully. This test
  7  *   suite currently only covers lwt_xmit hook. lwt_in tests have not been
  8  *   implemented.
  9  *
 10  * WARNING
 11  * -------
 12  *  This test suite can crash the kernel, thus should be run in a VM.
 13  *
 14  * Setup:
 15  * ---------
 16  *  all tests are performed in a single netns. A lwt encap route is setup for
 17  *  each subtest:
 18  *
 19  *    ip route add 10.0.0.0/24 encap bpf xmit <obj> sec "<section_N>" dev link_err
 20  *
 21  *  Here <obj> is statically defined to test_lwt_reroute.bpf.o, and it contains
 22  *  a single test program entry. This program sets packet mark by last byte of
 23  *  the IPv4 daddr. For example, a packet going to 1.2.3.4 will receive a skb
 24  *  mark 4. A packet will only be marked once, and IP x.x.x.0 will be skipped
 25  *  to avoid route loop. We didn't use generated BPF skeleton since the
 26  *  attachment for lwt programs are not supported by libbpf yet.
 27  *
 28  *  The test program will bring up a tun device, and sets up the following
 29  *  routes:
 30  *
 31  *    ip rule add pref 100 from all fwmark <tun_index> lookup 100
 32  *    ip route add table 100 default dev tun0
 33  *
 34  *  For normal testing, a ping command is running in the test netns:
 35  *
 36  *    ping 10.0.0.<tun_index> -c 1 -w 1 -s 100
 37  *
 38  *  For abnormal testing, fq is used as the qdisc of the tun device. Then a UDP
 39  *  socket will try to overflow the fq queue and trigger qdisc drop error.
 40  *
 41  * Scenarios:
 42  * --------------------------------
 43  *  1. Reroute to a running tun device
 44  *  2. Reroute to a device where qdisc drop
 45  *
 46  *  For case 1, ping packets should be received by the tun device.
 47  *
 48  *  For case 2, force UDP packets to overflow fq limit. As long as kernel
 49  *  is not crashed, it is considered successful.
 50  */
 51 #define NETNS "ns_lwt_reroute"
 52 #include <netinet/in.h>
 53 #include "lwt_helpers.h"
 54 #include "network_helpers.h"
 55 #include <linux/net_tstamp.h>
 56 
 57 #define BPF_OBJECT            "test_lwt_reroute.bpf.o"
 58 #define LOCAL_SRC             "10.0.0.1"
 59 #define TEST_CIDR             "10.0.0.0/24"
 60 #define XMIT_HOOK             "xmit"
 61 #define XMIT_SECTION          "lwt_xmit"
 62 #define NSEC_PER_SEC          1000000000ULL
 63 
 64 /* send a ping to be rerouted to the target device */
 65 static void ping_once(const char *ip)
 66 {
 67         /* We won't get a reply. Don't fail here */
 68         SYS_NOFAIL("ping %s -c1 -W1 -s %d",
 69                    ip, ICMP_PAYLOAD_SIZE);
 70 }
 71 
 72 /* Send snd_target UDP packets to overflow the fq queue and trigger qdisc drop
 73  * error. This is done via TX tstamp to force buffering delayed packets.
 74  */
 75 static int overflow_fq(int snd_target, const char *target_ip)
 76 {
 77         struct sockaddr_in addr = {
 78                 .sin_family = AF_INET,
 79                 .sin_port = htons(1234),
 80         };
 81 
 82         char data_buf[8]; /* only #pkts matter, so use a random small buffer */
 83         char control_buf[CMSG_SPACE(sizeof(uint64_t))];
 84         struct iovec iov = {
 85                 .iov_base = data_buf,
 86                 .iov_len = sizeof(data_buf),
 87         };
 88         int err = -1;
 89         int s = -1;
 90         struct sock_txtime txtime_on = {
 91                 .clockid = CLOCK_MONOTONIC,
 92                 .flags = 0,
 93         };
 94         struct msghdr msg = {
 95                 .msg_name = &addr,
 96                 .msg_namelen = sizeof(addr),
 97                 .msg_control = control_buf,
 98                 .msg_controllen = sizeof(control_buf),
 99                 .msg_iovlen = 1,
100                 .msg_iov = &iov,
101         };
102         struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
103 
104         memset(data_buf, 0, sizeof(data_buf));
105 
106         s = socket(AF_INET, SOCK_DGRAM, 0);
107         if (!ASSERT_GE(s, 0, "socket"))
108                 goto out;
109 
110         err = setsockopt(s, SOL_SOCKET, SO_TXTIME, &txtime_on, sizeof(txtime_on));
111         if (!ASSERT_OK(err, "setsockopt(SO_TXTIME)"))
112                 goto out;
113 
114         err = inet_pton(AF_INET, target_ip, &addr.sin_addr);
115         if (!ASSERT_EQ(err, 1, "inet_pton"))
116                 goto out;
117 
118         while (snd_target > 0) {
119                 struct timespec now;
120 
121                 memset(control_buf, 0, sizeof(control_buf));
122                 cmsg->cmsg_type = SCM_TXTIME;
123                 cmsg->cmsg_level = SOL_SOCKET;
124                 cmsg->cmsg_len = CMSG_LEN(sizeof(uint64_t));
125 
126                 err = clock_gettime(CLOCK_MONOTONIC, &now);
127                 if (!ASSERT_OK(err, "clock_gettime(CLOCK_MONOTONIC)")) {
128                         err = -1;
129                         goto out;
130                 }
131 
132                 *(uint64_t *)CMSG_DATA(cmsg) = (now.tv_nsec + 1) * NSEC_PER_SEC +
133                                                now.tv_nsec;
134 
135                 /* we will intentionally send more than fq limit, so ignore
136                  * the error here.
137                  */
138                 sendmsg(s, &msg, MSG_NOSIGNAL);
139                 snd_target--;
140         }
141 
142         /* no kernel crash so far is considered success */
143         err = 0;
144 
145 out:
146         if (s >= 0)
147                 close(s);
148 
149         return err;
150 }
151 
152 static int setup(const char *tun_dev)
153 {
154         int target_index = -1;
155         int tap_fd = -1;
156 
157         tap_fd = open_tuntap(tun_dev, false);
158         if (!ASSERT_GE(tap_fd, 0, "open_tun"))
159                 return -1;
160 
161         target_index = if_nametoindex(tun_dev);
162         if (!ASSERT_GE(target_index, 0, "if_nametoindex"))
163                 return -1;
164 
165         SYS(fail, "ip link add link_err type dummy");
166         SYS(fail, "ip link set lo up");
167         SYS(fail, "ip addr add dev lo " LOCAL_SRC "/32");
168         SYS(fail, "ip link set link_err up");
169         SYS(fail, "ip link set %s up", tun_dev);
170 
171         SYS(fail, "ip route add %s dev link_err encap bpf xmit obj %s sec lwt_xmit",
172             TEST_CIDR, BPF_OBJECT);
173 
174         SYS(fail, "ip rule add pref 100 from all fwmark %d lookup 100",
175             target_index);
176         SYS(fail, "ip route add t 100 default dev %s", tun_dev);
177 
178         return tap_fd;
179 
180 fail:
181         if (tap_fd >= 0)
182                 close(tap_fd);
183         return -1;
184 }
185 
186 static void test_lwt_reroute_normal_xmit(void)
187 {
188         const char *tun_dev = "tun0";
189         int tun_fd = -1;
190         int ifindex = -1;
191         char ip[256];
192         struct timeval timeo = {
193                 .tv_sec = 0,
194                 .tv_usec = 250000,
195         };
196 
197         tun_fd = setup(tun_dev);
198         if (!ASSERT_GE(tun_fd, 0, "setup_reroute"))
199                 return;
200 
201         ifindex = if_nametoindex(tun_dev);
202         if (!ASSERT_GE(ifindex, 0, "if_nametoindex"))
203                 return;
204 
205         snprintf(ip, 256, "10.0.0.%d", ifindex);
206 
207         /* ping packets should be received by the tun device */
208         ping_once(ip);
209 
210         if (!ASSERT_EQ(wait_for_packet(tun_fd, __expect_icmp_ipv4, &timeo), 1,
211                        "wait_for_packet"))
212                 log_err("%s xmit", __func__);
213 }
214 
215 /*
216  * Test the failure case when the skb is dropped at the qdisc. This is a
217  * regression prevention at the xmit hook only.
218  */
219 static void test_lwt_reroute_qdisc_dropped(void)
220 {
221         const char *tun_dev = "tun0";
222         int tun_fd = -1;
223         int ifindex = -1;
224         char ip[256];
225 
226         tun_fd = setup(tun_dev);
227         if (!ASSERT_GE(tun_fd, 0, "setup_reroute"))
228                 goto fail;
229 
230         SYS(fail, "tc qdisc replace dev %s root fq limit 5 flow_limit 5", tun_dev);
231 
232         ifindex = if_nametoindex(tun_dev);
233         if (!ASSERT_GE(ifindex, 0, "if_nametoindex"))
234                 return;
235 
236         snprintf(ip, 256, "10.0.0.%d", ifindex);
237         ASSERT_EQ(overflow_fq(10, ip), 0, "overflow_fq");
238 
239 fail:
240         if (tun_fd >= 0)
241                 close(tun_fd);
242 }
243 
244 static void *test_lwt_reroute_run(void *arg)
245 {
246         netns_delete();
247         RUN_TEST(lwt_reroute_normal_xmit);
248         RUN_TEST(lwt_reroute_qdisc_dropped);
249         return NULL;
250 }
251 
252 void test_lwt_reroute(void)
253 {
254         pthread_t test_thread;
255         int err;
256 
257         /* Run the tests in their own thread to isolate the namespace changes
258          * so they do not affect the environment of other tests.
259          * (specifically needed because of unshare(CLONE_NEWNS) in open_netns())
260          */
261         err = pthread_create(&test_thread, NULL, &test_lwt_reroute_run, NULL);
262         if (ASSERT_OK(err, "pthread_create"))
263                 ASSERT_OK(pthread_join(test_thread, NULL), "pthread_join");
264 }
265 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php