| // SPDX-License-Identifier: GPL-2.0 |
| #include <test_progs.h> |
| #include <network_helpers.h> |
| #include <bpf/btf.h> |
| #include <linux/if_link.h> |
| #include <linux/udp.h> |
| #include <net/if.h> |
| #include <unistd.h> |
| |
| #include "xdp_flowtable.skel.h" |
| |
| #define TX_NETNS_NAME "ns0" |
| #define RX_NETNS_NAME "ns1" |
| |
| #define TX_NAME "v0" |
| #define FORWARD_NAME "v1" |
| #define RX_NAME "d0" |
| |
| #define TX_MAC "00:00:00:00:00:01" |
| #define FORWARD_MAC "00:00:00:00:00:02" |
| #define RX_MAC "00:00:00:00:00:03" |
| #define DST_MAC "00:00:00:00:00:04" |
| |
| #define TX_ADDR "10.0.0.1" |
| #define FORWARD_ADDR "10.0.0.2" |
| #define RX_ADDR "20.0.0.1" |
| #define DST_ADDR "20.0.0.2" |
| |
| #define PREFIX_LEN "8" |
| #define N_PACKETS 10 |
| #define UDP_PORT 12345 |
| #define UDP_PORT_STR "12345" |
| |
| static int send_udp_traffic(void) |
| { |
| struct sockaddr_storage addr; |
| int i, sock; |
| |
| if (make_sockaddr(AF_INET, DST_ADDR, UDP_PORT, &addr, NULL)) |
| return -EINVAL; |
| |
| sock = socket(AF_INET, SOCK_DGRAM, 0); |
| if (sock < 0) |
| return sock; |
| |
| for (i = 0; i < N_PACKETS; i++) { |
| unsigned char buf[] = { 0xaa, 0xbb, 0xcc }; |
| int n; |
| |
| n = sendto(sock, buf, sizeof(buf), MSG_NOSIGNAL | MSG_CONFIRM, |
| (struct sockaddr *)&addr, sizeof(addr)); |
| if (n != sizeof(buf)) { |
| close(sock); |
| return -EINVAL; |
| } |
| |
| usleep(50000); /* 50ms */ |
| } |
| close(sock); |
| |
| return 0; |
| } |
| |
| void test_xdp_flowtable(void) |
| { |
| struct xdp_flowtable *skel = NULL; |
| struct nstoken *tok = NULL; |
| int iifindex, stats_fd; |
| __u32 value, key = 0; |
| struct bpf_link *link; |
| |
| if (SYS_NOFAIL("nft -v")) { |
| fprintf(stdout, "Missing required nft tool\n"); |
| test__skip(); |
| return; |
| } |
| |
| SYS(out, "ip netns add " TX_NETNS_NAME); |
| SYS(out, "ip netns add " RX_NETNS_NAME); |
| |
| tok = open_netns(RX_NETNS_NAME); |
| if (!ASSERT_OK_PTR(tok, "setns")) |
| goto out; |
| |
| SYS(out, "sysctl -qw net.ipv4.conf.all.forwarding=1"); |
| |
| SYS(out, "ip link add " TX_NAME " type veth peer " FORWARD_NAME); |
| SYS(out, "ip link set " TX_NAME " netns " TX_NETNS_NAME); |
| SYS(out, "ip link set dev " FORWARD_NAME " address " FORWARD_MAC); |
| SYS(out, |
| "ip addr add " FORWARD_ADDR "/" PREFIX_LEN " dev " FORWARD_NAME); |
| SYS(out, "ip link set dev " FORWARD_NAME " up"); |
| |
| SYS(out, "ip link add " RX_NAME " type dummy"); |
| SYS(out, "ip link set dev " RX_NAME " address " RX_MAC); |
| SYS(out, "ip addr add " RX_ADDR "/" PREFIX_LEN " dev " RX_NAME); |
| SYS(out, "ip link set dev " RX_NAME " up"); |
| |
| /* configure the flowtable */ |
| SYS(out, "nft add table ip filter"); |
| SYS(out, |
| "nft add flowtable ip filter f { hook ingress priority 0\\; " |
| "devices = { " FORWARD_NAME ", " RX_NAME " }\\; }"); |
| SYS(out, |
| "nft add chain ip filter forward " |
| "{ type filter hook forward priority 0\\; }"); |
| SYS(out, |
| "nft add rule ip filter forward ip protocol udp th dport " |
| UDP_PORT_STR " flow add @f"); |
| |
| /* Avoid ARP calls */ |
| SYS(out, |
| "ip -4 neigh add " DST_ADDR " lladdr " DST_MAC " dev " RX_NAME); |
| |
| close_netns(tok); |
| tok = open_netns(TX_NETNS_NAME); |
| if (!ASSERT_OK_PTR(tok, "setns")) |
| goto out; |
| |
| SYS(out, "ip addr add " TX_ADDR "/" PREFIX_LEN " dev " TX_NAME); |
| SYS(out, "ip link set dev " TX_NAME " address " TX_MAC); |
| SYS(out, "ip link set dev " TX_NAME " up"); |
| SYS(out, "ip route add default via " FORWARD_ADDR); |
| |
| close_netns(tok); |
| tok = open_netns(RX_NETNS_NAME); |
| if (!ASSERT_OK_PTR(tok, "setns")) |
| goto out; |
| |
| iifindex = if_nametoindex(FORWARD_NAME); |
| if (!ASSERT_NEQ(iifindex, 0, "iifindex")) |
| goto out; |
| |
| skel = xdp_flowtable__open_and_load(); |
| if (!ASSERT_OK_PTR(skel, "skel")) |
| goto out; |
| |
| link = bpf_program__attach_xdp(skel->progs.xdp_flowtable_do_lookup, |
| iifindex); |
| if (!ASSERT_OK_PTR(link, "prog_attach")) |
| goto out; |
| |
| close_netns(tok); |
| tok = open_netns(TX_NETNS_NAME); |
| if (!ASSERT_OK_PTR(tok, "setns")) |
| goto out; |
| |
| if (!ASSERT_OK(send_udp_traffic(), "send udp")) |
| goto out; |
| |
| close_netns(tok); |
| tok = open_netns(RX_NETNS_NAME); |
| if (!ASSERT_OK_PTR(tok, "setns")) |
| goto out; |
| |
| stats_fd = bpf_map__fd(skel->maps.stats); |
| if (!ASSERT_OK(bpf_map_lookup_elem(stats_fd, &key, &value), |
| "bpf_map_update_elem stats")) |
| goto out; |
| |
| ASSERT_GE(value, N_PACKETS - 2, "bpf_xdp_flow_lookup failed"); |
| out: |
| xdp_flowtable__destroy(skel); |
| if (tok) |
| close_netns(tok); |
| SYS_NOFAIL("ip netns del " TX_NETNS_NAME); |
| SYS_NOFAIL("ip netns del " RX_NETNS_NAME); |
| } |