|
1 | 1 | // SPDX-License-Identifier: GPL-2.0
|
2 | 2 | /* Copyright (c) 2023 Isovalent */
|
3 | 3 | #include <uapi/linux/if_link.h>
|
| 4 | +#include <uapi/linux/pkt_sched.h> |
4 | 5 | #include <net/if.h>
|
5 | 6 | #include <test_progs.h>
|
6 | 7 |
|
@@ -1581,3 +1582,338 @@ void serial_test_tc_links_dev_cleanup(void)
|
1581 | 1582 | test_tc_links_dev_cleanup_target(BPF_TCX_INGRESS);
|
1582 | 1583 | test_tc_links_dev_cleanup_target(BPF_TCX_EGRESS);
|
1583 | 1584 | }
|
| 1585 | + |
| 1586 | +static void test_tc_chain_mixed(int target) |
| 1587 | +{ |
| 1588 | + LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1); |
| 1589 | + LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = loopback); |
| 1590 | + LIBBPF_OPTS(bpf_tcx_opts, optl); |
| 1591 | + struct test_tc_link *skel; |
| 1592 | + struct bpf_link *link; |
| 1593 | + __u32 pid1, pid2, pid3; |
| 1594 | + int err; |
| 1595 | + |
| 1596 | + skel = test_tc_link__open(); |
| 1597 | + if (!ASSERT_OK_PTR(skel, "skel_open")) |
| 1598 | + goto cleanup; |
| 1599 | + |
| 1600 | + ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target), |
| 1601 | + 0, "tc4_attach_type"); |
| 1602 | + ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc5, target), |
| 1603 | + 0, "tc5_attach_type"); |
| 1604 | + ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc6, target), |
| 1605 | + 0, "tc6_attach_type"); |
| 1606 | + |
| 1607 | + err = test_tc_link__load(skel); |
| 1608 | + if (!ASSERT_OK(err, "skel_load")) |
| 1609 | + goto cleanup; |
| 1610 | + |
| 1611 | + pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4)); |
| 1612 | + pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc5)); |
| 1613 | + pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc6)); |
| 1614 | + |
| 1615 | + ASSERT_NEQ(pid1, pid2, "prog_ids_1_2"); |
| 1616 | + ASSERT_NEQ(pid2, pid3, "prog_ids_2_3"); |
| 1617 | + |
| 1618 | + assert_mprog_count(target, 0); |
| 1619 | + |
| 1620 | + tc_hook.attach_point = target == BPF_TCX_INGRESS ? |
| 1621 | + BPF_TC_INGRESS : BPF_TC_EGRESS; |
| 1622 | + err = bpf_tc_hook_create(&tc_hook); |
| 1623 | + err = err == -EEXIST ? 0 : err; |
| 1624 | + if (!ASSERT_OK(err, "bpf_tc_hook_create")) |
| 1625 | + goto cleanup; |
| 1626 | + |
| 1627 | + tc_opts.prog_fd = bpf_program__fd(skel->progs.tc5); |
| 1628 | + err = bpf_tc_attach(&tc_hook, &tc_opts); |
| 1629 | + if (!ASSERT_OK(err, "bpf_tc_attach")) |
| 1630 | + goto cleanup; |
| 1631 | + |
| 1632 | + link = bpf_program__attach_tcx(skel->progs.tc6, loopback, &optl); |
| 1633 | + if (!ASSERT_OK_PTR(link, "link_attach")) |
| 1634 | + goto cleanup; |
| 1635 | + |
| 1636 | + skel->links.tc6 = link; |
| 1637 | + |
| 1638 | + assert_mprog_count(target, 1); |
| 1639 | + |
| 1640 | + ASSERT_OK(system(ping_cmd), ping_cmd); |
| 1641 | + |
| 1642 | + ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4"); |
| 1643 | + ASSERT_EQ(skel->bss->seen_tc5, false, "seen_tc5"); |
| 1644 | + ASSERT_EQ(skel->bss->seen_tc6, true, "seen_tc6"); |
| 1645 | + |
| 1646 | + skel->bss->seen_tc4 = false; |
| 1647 | + skel->bss->seen_tc5 = false; |
| 1648 | + skel->bss->seen_tc6 = false; |
| 1649 | + |
| 1650 | + err = bpf_link__update_program(skel->links.tc6, skel->progs.tc4); |
| 1651 | + if (!ASSERT_OK(err, "link_update")) |
| 1652 | + goto cleanup; |
| 1653 | + |
| 1654 | + assert_mprog_count(target, 1); |
| 1655 | + |
| 1656 | + ASSERT_OK(system(ping_cmd), ping_cmd); |
| 1657 | + |
| 1658 | + ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4"); |
| 1659 | + ASSERT_EQ(skel->bss->seen_tc5, true, "seen_tc5"); |
| 1660 | + ASSERT_EQ(skel->bss->seen_tc6, false, "seen_tc6"); |
| 1661 | + |
| 1662 | + skel->bss->seen_tc4 = false; |
| 1663 | + skel->bss->seen_tc5 = false; |
| 1664 | + skel->bss->seen_tc6 = false; |
| 1665 | + |
| 1666 | + err = bpf_link__detach(skel->links.tc6); |
| 1667 | + if (!ASSERT_OK(err, "prog_detach")) |
| 1668 | + goto cleanup; |
| 1669 | + |
| 1670 | + __assert_mprog_count(target, 0, true, loopback); |
| 1671 | + |
| 1672 | + ASSERT_OK(system(ping_cmd), ping_cmd); |
| 1673 | + |
| 1674 | + ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4"); |
| 1675 | + ASSERT_EQ(skel->bss->seen_tc5, true, "seen_tc5"); |
| 1676 | + ASSERT_EQ(skel->bss->seen_tc6, false, "seen_tc6"); |
| 1677 | + |
| 1678 | +cleanup: |
| 1679 | + tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0; |
| 1680 | + err = bpf_tc_detach(&tc_hook, &tc_opts); |
| 1681 | + ASSERT_OK(err, "bpf_tc_detach"); |
| 1682 | + |
| 1683 | + tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS; |
| 1684 | + bpf_tc_hook_destroy(&tc_hook); |
| 1685 | + |
| 1686 | + test_tc_link__destroy(skel); |
| 1687 | +} |
| 1688 | + |
| 1689 | +void serial_test_tc_links_chain_mixed(void) |
| 1690 | +{ |
| 1691 | + test_tc_chain_mixed(BPF_TCX_INGRESS); |
| 1692 | + test_tc_chain_mixed(BPF_TCX_EGRESS); |
| 1693 | +} |
| 1694 | + |
| 1695 | +static void test_tc_links_ingress(int target, bool chain_tc_old, |
| 1696 | + bool tcx_teardown_first) |
| 1697 | +{ |
| 1698 | + LIBBPF_OPTS(bpf_tc_opts, tc_opts, |
| 1699 | + .handle = 1, |
| 1700 | + .priority = 1, |
| 1701 | + ); |
| 1702 | + LIBBPF_OPTS(bpf_tc_hook, tc_hook, |
| 1703 | + .ifindex = loopback, |
| 1704 | + .attach_point = BPF_TC_CUSTOM, |
| 1705 | + .parent = TC_H_INGRESS, |
| 1706 | + ); |
| 1707 | + bool hook_created = false, tc_attached = false; |
| 1708 | + LIBBPF_OPTS(bpf_tcx_opts, optl); |
| 1709 | + __u32 pid1, pid2, pid3; |
| 1710 | + struct test_tc_link *skel; |
| 1711 | + struct bpf_link *link; |
| 1712 | + int err; |
| 1713 | + |
| 1714 | + skel = test_tc_link__open(); |
| 1715 | + if (!ASSERT_OK_PTR(skel, "skel_open")) |
| 1716 | + goto cleanup; |
| 1717 | + |
| 1718 | + ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target), |
| 1719 | + 0, "tc1_attach_type"); |
| 1720 | + ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target), |
| 1721 | + 0, "tc2_attach_type"); |
| 1722 | + |
| 1723 | + err = test_tc_link__load(skel); |
| 1724 | + if (!ASSERT_OK(err, "skel_load")) |
| 1725 | + goto cleanup; |
| 1726 | + |
| 1727 | + pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1)); |
| 1728 | + pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2)); |
| 1729 | + pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3)); |
| 1730 | + |
| 1731 | + ASSERT_NEQ(pid1, pid2, "prog_ids_1_2"); |
| 1732 | + ASSERT_NEQ(pid2, pid3, "prog_ids_2_3"); |
| 1733 | + |
| 1734 | + assert_mprog_count(target, 0); |
| 1735 | + |
| 1736 | + if (chain_tc_old) { |
| 1737 | + ASSERT_OK(system("tc qdisc add dev lo ingress"), "add_ingress"); |
| 1738 | + hook_created = true; |
| 1739 | + |
| 1740 | + tc_opts.prog_fd = bpf_program__fd(skel->progs.tc3); |
| 1741 | + err = bpf_tc_attach(&tc_hook, &tc_opts); |
| 1742 | + if (!ASSERT_OK(err, "bpf_tc_attach")) |
| 1743 | + goto cleanup; |
| 1744 | + tc_attached = true; |
| 1745 | + } |
| 1746 | + |
| 1747 | + link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl); |
| 1748 | + if (!ASSERT_OK_PTR(link, "link_attach")) |
| 1749 | + goto cleanup; |
| 1750 | + |
| 1751 | + skel->links.tc1 = link; |
| 1752 | + |
| 1753 | + link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl); |
| 1754 | + if (!ASSERT_OK_PTR(link, "link_attach")) |
| 1755 | + goto cleanup; |
| 1756 | + |
| 1757 | + skel->links.tc2 = link; |
| 1758 | + |
| 1759 | + assert_mprog_count(target, 2); |
| 1760 | + |
| 1761 | + ASSERT_OK(system(ping_cmd), ping_cmd); |
| 1762 | + |
| 1763 | + ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); |
| 1764 | + ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2"); |
| 1765 | + ASSERT_EQ(skel->bss->seen_tc3, chain_tc_old, "seen_tc3"); |
| 1766 | + |
| 1767 | + skel->bss->seen_tc1 = false; |
| 1768 | + skel->bss->seen_tc2 = false; |
| 1769 | + skel->bss->seen_tc3 = false; |
| 1770 | + |
| 1771 | + err = bpf_link__detach(skel->links.tc2); |
| 1772 | + if (!ASSERT_OK(err, "prog_detach")) |
| 1773 | + goto cleanup; |
| 1774 | + |
| 1775 | + assert_mprog_count(target, 1); |
| 1776 | + |
| 1777 | + ASSERT_OK(system(ping_cmd), ping_cmd); |
| 1778 | + |
| 1779 | + ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); |
| 1780 | + ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2"); |
| 1781 | + ASSERT_EQ(skel->bss->seen_tc3, chain_tc_old, "seen_tc3"); |
| 1782 | +cleanup: |
| 1783 | + if (tc_attached) { |
| 1784 | + tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0; |
| 1785 | + err = bpf_tc_detach(&tc_hook, &tc_opts); |
| 1786 | + ASSERT_OK(err, "bpf_tc_detach"); |
| 1787 | + } |
| 1788 | + ASSERT_OK(system(ping_cmd), ping_cmd); |
| 1789 | + assert_mprog_count(target, 1); |
| 1790 | + if (hook_created && tcx_teardown_first) |
| 1791 | + ASSERT_OK(system("tc qdisc del dev lo ingress"), "del_ingress"); |
| 1792 | + ASSERT_OK(system(ping_cmd), ping_cmd); |
| 1793 | + test_tc_link__destroy(skel); |
| 1794 | + ASSERT_OK(system(ping_cmd), ping_cmd); |
| 1795 | + if (hook_created && !tcx_teardown_first) |
| 1796 | + ASSERT_OK(system("tc qdisc del dev lo ingress"), "del_ingress"); |
| 1797 | + ASSERT_OK(system(ping_cmd), ping_cmd); |
| 1798 | + assert_mprog_count(target, 0); |
| 1799 | +} |
| 1800 | + |
| 1801 | +void serial_test_tc_links_ingress(void) |
| 1802 | +{ |
| 1803 | + test_tc_links_ingress(BPF_TCX_INGRESS, true, true); |
| 1804 | + test_tc_links_ingress(BPF_TCX_INGRESS, true, false); |
| 1805 | + test_tc_links_ingress(BPF_TCX_INGRESS, false, false); |
| 1806 | +} |
| 1807 | + |
| 1808 | +static void test_tc_links_dev_mixed(int target) |
| 1809 | +{ |
| 1810 | + LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1); |
| 1811 | + LIBBPF_OPTS(bpf_tc_hook, tc_hook); |
| 1812 | + LIBBPF_OPTS(bpf_tcx_opts, optl); |
| 1813 | + __u32 pid1, pid2, pid3, pid4; |
| 1814 | + struct test_tc_link *skel; |
| 1815 | + struct bpf_link *link; |
| 1816 | + int err, ifindex; |
| 1817 | + |
| 1818 | + ASSERT_OK(system("ip link add dev tcx_opts1 type veth peer name tcx_opts2"), "add veth"); |
| 1819 | + ifindex = if_nametoindex("tcx_opts1"); |
| 1820 | + ASSERT_NEQ(ifindex, 0, "non_zero_ifindex"); |
| 1821 | + |
| 1822 | + skel = test_tc_link__open(); |
| 1823 | + if (!ASSERT_OK_PTR(skel, "skel_open")) |
| 1824 | + goto cleanup; |
| 1825 | + |
| 1826 | + ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target), |
| 1827 | + 0, "tc1_attach_type"); |
| 1828 | + ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target), |
| 1829 | + 0, "tc2_attach_type"); |
| 1830 | + ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target), |
| 1831 | + 0, "tc3_attach_type"); |
| 1832 | + ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target), |
| 1833 | + 0, "tc4_attach_type"); |
| 1834 | + |
| 1835 | + err = test_tc_link__load(skel); |
| 1836 | + if (!ASSERT_OK(err, "skel_load")) |
| 1837 | + goto cleanup; |
| 1838 | + |
| 1839 | + pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1)); |
| 1840 | + pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2)); |
| 1841 | + pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3)); |
| 1842 | + pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4)); |
| 1843 | + |
| 1844 | + ASSERT_NEQ(pid1, pid2, "prog_ids_1_2"); |
| 1845 | + ASSERT_NEQ(pid3, pid4, "prog_ids_3_4"); |
| 1846 | + ASSERT_NEQ(pid2, pid3, "prog_ids_2_3"); |
| 1847 | + |
| 1848 | + assert_mprog_count(target, 0); |
| 1849 | + |
| 1850 | + link = bpf_program__attach_tcx(skel->progs.tc1, ifindex, &optl); |
| 1851 | + if (!ASSERT_OK_PTR(link, "link_attach")) |
| 1852 | + goto cleanup; |
| 1853 | + |
| 1854 | + skel->links.tc1 = link; |
| 1855 | + |
| 1856 | + assert_mprog_count_ifindex(ifindex, target, 1); |
| 1857 | + |
| 1858 | + link = bpf_program__attach_tcx(skel->progs.tc2, ifindex, &optl); |
| 1859 | + if (!ASSERT_OK_PTR(link, "link_attach")) |
| 1860 | + goto cleanup; |
| 1861 | + |
| 1862 | + skel->links.tc2 = link; |
| 1863 | + |
| 1864 | + assert_mprog_count_ifindex(ifindex, target, 2); |
| 1865 | + |
| 1866 | + link = bpf_program__attach_tcx(skel->progs.tc3, ifindex, &optl); |
| 1867 | + if (!ASSERT_OK_PTR(link, "link_attach")) |
| 1868 | + goto cleanup; |
| 1869 | + |
| 1870 | + skel->links.tc3 = link; |
| 1871 | + |
| 1872 | + assert_mprog_count_ifindex(ifindex, target, 3); |
| 1873 | + |
| 1874 | + link = bpf_program__attach_tcx(skel->progs.tc4, ifindex, &optl); |
| 1875 | + if (!ASSERT_OK_PTR(link, "link_attach")) |
| 1876 | + goto cleanup; |
| 1877 | + |
| 1878 | + skel->links.tc4 = link; |
| 1879 | + |
| 1880 | + assert_mprog_count_ifindex(ifindex, target, 4); |
| 1881 | + |
| 1882 | + tc_hook.ifindex = ifindex; |
| 1883 | + tc_hook.attach_point = target == BPF_TCX_INGRESS ? |
| 1884 | + BPF_TC_INGRESS : BPF_TC_EGRESS; |
| 1885 | + |
| 1886 | + err = bpf_tc_hook_create(&tc_hook); |
| 1887 | + err = err == -EEXIST ? 0 : err; |
| 1888 | + if (!ASSERT_OK(err, "bpf_tc_hook_create")) |
| 1889 | + goto cleanup; |
| 1890 | + |
| 1891 | + tc_opts.prog_fd = bpf_program__fd(skel->progs.tc5); |
| 1892 | + err = bpf_tc_attach(&tc_hook, &tc_opts); |
| 1893 | + if (!ASSERT_OK(err, "bpf_tc_attach")) |
| 1894 | + goto cleanup; |
| 1895 | + |
| 1896 | + ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth"); |
| 1897 | + ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed"); |
| 1898 | + ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed"); |
| 1899 | + |
| 1900 | + ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc1)), 0, "tc1_ifindex"); |
| 1901 | + ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc2)), 0, "tc2_ifindex"); |
| 1902 | + ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc3)), 0, "tc3_ifindex"); |
| 1903 | + ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc4)), 0, "tc4_ifindex"); |
| 1904 | + |
| 1905 | + test_tc_link__destroy(skel); |
| 1906 | + return; |
| 1907 | +cleanup: |
| 1908 | + test_tc_link__destroy(skel); |
| 1909 | + |
| 1910 | + ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth"); |
| 1911 | + ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed"); |
| 1912 | + ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed"); |
| 1913 | +} |
| 1914 | + |
| 1915 | +void serial_test_tc_links_dev_mixed(void) |
| 1916 | +{ |
| 1917 | + test_tc_links_dev_mixed(BPF_TCX_INGRESS); |
| 1918 | + test_tc_links_dev_mixed(BPF_TCX_EGRESS); |
| 1919 | +} |
0 commit comments