diff options
Diffstat (limited to 'contrib/netbsd-tests/net/bpfilter/t_bpfilter.c')
-rw-r--r-- | contrib/netbsd-tests/net/bpfilter/t_bpfilter.c | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/contrib/netbsd-tests/net/bpfilter/t_bpfilter.c b/contrib/netbsd-tests/net/bpfilter/t_bpfilter.c new file mode 100644 index 0000000..9d8d56f --- /dev/null +++ b/contrib/netbsd-tests/net/bpfilter/t_bpfilter.c @@ -0,0 +1,400 @@ +/* $NetBSD: t_bpfilter.c,v 1.8 2014/06/24 11:32:36 alnsn Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <sys/cdefs.h> +__RCSID("$NetBSD: t_bpfilter.c,v 1.8 2014/06/24 11:32:36 alnsn Exp $"); + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/mbuf.h> +#include <sys/sysctl.h> +#include <sys/mman.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <net/if.h> +#include <net/if_ether.h> +#include <net/bpf.h> + +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <rump/rump.h> +#include <rump/rump_syscalls.h> + +/* XXX: atf-c.h has collisions with mbuf */ +#undef m_type +#undef m_data +#include <atf-c.h> + +#include "../../h_macros.h" +#include "../config/netconfig.c" + + +#define SNAPLEN UINT32_MAX + +#define BMAGIC UINT32_C(0x37) +#define HMAGIC UINT32_C(0xc2c2) +#define WMAGIC UINT32_C(0x7d7d7d7d) + +static const char magic_echo_reply_tail[7] = { + BMAGIC, + HMAGIC & 0xff, + HMAGIC & 0xff, + WMAGIC & 0xff, + WMAGIC & 0xff, + WMAGIC & 0xff, + WMAGIC & 0xff +}; + +/* + * Match ICMP_ECHOREPLY packet with 7 magic bytes at the end. + */ +static struct bpf_insn magic_echo_reply_prog[] = { + BPF_STMT(BPF_LD+BPF_ABS+BPF_B, + sizeof(struct ip) + offsetof(struct icmp, icmp_type)), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ICMP_ECHOREPLY, 1, 0), + BPF_STMT(BPF_RET+BPF_K, 0), + + BPF_STMT(BPF_LD+BPF_W+BPF_LEN, 0), /* A <- len */ + BPF_STMT(BPF_ALU+BPF_SUB+BPF_K, 7), /* A <- A - 7 */ + BPF_STMT(BPF_MISC+BPF_TAX, 0), /* X <- A */ + + BPF_STMT(BPF_LD+BPF_IND+BPF_B, 0), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, BMAGIC, 1, 0), + BPF_STMT(BPF_RET+BPF_K, 0), + + BPF_STMT(BPF_LD+BPF_IND+BPF_H, 1), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, HMAGIC, 1, 0), + BPF_STMT(BPF_RET+BPF_K, 0), + + BPF_STMT(BPF_LD+BPF_IND+BPF_W, 3), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, WMAGIC, 1, 0), + BPF_STMT(BPF_RET+BPF_K, 0), + + BPF_STMT(BPF_RET+BPF_K, SNAPLEN) +}; + +static struct bpf_insn badmem_prog[] = { + BPF_STMT(BPF_LD+BPF_MEM, 5), + BPF_STMT(BPF_RET+BPF_A, 0), +}; + +static struct bpf_insn noinitA_prog[] = { + BPF_STMT(BPF_RET+BPF_A, 0), +}; + +static struct bpf_insn noinitX_prog[] = { + BPF_STMT(BPF_MISC+BPF_TXA, 0), + BPF_STMT(BPF_RET+BPF_A, 0), +}; + +static uint16_t +in_cksum(void *data, size_t len) +{ + uint16_t *buf = data; + unsigned sum; + + for (sum = 0; len > 1; len -= 2) + sum += *buf++; + if (len) + sum += *(uint8_t *)buf; + + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + return ~sum; +} + +/* + * Based on netcfg_rump_pingtest(). + */ +static bool __unused +pingtest(const char *dst, unsigned int wirelen, const char tail[7]) +{ + struct timeval tv; + struct sockaddr_in sin; + struct icmp *icmp; + char *pkt; + unsigned int pktsize; + socklen_t slen; + int s; + bool rv = false; + + if (wirelen < ETHER_HDR_LEN + sizeof(struct ip)) + return false; + + pktsize = wirelen - ETHER_HDR_LEN - sizeof(struct ip); + if (pktsize < sizeof(struct icmp) + 7) + return false; + + s = rump_sys_socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); + if (s == -1) + return false; + + pkt = NULL; + + tv.tv_sec = 1; + tv.tv_usec = 0; + if (rump_sys_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, + &tv, sizeof(tv)) == -1) + goto out; + + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr(dst); + + pkt = calloc(1, pktsize); + icmp = (struct icmp *)pkt; + if (pkt == NULL) + goto out; + + memcpy(pkt + pktsize - 7, tail, 7); + icmp->icmp_type = ICMP_ECHO; + icmp->icmp_id = htons(37); + icmp->icmp_seq = htons(1); + icmp->icmp_cksum = in_cksum(pkt, pktsize); + + slen = sizeof(sin); + if (rump_sys_sendto(s, pkt, pktsize, 0, + (struct sockaddr *)&sin, slen) == -1) { + goto out; + } + + if (rump_sys_recvfrom(s, pkt, pktsize, 0, + (struct sockaddr *)&sin, &slen) == -1) + goto out; + + rv = true; + out: + if (pkt != NULL) + free(pkt); + rump_sys_close(s); + return rv; +} + +static void +magic_ping_test(const char *name, unsigned int wirelen) +{ + struct bpf_program prog; + struct bpf_stat bstat; + struct ifreq ifr; + struct timeval tv; + unsigned int bufsize; + bool pinged; + ssize_t n; + char *buf; + pid_t child; + int bpfd; + char token; + int channel[2]; + + struct bpf_hdr *hdr; + + RL(pipe(channel)); + + prog.bf_len = __arraycount(magic_echo_reply_prog); + prog.bf_insns = magic_echo_reply_prog; + + child = fork(); + RZ(rump_init()); + netcfg_rump_makeshmif(name, ifr.ifr_name); + + switch (child) { + case -1: + atf_tc_fail_errno("fork failed"); + case 0: + netcfg_rump_if(ifr.ifr_name, "10.1.1.10", "255.0.0.0"); + close(channel[0]); + ATF_CHECK(write(channel[1], "U", 1) == 1); + close(channel[1]); + pause(); + return; + default: + break; + } + + netcfg_rump_if(ifr.ifr_name, "10.1.1.20", "255.0.0.0"); + + RL(bpfd = rump_sys_open("/dev/bpf", O_RDONLY)); + + tv.tv_sec = 0; + tv.tv_usec = 500; + RL(rump_sys_ioctl(bpfd, BIOCSRTIMEOUT, &tv)); + + RL(rump_sys_ioctl(bpfd, BIOCGBLEN, &bufsize)); + RL(rump_sys_ioctl(bpfd, BIOCSETF, &prog)); + RL(rump_sys_ioctl(bpfd, BIOCSETIF, &ifr)); + + close(channel[1]); + ATF_CHECK(read(channel[0], &token, 1) == 1 && token == 'U'); + + pinged = pingtest("10.1.1.10", wirelen, magic_echo_reply_tail); + ATF_CHECK(pinged); + + buf = malloc(bufsize); + hdr = (struct bpf_hdr *)buf; + ATF_REQUIRE(buf != NULL); + ATF_REQUIRE(bufsize > sizeof(struct bpf_hdr)); + + n = rump_sys_read(bpfd, buf, bufsize); + + ATF_CHECK(n > (int)sizeof(struct bpf_hdr)); + ATF_CHECK(hdr->bh_caplen == MIN(SNAPLEN, wirelen)); + + RL(rump_sys_ioctl(bpfd, BIOCGSTATS, &bstat)); + ATF_CHECK(bstat.bs_capt >= 1); /* XXX == 1 */ + + rump_sys_close(bpfd); + free(buf); + + close(channel[0]); + + kill(child, SIGKILL); +} + +static int +send_bpf_prog(const char *ifname, struct bpf_program *prog) +{ + struct ifreq ifr; + int bpfd, e, rv; + + RZ(rump_init()); + netcfg_rump_makeshmif(ifname, ifr.ifr_name); + netcfg_rump_if(ifr.ifr_name, "10.1.1.20", "255.0.0.0"); + + RL(bpfd = rump_sys_open("/dev/bpf", O_RDONLY)); + + rv = rump_sys_ioctl(bpfd, BIOCSETF, prog); + e = errno; + + rump_sys_close(bpfd); + errno = e; + + return rv; +} + +ATF_TC(bpfiltercontig); +ATF_TC_HEAD(bpfiltercontig, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Checks that bpf program " + "can read bytes from contiguous buffer."); + atf_tc_set_md_var(tc, "timeout", "30"); +} + +ATF_TC_BODY(bpfiltercontig, tc) +{ + + magic_ping_test("bpfiltercontig", 128); +} + + +ATF_TC(bpfiltermchain); +ATF_TC_HEAD(bpfiltermchain, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Checks that bpf program " + "can read bytes from mbuf chain."); + atf_tc_set_md_var(tc, "timeout", "30"); +} + +ATF_TC_BODY(bpfiltermchain, tc) +{ + + magic_ping_test("bpfiltermchain", MINCLSIZE + 1); +} + + +ATF_TC(bpfilterbadmem); +ATF_TC_HEAD(bpfilterbadmem, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Checks that bpf program that " + "doesn't initialize memomy store is rejected by the kernel"); + atf_tc_set_md_var(tc, "timeout", "30"); +} + +ATF_TC_BODY(bpfilterbadmem, tc) +{ + struct bpf_program prog; + + prog.bf_len = __arraycount(badmem_prog); + prog.bf_insns = badmem_prog; + ATF_CHECK_ERRNO(EINVAL, send_bpf_prog("bpfilterbadmem", &prog) == -1); +} + +ATF_TC(bpfilternoinitA); +ATF_TC_HEAD(bpfilternoinitA, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Checks that bpf program that " + "doesn't initialize the A register is accepted by the kernel"); + atf_tc_set_md_var(tc, "timeout", "30"); +} + +ATF_TC_BODY(bpfilternoinitA, tc) +{ + struct bpf_program prog; + + prog.bf_len = __arraycount(noinitA_prog); + prog.bf_insns = noinitA_prog; + RL(send_bpf_prog("bpfilternoinitA", &prog)); +} + +ATF_TC(bpfilternoinitX); +ATF_TC_HEAD(bpfilternoinitX, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Checks that bpf program that " + "doesn't initialize the X register is accepted by the kernel"); + atf_tc_set_md_var(tc, "timeout", "30"); +} + +ATF_TC_BODY(bpfilternoinitX, tc) +{ + struct bpf_program prog; + + prog.bf_len = __arraycount(noinitX_prog); + prog.bf_insns = noinitX_prog; + RL(send_bpf_prog("bpfilternoinitX", &prog)); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, bpfiltercontig); + ATF_TP_ADD_TC(tp, bpfiltermchain); + ATF_TP_ADD_TC(tp, bpfilterbadmem); + ATF_TP_ADD_TC(tp, bpfilternoinitA); + ATF_TP_ADD_TC(tp, bpfilternoinitX); + + return atf_no_error(); +} |