diff options
| author | Andrey Ignatov <rdna@fb.com> | 2018-03-30 18:08:03 -0400 |
|---|---|---|
| committer | Daniel Borkmann <daniel@iogearbox.net> | 2018-03-30 20:15:30 -0400 |
| commit | e50b0a6f089308bec6b2d0198abed231dee4d277 (patch) | |
| tree | 66724709a340804aa54832903b69650130ff2983 /tools | |
| parent | 4fbac77d2d092b475dda9eea66da674369665427 (diff) | |
selftests/bpf: Selftest for sys_bind hooks
Add selftest to work with bpf_sock_addr context from
`BPF_PROG_TYPE_CGROUP_SOCK_ADDR` programs.
Try to bind(2) on IP:port and apply:
* loads to make sure context can be read correctly, including narrow
loads (byte, half) for IP and full-size loads (word) for all fields;
* stores to those fields allowed by verifier.
All combination from IPv4/IPv6 and TCP/UDP are tested.
Both scenarios are tested:
* valid programs can be loaded and attached;
* invalid programs can be neither loaded nor attached.
Test passes when expected data can be read from context in the
BPF-program, and after the call to bind(2) socket is bound to IP:port
pair that was written by BPF-program to the context.
Example:
# ./test_sock_addr
Attached bind4 program.
Test case #1 (IPv4/TCP):
Requested: bind(192.168.1.254, 4040) ..
Actual: bind(127.0.0.1, 4444)
Test case #2 (IPv4/UDP):
Requested: bind(192.168.1.254, 4040) ..
Actual: bind(127.0.0.1, 4444)
Attached bind6 program.
Test case #3 (IPv6/TCP):
Requested: bind(face:b00c:1234:5678::abcd, 6060) ..
Actual: bind(::1, 6666)
Test case #4 (IPv6/UDP):
Requested: bind(face:b00c:1234:5678::abcd, 6060) ..
Actual: bind(::1, 6666)
### SUCCESS
Signed-off-by: Andrey Ignatov <rdna@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/include/uapi/linux/bpf.h | 23 | ||||
| -rw-r--r-- | tools/lib/bpf/libbpf.c | 6 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/Makefile | 3 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/test_sock_addr.c | 486 |
4 files changed, 517 insertions, 1 deletions
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index e1c1fed63396..f2120c5c0578 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h | |||
| @@ -136,6 +136,7 @@ enum bpf_prog_type { | |||
| 136 | BPF_PROG_TYPE_CGROUP_DEVICE, | 136 | BPF_PROG_TYPE_CGROUP_DEVICE, |
| 137 | BPF_PROG_TYPE_SK_MSG, | 137 | BPF_PROG_TYPE_SK_MSG, |
| 138 | BPF_PROG_TYPE_RAW_TRACEPOINT, | 138 | BPF_PROG_TYPE_RAW_TRACEPOINT, |
| 139 | BPF_PROG_TYPE_CGROUP_SOCK_ADDR, | ||
| 139 | }; | 140 | }; |
| 140 | 141 | ||
| 141 | enum bpf_attach_type { | 142 | enum bpf_attach_type { |
| @@ -147,6 +148,8 @@ enum bpf_attach_type { | |||
| 147 | BPF_SK_SKB_STREAM_VERDICT, | 148 | BPF_SK_SKB_STREAM_VERDICT, |
| 148 | BPF_CGROUP_DEVICE, | 149 | BPF_CGROUP_DEVICE, |
| 149 | BPF_SK_MSG_VERDICT, | 150 | BPF_SK_MSG_VERDICT, |
| 151 | BPF_CGROUP_INET4_BIND, | ||
| 152 | BPF_CGROUP_INET6_BIND, | ||
| 150 | __MAX_BPF_ATTACH_TYPE | 153 | __MAX_BPF_ATTACH_TYPE |
| 151 | }; | 154 | }; |
| 152 | 155 | ||
| @@ -1009,6 +1012,26 @@ struct bpf_map_info { | |||
| 1009 | __u64 netns_ino; | 1012 | __u64 netns_ino; |
| 1010 | } __attribute__((aligned(8))); | 1013 | } __attribute__((aligned(8))); |
| 1011 | 1014 | ||
| 1015 | /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed | ||
| 1016 | * by user and intended to be used by socket (e.g. to bind to, depends on | ||
| 1017 | * attach attach type). | ||
| 1018 | */ | ||
| 1019 | struct bpf_sock_addr { | ||
| 1020 | __u32 user_family; /* Allows 4-byte read, but no write. */ | ||
| 1021 | __u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write. | ||
| 1022 | * Stored in network byte order. | ||
| 1023 | */ | ||
| 1024 | __u32 user_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write. | ||
| 1025 | * Stored in network byte order. | ||
| 1026 | */ | ||
| 1027 | __u32 user_port; /* Allows 4-byte read and write. | ||
| 1028 | * Stored in network byte order | ||
| 1029 | */ | ||
| 1030 | __u32 family; /* Allows 4-byte read, but no write */ | ||
| 1031 | __u32 type; /* Allows 4-byte read, but no write */ | ||
| 1032 | __u32 protocol; /* Allows 4-byte read, but no write */ | ||
| 1033 | }; | ||
| 1034 | |||
| 1012 | /* User bpf_sock_ops struct to access socket values and specify request ops | 1035 | /* User bpf_sock_ops struct to access socket values and specify request ops |
| 1013 | * and their replies. | 1036 | * and their replies. |
| 1014 | * Some of this fields are in network (bigendian) byte order and may need | 1037 | * Some of this fields are in network (bigendian) byte order and may need |
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 48e3e743ebf7..d7ce8818982c 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c | |||
| @@ -1859,6 +1859,9 @@ static void bpf_program__set_expected_attach_type(struct bpf_program *prog, | |||
| 1859 | 1859 | ||
| 1860 | #define BPF_PROG_SEC(string, ptype) BPF_PROG_SEC_FULL(string, ptype, 0) | 1860 | #define BPF_PROG_SEC(string, ptype) BPF_PROG_SEC_FULL(string, ptype, 0) |
| 1861 | 1861 | ||
| 1862 | #define BPF_SA_PROG_SEC(string, ptype) \ | ||
| 1863 | BPF_PROG_SEC_FULL(string, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, ptype) | ||
| 1864 | |||
| 1862 | static const struct { | 1865 | static const struct { |
| 1863 | const char *sec; | 1866 | const char *sec; |
| 1864 | size_t len; | 1867 | size_t len; |
| @@ -1882,10 +1885,13 @@ static const struct { | |||
| 1882 | BPF_PROG_SEC("sockops", BPF_PROG_TYPE_SOCK_OPS), | 1885 | BPF_PROG_SEC("sockops", BPF_PROG_TYPE_SOCK_OPS), |
| 1883 | BPF_PROG_SEC("sk_skb", BPF_PROG_TYPE_SK_SKB), | 1886 | BPF_PROG_SEC("sk_skb", BPF_PROG_TYPE_SK_SKB), |
| 1884 | BPF_PROG_SEC("sk_msg", BPF_PROG_TYPE_SK_MSG), | 1887 | BPF_PROG_SEC("sk_msg", BPF_PROG_TYPE_SK_MSG), |
| 1888 | BPF_SA_PROG_SEC("cgroup/bind4", BPF_CGROUP_INET4_BIND), | ||
| 1889 | BPF_SA_PROG_SEC("cgroup/bind6", BPF_CGROUP_INET6_BIND), | ||
| 1885 | }; | 1890 | }; |
| 1886 | 1891 | ||
| 1887 | #undef BPF_PROG_SEC | 1892 | #undef BPF_PROG_SEC |
| 1888 | #undef BPF_PROG_SEC_FULL | 1893 | #undef BPF_PROG_SEC_FULL |
| 1894 | #undef BPF_SA_PROG_SEC | ||
| 1889 | 1895 | ||
| 1890 | static int bpf_program__identify_section(struct bpf_program *prog) | 1896 | static int bpf_program__identify_section(struct bpf_program *prog) |
| 1891 | { | 1897 | { |
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index f35fb02bdf56..f4717c910874 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile | |||
| @@ -23,7 +23,7 @@ urandom_read: urandom_read.c | |||
| 23 | 23 | ||
| 24 | # Order correspond to 'make run_tests' order | 24 | # Order correspond to 'make run_tests' order |
| 25 | TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ | 25 | TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ |
| 26 | test_align test_verifier_log test_dev_cgroup test_tcpbpf_user | 26 | test_align test_verifier_log test_dev_cgroup test_tcpbpf_user test_sock_addr |
| 27 | 27 | ||
| 28 | TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ | 28 | TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ |
| 29 | test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ | 29 | test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ |
| @@ -51,6 +51,7 @@ $(TEST_GEN_PROGS): $(BPFOBJ) | |||
| 51 | $(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a | 51 | $(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a |
| 52 | 52 | ||
| 53 | $(OUTPUT)/test_dev_cgroup: cgroup_helpers.c | 53 | $(OUTPUT)/test_dev_cgroup: cgroup_helpers.c |
| 54 | $(OUTPUT)/test_sock_addr: cgroup_helpers.c | ||
| 54 | 55 | ||
| 55 | .PHONY: force | 56 | .PHONY: force |
| 56 | 57 | ||
diff --git a/tools/testing/selftests/bpf/test_sock_addr.c b/tools/testing/selftests/bpf/test_sock_addr.c new file mode 100644 index 000000000000..a57e13a65e37 --- /dev/null +++ b/tools/testing/selftests/bpf/test_sock_addr.c | |||
| @@ -0,0 +1,486 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | // Copyright (c) 2018 Facebook | ||
| 3 | |||
| 4 | #include <stdio.h> | ||
| 5 | #include <stdlib.h> | ||
| 6 | #include <unistd.h> | ||
| 7 | |||
| 8 | #include <arpa/inet.h> | ||
| 9 | #include <sys/types.h> | ||
| 10 | #include <sys/socket.h> | ||
| 11 | |||
| 12 | #include <linux/filter.h> | ||
| 13 | |||
| 14 | #include <bpf/bpf.h> | ||
| 15 | |||
| 16 | #include "cgroup_helpers.h" | ||
| 17 | |||
| 18 | #define CG_PATH "/foo" | ||
| 19 | |||
| 20 | #define SERV4_IP "192.168.1.254" | ||
| 21 | #define SERV4_REWRITE_IP "127.0.0.1" | ||
| 22 | #define SERV4_PORT 4040 | ||
| 23 | #define SERV4_REWRITE_PORT 4444 | ||
| 24 | |||
| 25 | #define SERV6_IP "face:b00c:1234:5678::abcd" | ||
| 26 | #define SERV6_REWRITE_IP "::1" | ||
| 27 | #define SERV6_PORT 6060 | ||
| 28 | #define SERV6_REWRITE_PORT 6666 | ||
| 29 | |||
| 30 | #define INET_NTOP_BUF 40 | ||
| 31 | |||
| 32 | typedef int (*load_fn)(enum bpf_attach_type, const char *comment); | ||
| 33 | typedef int (*info_fn)(int, struct sockaddr *, socklen_t *); | ||
| 34 | |||
| 35 | struct program { | ||
| 36 | enum bpf_attach_type type; | ||
| 37 | load_fn loadfn; | ||
| 38 | int fd; | ||
| 39 | const char *name; | ||
| 40 | enum bpf_attach_type invalid_type; | ||
| 41 | }; | ||
| 42 | |||
| 43 | char bpf_log_buf[BPF_LOG_BUF_SIZE]; | ||
| 44 | |||
| 45 | static int mk_sockaddr(int domain, const char *ip, unsigned short port, | ||
| 46 | struct sockaddr *addr, socklen_t addr_len) | ||
| 47 | { | ||
| 48 | struct sockaddr_in6 *addr6; | ||
| 49 | struct sockaddr_in *addr4; | ||
| 50 | |||
| 51 | if (domain != AF_INET && domain != AF_INET6) { | ||
| 52 | log_err("Unsupported address family"); | ||
| 53 | return -1; | ||
| 54 | } | ||
| 55 | |||
| 56 | memset(addr, 0, addr_len); | ||
| 57 | |||
| 58 | if (domain == AF_INET) { | ||
| 59 | if (addr_len < sizeof(struct sockaddr_in)) | ||
| 60 | return -1; | ||
| 61 | addr4 = (struct sockaddr_in *)addr; | ||
| 62 | addr4->sin_family = domain; | ||
| 63 | addr4->sin_port = htons(port); | ||
| 64 | if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1) { | ||
| 65 | log_err("Invalid IPv4: %s", ip); | ||
| 66 | return -1; | ||
| 67 | } | ||
| 68 | } else if (domain == AF_INET6) { | ||
| 69 | if (addr_len < sizeof(struct sockaddr_in6)) | ||
| 70 | return -1; | ||
| 71 | addr6 = (struct sockaddr_in6 *)addr; | ||
| 72 | addr6->sin6_family = domain; | ||
| 73 | addr6->sin6_port = htons(port); | ||
| 74 | if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1) { | ||
| 75 | log_err("Invalid IPv6: %s", ip); | ||
| 76 | return -1; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | return 0; | ||
| 81 | |||
