npf_bpf_comp.c revision 1.14
1/*- 2 * Copyright (c) 2010-2019 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This material is based upon work partially supported by The 6 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30/* 31 * BPF byte-code generation for NPF rules. 32 * 33 * Overview 34 * 35 * Each NPF rule is compiled into BPF micro-program. There is a 36 * BPF byte-code fragment for each higher-level filtering logic, 37 * e.g. to match L4 protocol, IP/mask, etc. The generation process 38 * combines multiple BPF-byte code fragments into one program. 39 * 40 * Basic case 41 * 42 * Consider a basic case, where all filters should match. They 43 * are expressed as logical conjunction, e.g.: 44 * 45 * A and B and C and D 46 * 47 * Each test (filter) criterion can be evaluated to true (match) or 48 * false (no match) and the logic is as follows: 49 * 50 * - If the value is true, then jump to the "next" test (offset 0). 51 * 52 * - If the value is false, then jump to the JUMP_MAGIC value (0xff). 53 * This "magic" value is used to indicate that it will have to be 54 * patched at a later stage. 55 * 56 * Once all byte-code fragments are combined into one, then there 57 * are two additional steps: 58 * 59 * - Two instructions are appended at the end of the program: return 60 * "success" followed by return "failure". 61 * 62 * - All jumps with the JUMP_MAGIC value are patched to point to the 63 * "return failure" instruction. 64 * 65 * Therefore, if all filter criteria will match, then the first 66 * instruction will be reached, indicating a successful match of the 67 * rule. Otherwise, if any of the criteria will not match, it will 68 * take the failure path and the rule will not matching. 69 * 70 * Grouping 71 * 72 * Filters can have groups, which are have a meaning of logical 73 * disjunction, e.g.: 74 * 75 * A and B and (C or D) 76 * 77 * In such case, the logic inside the group has to be inverted i.e. 78 * the jump values swapped. If the test value is true, then jump 79 * out of the group; if false, then jump "next". At the end of the 80 * group, an addition failure path is appended and the JUMP_MAGIC 81 * uses within the group are patched to jump past the said path. 82 */ 83 84#include <sys/cdefs.h> 85__RCSID("$NetBSD: npf_bpf_comp.c,v 1.14 2019/08/08 21:29:15 rmind Exp $"); 86 87#include <stdlib.h> 88#include <stdbool.h> 89#include <stddef.h> 90#include <string.h> 91#include <inttypes.h> 92#include <err.h> 93#include <assert.h> 94 95#include <netinet/in.h> 96#include <netinet/in_systm.h> 97#define __FAVOR_BSD 98#include <netinet/ip.h> 99#include <netinet/ip6.h> 100#include <netinet/udp.h> 101#include <netinet/tcp.h> 102#include <netinet/ip_icmp.h> 103#include <netinet/icmp6.h> 104 105#include <net/bpf.h> 106 107#include "npfctl.h" 108 109/* 110 * Note: clear X_EQ_L4OFF when register X is invalidated i.e. it stores 111 * something other than L4 header offset. Generally, when BPF_LDX is used. 112 */ 113#define FETCHED_L3 0x01 114#define CHECKED_L4 0x02 115#define X_EQ_L4OFF 0x04 116 117struct npf_bpf { 118 /* 119 * BPF program code, the allocated length (in bytes), the number 120 * of logical blocks and the flags. 121 */ 122 struct bpf_program prog; 123 size_t alen; 124 u_int nblocks; 125 sa_family_t af; 126 uint32_t flags; 127 128 /* 129 * The current group offset (counted in BPF instructions) 130 * and block number at the start of the group. 131 */ 132 bool ingroup; 133 u_int goff; 134 u_int gblock; 135 136 /* BPF marks, allocated length and the real length. */ 137 uint32_t * marks; 138 size_t malen; 139 size_t mlen; 140}; 141 142/* 143 * NPF success and failure values to be returned from BPF. 144 */ 145#define NPF_BPF_SUCCESS ((u_int)-1) 146#define NPF_BPF_FAILURE 0 147 148/* 149 * Magic value to indicate the failure path, which is fixed up on completion. 150 * Note: this is the longest jump offset in BPF, since the offset is one byte. 151 */ 152#define JUMP_MAGIC 0xff 153 154/* Reduce re-allocations by expanding in 64 byte blocks. */ 155#define ALLOC_MASK (64 - 1) 156#define ALLOC_ROUND(x) (((x) + ALLOC_MASK) & ~ALLOC_MASK) 157 158#ifndef IPV6_VERSION 159#define IPV6_VERSION 0x60 160#endif 161 162npf_bpf_t * 163npfctl_bpf_create(void) 164{ 165 return ecalloc(1, sizeof(npf_bpf_t)); 166} 167 168static void 169fixup_jumps(npf_bpf_t *ctx, u_int start, u_int end, bool swap) 170{ 171 struct bpf_program *bp = &ctx->prog; 172 173 for (u_int i = start; i < end; i++) { 174 struct bpf_insn *insn = &bp->bf_insns[i]; 175 const u_int fail_off = end - i; 176 bool seen_magic = false; 177 178 if (fail_off >= JUMP_MAGIC) { 179 errx(EXIT_FAILURE, "BPF generation error: " 180 "the number of instructions is over the limit"); 181 } 182 if (BPF_CLASS(insn->code) != BPF_JMP) { 183 continue; 184 } 185 if (BPF_OP(insn->code) == BPF_JA) { 186 /* 187 * BPF_JA can be used to jump to the failure path. 188 * If we are swapping i.e. inside the group, then 189 * jump "next"; groups have a failure path appended 190 * at their end. 191 */ 192 if (insn->k == JUMP_MAGIC) { 193 insn->k = swap ? 0 : fail_off; 194 } 195 continue; 196 } 197 198 /* 199 * Fixup the "magic" value. Swap only the "magic" jumps. 200 */ 201 202 if (insn->jt == JUMP_MAGIC) { 203 insn->jt = fail_off; 204 seen_magic = true; 205 } 206 if (insn->jf == JUMP_MAGIC) { 207 insn->jf = fail_off; 208 seen_magic = true; 209 } 210 211 if (seen_magic && swap) { 212 uint8_t jt = insn->jt; 213 insn->jt = insn->jf; 214 insn->jf = jt; 215 } 216 } 217} 218 219static void 220add_insns(npf_bpf_t *ctx, struct bpf_insn *insns, size_t count) 221{ 222 struct bpf_program *bp = &ctx->prog; 223 size_t offset, len, reqlen; 224 225 /* Note: bf_len is the count of instructions. */ 226 offset = bp->bf_len * sizeof(struct bpf_insn); 227 len = count * sizeof(struct bpf_insn); 228 229 /* Ensure the memory buffer for the program. */ 230 reqlen = ALLOC_ROUND(offset + len); 231 if (reqlen > ctx->alen) { 232 bp->bf_insns = erealloc(bp->bf_insns, reqlen); 233 ctx->alen = reqlen; 234 } 235 236 /* Add the code block. */ 237 memcpy((uint8_t *)bp->bf_insns + offset, insns, len); 238 bp->bf_len += count; 239} 240 241static void 242done_raw_block(npf_bpf_t *ctx, const uint32_t *m, size_t len) 243{ 244 size_t reqlen, nargs = m[1]; 245 246 if ((len / sizeof(uint32_t) - 2) != nargs) { 247 errx(EXIT_FAILURE, "invalid BPF block description"); 248 } 249 reqlen = ALLOC_ROUND(ctx->mlen + len); 250 if (reqlen > ctx->malen) { 251 ctx->marks = erealloc(ctx->marks, reqlen); 252 ctx->malen = reqlen; 253 } 254 memcpy((uint8_t *)ctx->marks + ctx->mlen, m, len); 255 ctx->mlen += len; 256} 257 258static void 259done_block(npf_bpf_t *ctx, const uint32_t *m, size_t len) 260{ 261 done_raw_block(ctx, m, len); 262 ctx->nblocks++; 263} 264 265struct bpf_program * 266npfctl_bpf_complete(npf_bpf_t *ctx) 267{ 268 struct bpf_program *bp = &ctx->prog; 269 const u_int retoff = bp->bf_len; 270 271 /* No instructions (optimised out). */ 272 if (!bp->bf_len) 273 return NULL; 274 275 /* Add the return fragment (success and failure paths). */ 276 struct bpf_insn insns_ret[] = { 277 BPF_STMT(BPF_RET+BPF_K, NPF_BPF_SUCCESS), 278 BPF_STMT(BPF_RET+BPF_K, NPF_BPF_FAILURE), 279 }; 280 add_insns(ctx, insns_ret, __arraycount(insns_ret)); 281 282 /* Fixup all jumps to the main failure path. */ 283 fixup_jumps(ctx, 0, retoff, false); 284 285 return &ctx->prog; 286} 287 288const void * 289npfctl_bpf_bmarks(npf_bpf_t *ctx, size_t *len) 290{ 291 *len = ctx->mlen; 292 return ctx->marks; 293} 294 295void 296npfctl_bpf_destroy(npf_bpf_t *ctx) 297{ 298 free(ctx->prog.bf_insns); 299 free(ctx->marks); 300 free(ctx); 301} 302 303/* 304 * npfctl_bpf_group_enter: begin a logical group. It merely uses logical 305 * disjunction (OR) for compares within the group. 306 */ 307void 308npfctl_bpf_group_enter(npf_bpf_t *ctx) 309{ 310 struct bpf_program *bp = &ctx->prog; 311 312 assert(ctx->goff == 0); 313 assert(ctx->gblock == 0); 314 315 ctx->goff = bp->bf_len; 316 ctx->gblock = ctx->nblocks; 317 ctx->ingroup = true; 318} 319 320void 321npfctl_bpf_group_exit(npf_bpf_t *ctx, bool invert) 322{ 323 struct bpf_program *bp = &ctx->prog; 324 const size_t curoff = bp->bf_len; 325 326 /* If there are no blocks or only one - nothing to do. */ 327 if (!invert && (ctx->nblocks - ctx->gblock) <= 1) { 328 ctx->goff = ctx->gblock = 0; 329 return; 330 } 331 332 /* 333 * If inverting, then prepend a jump over the statement below. 334 * On match, it will skip-through and the fail path will be taken. 335 */ 336 if (invert) { 337 struct bpf_insn insns_ret[] = { 338 BPF_STMT(BPF_JMP+BPF_JA, 1), 339 }; 340 add_insns(ctx, insns_ret, __arraycount(insns_ret)); 341 } 342 343 /* 344 * Append a failure return as a fall-through i.e. if there is 345 * no match within the group. 346 */ 347 struct bpf_insn insns_ret[] = { 348 BPF_STMT(BPF_RET+BPF_K, NPF_BPF_FAILURE), 349 }; 350 add_insns(ctx, insns_ret, __arraycount(insns_ret)); 351 352 /* 353 * Adjust jump offsets: on match - jump outside the group i.e. 354 * to the current offset. Otherwise, jump to the next instruction 355 * which would lead to the fall-through code above if none matches. 356 */ 357 fixup_jumps(ctx, ctx->goff, curoff, true); 358 ctx->goff = ctx->gblock = 0; 359} 360 361static void 362fetch_l3(npf_bpf_t *ctx, sa_family_t af, u_int flags) 363{ 364 u_int ver; 365 366 switch (af) { 367 case AF_INET: 368 ver = IPVERSION; 369 break; 370 case AF_INET6: 371 ver = IPV6_VERSION >> 4; 372 break; 373 case AF_UNSPEC: 374 ver = 0; 375 break; 376 default: 377 abort(); 378 } 379 380 /* 381 * The memory store is populated with: 382 * - BPF_MW_IPVER: IP version (4 or 6). 383 * - BPF_MW_L4OFF: L4 header offset. 384 * - BPF_MW_L4PROTO: L4 protocol. 385 */ 386 if ((ctx->flags & FETCHED_L3) == 0 || (af && ctx->af == 0)) { 387 const uint8_t jt = ver ? 0 : JUMP_MAGIC; 388 const uint8_t jf = ver ? JUMP_MAGIC : 0; 389 bool ingroup = ctx->ingroup; 390 391 /* 392 * L3 block cannot be inserted in the middle of a group. 393 * In fact, it never is. Check and start the group after. 394 */ 395 if (ingroup) { 396 assert(ctx->nblocks == ctx->gblock); 397 npfctl_bpf_group_exit(ctx, false); 398 } 399 400 /* 401 * A <- IP version; A == expected-version? 402 * If no particular version specified, check for non-zero. 403 */ 404 struct bpf_insn insns_af[] = { 405 BPF_STMT(BPF_LD+BPF_W+BPF_MEM, BPF_MW_IPVER), 406 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ver, jt, jf), 407 }; 408 add_insns(ctx, insns_af, __arraycount(insns_af)); 409 ctx->flags |= FETCHED_L3; 410 ctx->af = af; 411 412 if (af) { 413 uint32_t mwords[] = { BM_IPVER, 1, af }; 414 done_raw_block(ctx, mwords, sizeof(mwords)); 415 } 416 if (ingroup) { 417 npfctl_bpf_group_enter(ctx); 418 } 419 420 } else if (af && af != ctx->af) { 421 errx(EXIT_FAILURE, "address family mismatch"); 422 } 423 424 if ((flags & X_EQ_L4OFF) != 0 && (ctx->flags & X_EQ_L4OFF) == 0) { 425 /* X <- IP header length */ 426 struct bpf_insn insns_hlen[] = { 427 BPF_STMT(BPF_LDX+BPF_MEM, BPF_MW_L4OFF), 428 }; 429 add_insns(ctx, insns_hlen, __arraycount(insns_hlen)); 430 ctx->flags |= X_EQ_L4OFF; 431 } 432} 433 434/* 435 * npfctl_bpf_proto: code block to match IP version and L4 protocol. 436 */ 437void 438npfctl_bpf_proto(npf_bpf_t *ctx, sa_family_t af, int proto) 439{ 440 assert(af != AF_UNSPEC || proto != -1); 441 442 /* Note: fails if IP version does not match. */ 443 fetch_l3(ctx, af, 0); 444 if (proto == -1) { 445 return; 446 } 447 448 struct bpf_insn insns_proto[] = { 449 /* A <- L4 protocol; A == expected-protocol? */ 450 BPF_STMT(BPF_LD+BPF_W+BPF_MEM, BPF_MW_L4PROTO), 451 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, proto, 0, JUMP_MAGIC), 452 }; 453 add_insns(ctx, insns_proto, __arraycount(insns_proto)); 454 455 uint32_t mwords[] = { BM_PROTO, 1, proto }; 456 done_block(ctx, mwords, sizeof(mwords)); 457 ctx->flags |= CHECKED_L4; 458} 459 460/* 461 * npfctl_bpf_cidr: code block to match IPv4 or IPv6 CIDR. 462 * 463 * => IP address shall be in the network byte order. 464 */ 465void 466npfctl_bpf_cidr(npf_bpf_t *ctx, u_int opts, sa_family_t af, 467 const npf_addr_t *addr, const npf_netmask_t mask) 468{ 469 const uint32_t *awords = (const uint32_t *)addr; 470 u_int nwords, length, maxmask, off; 471 472 assert(((opts & MATCH_SRC) != 0) ^ ((opts & MATCH_DST) != 0)); 473 assert((mask && mask <= NPF_MAX_NETMASK) || mask == NPF_NO_NETMASK); 474 475 switch (af) { 476 case AF_INET: 477 maxmask = 32; 478 off = (opts & MATCH_SRC) ? 479 offsetof(struct ip, ip_src) : 480 offsetof(struct ip, ip_dst); 481 nwords = sizeof(struct in_addr) / sizeof(uint32_t); 482 break; 483 case AF_INET6: 484 maxmask = 128; 485 off = (opts & MATCH_SRC) ? 486 offsetof(struct ip6_hdr, ip6_src) : 487 offsetof(struct ip6_hdr, ip6_dst); 488 nwords = sizeof(struct in6_addr) / sizeof(uint32_t); 489 break; 490 default: 491 abort(); 492 } 493 494 /* Ensure address family. */ 495 fetch_l3(ctx, af, 0); 496 497 length = (mask == NPF_NO_NETMASK) ? maxmask : mask; 498 499 /* CAUTION: BPF operates in host byte-order. */ 500 for (u_int i = 0; i < nwords; i++) { 501 const u_int woff = i * sizeof(uint32_t); 502 uint32_t word = ntohl(awords[i]); 503 uint32_t wordmask; 504 505 if (length >= 32) { 506 /* The mask is a full word - do not apply it. */ 507 wordmask = 0; 508 length -= 32; 509 } else if (length) { 510 wordmask = 0xffffffff << (32 - length); 511 length = 0; 512 } else { 513 /* The mask became zero - skip the rest. */ 514 break; 515 } 516 517 /* A <- IP address (or one word of it) */ 518 struct bpf_insn insns_ip[] = { 519 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, off + woff), 520 }; 521 add_insns(ctx, insns_ip, __arraycount(insns_ip)); 522 523 /* A <- (A & MASK) */ 524 if (wordmask) { 525 struct bpf_insn insns_mask[] = { 526 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, wordmask), 527 }; 528 add_insns(ctx, insns_mask, __arraycount(insns_mask)); 529 } 530 531 /* A == expected-IP-word ? */ 532 struct bpf_insn insns_cmp[] = { 533 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, word, 0, JUMP_MAGIC), 534 }; 535 add_insns(ctx, insns_cmp, __arraycount(insns_cmp)); 536 } 537 538 uint32_t mwords[] = { 539 (opts & MATCH_SRC) ? BM_SRC_CIDR: BM_DST_CIDR, 6, 540 af, mask, awords[0], awords[1], awords[2], awords[3], 541 }; 542 done_block(ctx, mwords, sizeof(mwords)); 543} 544 545/* 546 * npfctl_bpf_ports: code block to match TCP/UDP port range. 547 * 548 * => Port numbers shall be in the network byte order. 549 */ 550void 551npfctl_bpf_ports(npf_bpf_t *ctx, u_int opts, in_port_t from, in_port_t to) 552{ 553 const u_int sport_off = offsetof(struct udphdr, uh_sport); 554 const u_int dport_off = offsetof(struct udphdr, uh_dport); 555 u_int off; 556 557 /* TCP and UDP port offsets are the same. */ 558 assert(sport_off == offsetof(struct tcphdr, th_sport)); 559 assert(dport_off == offsetof(struct tcphdr, th_dport)); 560 assert(ctx->flags & CHECKED_L4); 561 562 assert(((opts & MATCH_SRC) != 0) ^ ((opts & MATCH_DST) != 0)); 563 off = (opts & MATCH_SRC) ? sport_off : dport_off; 564 565 /* X <- IP header length */ 566 fetch_l3(ctx, AF_UNSPEC, X_EQ_L4OFF); 567 568 struct bpf_insn insns_fetch[] = { 569 /* A <- port */ 570 BPF_STMT(BPF_LD+BPF_H+BPF_IND, off), 571 }; 572 add_insns(ctx, insns_fetch, __arraycount(insns_fetch)); 573 574 /* CAUTION: BPF operates in host byte-order. */ 575 from = ntohs(from); 576 to = ntohs(to); 577 578 if (from == to) { 579 /* Single port case. */ 580 struct bpf_insn insns_port[] = { 581 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, from, 0, JUMP_MAGIC), 582 }; 583 add_insns(ctx, insns_port, __arraycount(insns_port)); 584 } else { 585 /* Port range case. */ 586 struct bpf_insn insns_range[] = { 587 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, from, 0, 1), 588 BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, to, 0, 1), 589 BPF_STMT(BPF_JMP+BPF_JA, JUMP_MAGIC), 590 }; 591 add_insns(ctx, insns_range, __arraycount(insns_range)); 592 } 593 594 uint32_t mwords[] = { 595 opts & MATCH_SRC ? BM_SRC_PORTS : BM_DST_PORTS, 2, from, to 596 }; 597 done_block(ctx, mwords, sizeof(mwords)); 598} 599 600/* 601 * npfctl_bpf_tcpfl: code block to match TCP flags. 602 */ 603void 604npfctl_bpf_tcpfl(npf_bpf_t *ctx, uint8_t tf, uint8_t tf_mask, bool checktcp) 605{ 606 const u_int tcpfl_off = offsetof(struct tcphdr, th_flags); 607 const bool usingmask = tf_mask != tf; 608 609 /* X <- IP header length */ 610 fetch_l3(ctx, AF_UNSPEC, X_EQ_L4OFF); 611 if (checktcp) { 612 const u_int jf = usingmask ? 3 : 2; 613 assert(ctx->ingroup == false); 614 615 /* A <- L4 protocol; A == TCP? If not, jump out. */ 616 struct bpf_insn insns_tcp[] = { 617 BPF_STMT(BPF_LD+BPF_W+BPF_MEM, BPF_MW_L4PROTO), 618 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_TCP, 0, jf), 619 }; 620 add_insns(ctx, insns_tcp, __arraycount(insns_tcp)); 621 } else { 622 assert(ctx->flags & CHECKED_L4); 623 } 624 625 struct bpf_insn insns_tf[] = { 626 /* A <- TCP flags */ 627 BPF_STMT(BPF_LD+BPF_B+BPF_IND, tcpfl_off), 628 }; 629 add_insns(ctx, insns_tf, __arraycount(insns_tf)); 630 631 if (usingmask) { 632 /* A <- (A & mask) */ 633 struct bpf_insn insns_mask[] = { 634 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, tf_mask), 635 }; 636 add_insns(ctx, insns_mask, __arraycount(insns_mask)); 637 } 638 639 struct bpf_insn insns_cmp[] = { 640 /* A == expected-TCP-flags? */ 641 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, tf, 0, JUMP_MAGIC), 642 }; 643 add_insns(ctx, insns_cmp, __arraycount(insns_cmp)); 644 645 uint32_t mwords[] = { BM_TCPFL, 2, tf, tf_mask}; 646 done_block(ctx, mwords, sizeof(mwords)); 647} 648 649/* 650 * npfctl_bpf_icmp: code block to match ICMP type and/or code. 651 * Note: suitable both for the ICMPv4 and ICMPv6. 652 */ 653void 654npfctl_bpf_icmp(npf_bpf_t *ctx, int type, int code) 655{ 656 const u_int type_off = offsetof(struct icmp, icmp_type); 657 const u_int code_off = offsetof(struct icmp, icmp_code); 658 659 assert(ctx->flags & CHECKED_L4); 660 assert(offsetof(struct icmp6_hdr, icmp6_type) == type_off); 661 assert(offsetof(struct icmp6_hdr, icmp6_code) == code_off); 662 assert(type != -1 || code != -1); 663 664 /* X <- IP header length */ 665 fetch_l3(ctx, AF_UNSPEC, X_EQ_L4OFF); 666 667 if (type != -1) { 668 struct bpf_insn insns_type[] = { 669 BPF_STMT(BPF_LD+BPF_B+BPF_IND, type_off), 670 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, type, 0, JUMP_MAGIC), 671 }; 672 add_insns(ctx, insns_type, __arraycount(insns_type)); 673 674 uint32_t mwords[] = { BM_ICMP_TYPE, 1, type }; 675 done_block(ctx, mwords, sizeof(mwords)); 676 } 677 678 if (code != -1) { 679 struct bpf_insn insns_code[] = { 680 BPF_STMT(BPF_LD+BPF_B+BPF_IND, code_off), 681 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, code, 0, JUMP_MAGIC), 682 }; 683 add_insns(ctx, insns_code, __arraycount(insns_code)); 684 685 uint32_t mwords[] = { BM_ICMP_CODE, 1, code }; 686 done_block(ctx, mwords, sizeof(mwords)); 687 } 688} 689 690#define SRC_FLAG_BIT (1U << 31) 691 692/* 693 * npfctl_bpf_table: code block to match source/destination IP address 694 * against NPF table specified by ID. 695 */ 696void 697npfctl_bpf_table(npf_bpf_t *ctx, u_int opts, u_int tid) 698{ 699 const bool src = (opts & MATCH_SRC) != 0; 700 701 struct bpf_insn insns_table[] = { 702 BPF_STMT(BPF_LD+BPF_IMM, (src ? SRC_FLAG_BIT : 0) | tid), 703 BPF_STMT(BPF_MISC+BPF_COP, NPF_COP_TABLE), 704 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, JUMP_MAGIC, 0), 705 }; 706 add_insns(ctx, insns_table, __arraycount(insns_table)); 707 708 uint32_t mwords[] = { src ? BM_SRC_TABLE: BM_DST_TABLE, 1, tid }; 709 done_block(ctx, mwords, sizeof(mwords)); 710} 711