1/* $NetBSD: t_bpfilter.c,v 1.8 2014/06/24 11:32:36 alnsn Exp $ */ 2 3/*- 4 * Copyright (c) 2012 The NetBSD Foundation, Inc. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27#include <sys/cdefs.h> 28__RCSID("$NetBSD: t_bpfilter.c,v 1.8 2014/06/24 11:32:36 alnsn Exp $"); 29 30#include <sys/param.h> 31#include <sys/ioctl.h> 32#include <sys/socket.h> 33#include <sys/mbuf.h> 34#include <sys/sysctl.h> 35#include <sys/mman.h> 36#include <sys/wait.h> 37#include <unistd.h> 38 39#include <net/if.h> 40#include <net/if_ether.h> 41#include <net/bpf.h> 42 43#include <fcntl.h> 44#include <stdint.h> 45#include <stdio.h> 46#include <string.h> 47 48#include <rump/rump.h> 49#include <rump/rump_syscalls.h> 50 51/* XXX: atf-c.h has collisions with mbuf */ 52#undef m_type 53#undef m_data 54#include <atf-c.h> 55 56#include "../../h_macros.h" 57#include "../config/netconfig.c" 58 59 60#define SNAPLEN UINT32_MAX 61 62#define BMAGIC UINT32_C(0x37) 63#define HMAGIC UINT32_C(0xc2c2) 64#define WMAGIC UINT32_C(0x7d7d7d7d) 65 66static const char magic_echo_reply_tail[7] = { 67 BMAGIC, 68 HMAGIC & 0xff, 69 HMAGIC & 0xff, 70 WMAGIC & 0xff, 71 WMAGIC & 0xff, 72 WMAGIC & 0xff, 73 WMAGIC & 0xff 74}; 75 76/* 77 * Match ICMP_ECHOREPLY packet with 7 magic bytes at the end. 78 */ 79static struct bpf_insn magic_echo_reply_prog[] = { 80 BPF_STMT(BPF_LD+BPF_ABS+BPF_B, 81 sizeof(struct ip) + offsetof(struct icmp, icmp_type)), 82 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ICMP_ECHOREPLY, 1, 0), 83 BPF_STMT(BPF_RET+BPF_K, 0), 84 85 BPF_STMT(BPF_LD+BPF_W+BPF_LEN, 0), /* A <- len */ 86 BPF_STMT(BPF_ALU+BPF_SUB+BPF_K, 7), /* A <- A - 7 */ 87 BPF_STMT(BPF_MISC+BPF_TAX, 0), /* X <- A */ 88 89 BPF_STMT(BPF_LD+BPF_IND+BPF_B, 0), 90 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, BMAGIC, 1, 0), 91 BPF_STMT(BPF_RET+BPF_K, 0), 92 93 BPF_STMT(BPF_LD+BPF_IND+BPF_H, 1), 94 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, HMAGIC, 1, 0), 95 BPF_STMT(BPF_RET+BPF_K, 0), 96 97 BPF_STMT(BPF_LD+BPF_IND+BPF_W, 3), 98 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, WMAGIC, 1, 0), 99 BPF_STMT(BPF_RET+BPF_K, 0), 100 101 BPF_STMT(BPF_RET+BPF_K, SNAPLEN) 102}; 103 104static struct bpf_insn badmem_prog[] = { 105 BPF_STMT(BPF_LD+BPF_MEM, 5), 106 BPF_STMT(BPF_RET+BPF_A, 0), 107}; 108 109static struct bpf_insn noinitA_prog[] = { 110 BPF_STMT(BPF_RET+BPF_A, 0), 111}; 112 113static struct bpf_insn noinitX_prog[] = { 114 BPF_STMT(BPF_MISC+BPF_TXA, 0), 115 BPF_STMT(BPF_RET+BPF_A, 0), 116}; 117 118static uint16_t 119in_cksum(void *data, size_t len) 120{ 121 uint16_t *buf = data; 122 unsigned sum; 123 124 for (sum = 0; len > 1; len -= 2) 125 sum += *buf++; 126 if (len) 127 sum += *(uint8_t *)buf; 128 129 sum = (sum >> 16) + (sum & 0xffff); 130 sum += (sum >> 16); 131 132 return ~sum; 133} 134 135/* 136 * Based on netcfg_rump_pingtest(). 137 */ 138static bool __unused 139pingtest(const char *dst, unsigned int wirelen, const char tail[7]) 140{ 141 struct timeval tv; 142 struct sockaddr_in sin; 143 struct icmp *icmp; 144 char *pkt; 145 unsigned int pktsize; 146 socklen_t slen; 147 int s; 148 bool rv = false; 149 150 if (wirelen < ETHER_HDR_LEN + sizeof(struct ip)) 151 return false; 152 153 pktsize = wirelen - ETHER_HDR_LEN - sizeof(struct ip); 154 if (pktsize < sizeof(struct icmp) + 7) 155 return false; 156 157 s = rump_sys_socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); 158 if (s == -1) 159 return false; 160 161 pkt = NULL; 162 163 tv.tv_sec = 1; 164 tv.tv_usec = 0; 165 if (rump_sys_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, 166 &tv, sizeof(tv)) == -1) 167 goto out; 168 169 memset(&sin, 0, sizeof(sin)); 170 sin.sin_len = sizeof(sin); 171 sin.sin_family = AF_INET; 172 sin.sin_addr.s_addr = inet_addr(dst); 173 174 pkt = calloc(1, pktsize); 175 icmp = (struct icmp *)pkt; 176 if (pkt == NULL) 177 goto out; 178 179 memcpy(pkt + pktsize - 7, tail, 7); 180 icmp->icmp_type = ICMP_ECHO; 181 icmp->icmp_id = htons(37); 182 icmp->icmp_seq = htons(1); 183 icmp->icmp_cksum = in_cksum(pkt, pktsize); 184 185 slen = sizeof(sin); 186 if (rump_sys_sendto(s, pkt, pktsize, 0, 187 (struct sockaddr *)&sin, slen) == -1) { 188 goto out; 189 } 190 191 if (rump_sys_recvfrom(s, pkt, pktsize, 0, 192 (struct sockaddr *)&sin, &slen) == -1) 193 goto out; 194 195 rv = true; 196 out: 197 if (pkt != NULL) 198 free(pkt); 199 rump_sys_close(s); 200 return rv; 201} 202 203static void 204magic_ping_test(const char *name, unsigned int wirelen) 205{ 206 struct bpf_program prog; 207 struct bpf_stat bstat; 208 struct ifreq ifr; 209 struct timeval tv; 210 unsigned int bufsize; 211 bool pinged; 212 ssize_t n; 213 char *buf; 214 pid_t child; 215 int bpfd; 216 char token; 217 int channel[2]; 218 219 struct bpf_hdr *hdr; 220 221 RL(pipe(channel)); 222 223 prog.bf_len = __arraycount(magic_echo_reply_prog); 224 prog.bf_insns = magic_echo_reply_prog; 225 226 child = fork(); 227 RZ(rump_init()); 228 netcfg_rump_makeshmif(name, ifr.ifr_name); 229 230 switch (child) { 231 case -1: 232 atf_tc_fail_errno("fork failed"); 233 case 0: 234 netcfg_rump_if(ifr.ifr_name, "10.1.1.10", "255.0.0.0"); 235 close(channel[0]); 236 ATF_CHECK(write(channel[1], "U", 1) == 1); 237 close(channel[1]); 238 pause(); 239 return; 240 default: 241 break; 242 } 243 244 netcfg_rump_if(ifr.ifr_name, "10.1.1.20", "255.0.0.0"); 245 246 RL(bpfd = rump_sys_open("/dev/bpf", O_RDONLY)); 247 248 tv.tv_sec = 0; 249 tv.tv_usec = 500; 250 RL(rump_sys_ioctl(bpfd, BIOCSRTIMEOUT, &tv)); 251 252 RL(rump_sys_ioctl(bpfd, BIOCGBLEN, &bufsize)); 253 RL(rump_sys_ioctl(bpfd, BIOCSETF, &prog)); 254 RL(rump_sys_ioctl(bpfd, BIOCSETIF, &ifr)); 255 256 close(channel[1]); 257 ATF_CHECK(read(channel[0], &token, 1) == 1 && token == 'U'); 258 259 pinged = pingtest("10.1.1.10", wirelen, magic_echo_reply_tail); 260 ATF_CHECK(pinged); 261 262 buf = malloc(bufsize); 263 hdr = (struct bpf_hdr *)buf; 264 ATF_REQUIRE(buf != NULL); 265 ATF_REQUIRE(bufsize > sizeof(struct bpf_hdr)); 266 267 n = rump_sys_read(bpfd, buf, bufsize); 268 269 ATF_CHECK(n > (int)sizeof(struct bpf_hdr)); 270 ATF_CHECK(hdr->bh_caplen == MIN(SNAPLEN, wirelen)); 271 272 RL(rump_sys_ioctl(bpfd, BIOCGSTATS, &bstat)); 273 ATF_CHECK(bstat.bs_capt >= 1); /* XXX == 1 */ 274 275 rump_sys_close(bpfd); 276 free(buf); 277 278 close(channel[0]); 279 280 kill(child, SIGKILL); 281} 282 283static int 284send_bpf_prog(const char *ifname, struct bpf_program *prog) 285{ 286 struct ifreq ifr; 287 int bpfd, e, rv; 288 289 RZ(rump_init()); 290 netcfg_rump_makeshmif(ifname, ifr.ifr_name); 291 netcfg_rump_if(ifr.ifr_name, "10.1.1.20", "255.0.0.0"); 292 293 RL(bpfd = rump_sys_open("/dev/bpf", O_RDONLY)); 294 295 rv = rump_sys_ioctl(bpfd, BIOCSETF, prog); 296 e = errno; 297 298 rump_sys_close(bpfd); 299 errno = e; 300 301 return rv; 302} 303 304ATF_TC(bpfiltercontig); 305ATF_TC_HEAD(bpfiltercontig, tc) 306{ 307 308 atf_tc_set_md_var(tc, "descr", "Checks that bpf program " 309 "can read bytes from contiguous buffer."); 310 atf_tc_set_md_var(tc, "timeout", "30"); 311} 312 313ATF_TC_BODY(bpfiltercontig, tc) 314{ 315 316 magic_ping_test("bpfiltercontig", 128); 317} 318 319 320ATF_TC(bpfiltermchain); 321ATF_TC_HEAD(bpfiltermchain, tc) 322{ 323 324 atf_tc_set_md_var(tc, "descr", "Checks that bpf program " 325 "can read bytes from mbuf chain."); 326 atf_tc_set_md_var(tc, "timeout", "30"); 327} 328 329ATF_TC_BODY(bpfiltermchain, tc) 330{ 331 332 magic_ping_test("bpfiltermchain", MINCLSIZE + 1); 333} 334 335 336ATF_TC(bpfilterbadmem); 337ATF_TC_HEAD(bpfilterbadmem, tc) 338{ 339 340 atf_tc_set_md_var(tc, "descr", "Checks that bpf program that " 341 "doesn't initialize memomy store is rejected by the kernel"); 342 atf_tc_set_md_var(tc, "timeout", "30"); 343} 344 345ATF_TC_BODY(bpfilterbadmem, tc) 346{ 347 struct bpf_program prog; 348 349 prog.bf_len = __arraycount(badmem_prog); 350 prog.bf_insns = badmem_prog; 351 ATF_CHECK_ERRNO(EINVAL, send_bpf_prog("bpfilterbadmem", &prog) == -1); 352} 353 354ATF_TC(bpfilternoinitA); 355ATF_TC_HEAD(bpfilternoinitA, tc) 356{ 357 358 atf_tc_set_md_var(tc, "descr", "Checks that bpf program that " 359 "doesn't initialize the A register is accepted by the kernel"); 360 atf_tc_set_md_var(tc, "timeout", "30"); 361} 362 363ATF_TC_BODY(bpfilternoinitA, tc) 364{ 365 struct bpf_program prog; 366 367 prog.bf_len = __arraycount(noinitA_prog); 368 prog.bf_insns = noinitA_prog; 369 RL(send_bpf_prog("bpfilternoinitA", &prog)); 370} 371 372ATF_TC(bpfilternoinitX); 373ATF_TC_HEAD(bpfilternoinitX, tc) 374{ 375 376 atf_tc_set_md_var(tc, "descr", "Checks that bpf program that " 377 "doesn't initialize the X register is accepted by the kernel"); 378 atf_tc_set_md_var(tc, "timeout", "30"); 379} 380 381ATF_TC_BODY(bpfilternoinitX, tc) 382{ 383 struct bpf_program prog; 384 385 prog.bf_len = __arraycount(noinitX_prog); 386 prog.bf_insns = noinitX_prog; 387 RL(send_bpf_prog("bpfilternoinitX", &prog)); 388} 389 390ATF_TP_ADD_TCS(tp) 391{ 392 393 ATF_TP_ADD_TC(tp, bpfiltercontig); 394 ATF_TP_ADD_TC(tp, bpfiltermchain); 395 ATF_TP_ADD_TC(tp, bpfilterbadmem); 396 ATF_TP_ADD_TC(tp, bpfilternoinitA); 397 ATF_TP_ADD_TC(tp, bpfilternoinitX); 398 399 return atf_no_error(); 400} 401