|
19 | 19 | #include <linux/error-injection.h>
|
20 | 20 | #include <linux/smp.h>
|
21 | 21 | #include <linux/sock_diag.h>
|
| 22 | +#include <linux/netfilter.h> |
22 | 23 | #include <net/xdp.h>
|
| 24 | +#include <net/netfilter/nf_bpf_link.h> |
23 | 25 |
|
24 | 26 | #define CREATE_TRACE_POINTS
|
25 | 27 | #include <trace/events/bpf_test_run.h>
|
@@ -1691,6 +1693,162 @@ int bpf_prog_test_run_syscall(struct bpf_prog *prog,
|
1691 | 1693 | return err;
|
1692 | 1694 | }
|
1693 | 1695 |
|
| 1696 | +static int verify_and_copy_hook_state(struct nf_hook_state *state, |
| 1697 | + const struct nf_hook_state *user, |
| 1698 | + struct net_device *dev) |
| 1699 | +{ |
| 1700 | + if (user->in || user->out) |
| 1701 | + return -EINVAL; |
| 1702 | + |
| 1703 | + if (user->net || user->sk || user->okfn) |
| 1704 | + return -EINVAL; |
| 1705 | + |
| 1706 | + switch (user->pf) { |
| 1707 | + case NFPROTO_IPV4: |
| 1708 | + case NFPROTO_IPV6: |
| 1709 | + switch (state->hook) { |
| 1710 | + case NF_INET_PRE_ROUTING: |
| 1711 | + state->in = dev; |
| 1712 | + break; |
| 1713 | + case NF_INET_LOCAL_IN: |
| 1714 | + state->in = dev; |
| 1715 | + break; |
| 1716 | + case NF_INET_FORWARD: |
| 1717 | + state->in = dev; |
| 1718 | + state->out = dev; |
| 1719 | + break; |
| 1720 | + case NF_INET_LOCAL_OUT: |
| 1721 | + state->out = dev; |
| 1722 | + break; |
| 1723 | + case NF_INET_POST_ROUTING: |
| 1724 | + state->out = dev; |
| 1725 | + break; |
| 1726 | + } |
| 1727 | + |
| 1728 | + break; |
| 1729 | + default: |
| 1730 | + return -EINVAL; |
| 1731 | + } |
| 1732 | + |
| 1733 | + state->pf = user->pf; |
| 1734 | + state->hook = user->hook; |
| 1735 | + |
| 1736 | + return 0; |
| 1737 | +} |
| 1738 | + |
| 1739 | +static __be16 nfproto_eth(int nfproto) |
| 1740 | +{ |
| 1741 | + switch (nfproto) { |
| 1742 | + case NFPROTO_IPV4: |
| 1743 | + return htons(ETH_P_IP); |
| 1744 | + case NFPROTO_IPV6: |
| 1745 | + break; |
| 1746 | + } |
| 1747 | + |
| 1748 | + return htons(ETH_P_IPV6); |
| 1749 | +} |
| 1750 | + |
| 1751 | +int bpf_prog_test_run_nf(struct bpf_prog *prog, |
| 1752 | + const union bpf_attr *kattr, |
| 1753 | + union bpf_attr __user *uattr) |
| 1754 | +{ |
| 1755 | + struct net *net = current->nsproxy->net_ns; |
| 1756 | + struct net_device *dev = net->loopback_dev; |
| 1757 | + struct nf_hook_state *user_ctx, hook_state = { |
| 1758 | + .pf = NFPROTO_IPV4, |
| 1759 | + .hook = NF_INET_LOCAL_OUT, |
| 1760 | + }; |
| 1761 | + u32 size = kattr->test.data_size_in; |
| 1762 | + u32 repeat = kattr->test.repeat; |
| 1763 | + struct bpf_nf_ctx ctx = { |
| 1764 | + .state = &hook_state, |
| 1765 | + }; |
| 1766 | + struct sk_buff *skb = NULL; |
| 1767 | + u32 retval, duration; |
| 1768 | + void *data; |
| 1769 | + int ret; |
| 1770 | + |
| 1771 | + if (kattr->test.flags || kattr->test.cpu || kattr->test.batch_size) |
| 1772 | + return -EINVAL; |
| 1773 | + |
| 1774 | + if (size < sizeof(struct iphdr)) |
| 1775 | + return -EINVAL; |
| 1776 | + |
| 1777 | + data = bpf_test_init(kattr, kattr->test.data_size_in, size, |
| 1778 | + NET_SKB_PAD + NET_IP_ALIGN, |
| 1779 | + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); |
| 1780 | + if (IS_ERR(data)) |
| 1781 | + return PTR_ERR(data); |
| 1782 | + |
| 1783 | + if (!repeat) |
| 1784 | + repeat = 1; |
| 1785 | + |
| 1786 | + user_ctx = bpf_ctx_init(kattr, sizeof(struct nf_hook_state)); |
| 1787 | + if (IS_ERR(user_ctx)) { |
| 1788 | + kfree(data); |
| 1789 | + return PTR_ERR(user_ctx); |
| 1790 | + } |
| 1791 | + |
| 1792 | + if (user_ctx) { |
| 1793 | + ret = verify_and_copy_hook_state(&hook_state, user_ctx, dev); |
| 1794 | + if (ret) |
| 1795 | + goto out; |
| 1796 | + } |
| 1797 | + |
| 1798 | + skb = slab_build_skb(data); |
| 1799 | + if (!skb) { |
| 1800 | + ret = -ENOMEM; |
| 1801 | + goto out; |
| 1802 | + } |
| 1803 | + |
| 1804 | + data = NULL; /* data released via kfree_skb */ |
| 1805 | + |
| 1806 | + skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN); |
| 1807 | + __skb_put(skb, size); |
| 1808 | + |
| 1809 | + ret = -EINVAL; |
| 1810 | + |
| 1811 | + if (hook_state.hook != NF_INET_LOCAL_OUT) { |
| 1812 | + if (size < ETH_HLEN + sizeof(struct iphdr)) |
| 1813 | + goto out; |
| 1814 | + |
| 1815 | + skb->protocol = eth_type_trans(skb, dev); |
| 1816 | + switch (skb->protocol) { |
| 1817 | + case htons(ETH_P_IP): |
| 1818 | + if (hook_state.pf == NFPROTO_IPV4) |
| 1819 | + break; |
| 1820 | + goto out; |
| 1821 | + case htons(ETH_P_IPV6): |
| 1822 | + if (size < ETH_HLEN + sizeof(struct ipv6hdr)) |
| 1823 | + goto out; |
| 1824 | + if (hook_state.pf == NFPROTO_IPV6) |
| 1825 | + break; |
| 1826 | + goto out; |
| 1827 | + default: |
| 1828 | + ret = -EPROTO; |
| 1829 | + goto out; |
| 1830 | + } |
| 1831 | + |
| 1832 | + skb_reset_network_header(skb); |
| 1833 | + } else { |
| 1834 | + skb->protocol = nfproto_eth(hook_state.pf); |
| 1835 | + } |
| 1836 | + |
| 1837 | + ctx.skb = skb; |
| 1838 | + |
| 1839 | + ret = bpf_test_run(prog, &ctx, repeat, &retval, &duration, false); |
| 1840 | + if (ret) |
| 1841 | + goto out; |
| 1842 | + |
| 1843 | + ret = bpf_test_finish(kattr, uattr, NULL, NULL, 0, retval, duration); |
| 1844 | + |
| 1845 | +out: |
| 1846 | + kfree(user_ctx); |
| 1847 | + kfree_skb(skb); |
| 1848 | + kfree(data); |
| 1849 | + return ret; |
| 1850 | +} |
| 1851 | + |
1694 | 1852 | static const struct btf_kfunc_id_set bpf_prog_test_kfunc_set = {
|
1695 | 1853 | .owner = THIS_MODULE,
|
1696 | 1854 | .set = &test_sk_check_kfunc_ids,
|
|
0 commit comments