1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2004 Andre Oppermann, Internet Business Solutions AG 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include "opt_ipfw.h" 33 #include "opt_inet.h" 34 #include "opt_inet6.h" 35 #ifndef INET 36 #error IPFIREWALL requires INET. 37 #endif /* INET */ 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/malloc.h> 42 #include <sys/mbuf.h> 43 #include <sys/module.h> 44 #include <sys/kernel.h> 45 #include <sys/lock.h> 46 #include <sys/rwlock.h> 47 #include <sys/socket.h> 48 #include <sys/sysctl.h> 49 50 #include <net/if.h> 51 #include <net/route.h> 52 #include <net/ethernet.h> 53 #include <net/pfil.h> 54 #include <net/vnet.h> 55 56 #include <netinet/in.h> 57 #include <netinet/in_systm.h> 58 #include <netinet/ip.h> 59 #include <netinet/ip_var.h> 60 #include <netinet/ip_fw.h> 61 #ifdef INET6 62 #include <netinet/ip6.h> 63 #include <netinet6/ip6_var.h> 64 #include <netinet6/scope6_var.h> 65 #endif 66 67 #include <netgraph/ng_ipfw.h> 68 69 #include <netpfil/ipfw/ip_fw_private.h> 70 71 #include <machine/in_cksum.h> 72 73 VNET_DEFINE_STATIC(int, fw_enable) = 1; 74 #define V_fw_enable VNET(fw_enable) 75 76 #ifdef INET6 77 VNET_DEFINE_STATIC(int, fw6_enable) = 1; 78 #define V_fw6_enable VNET(fw6_enable) 79 #endif 80 81 VNET_DEFINE_STATIC(int, fwlink_enable) = 0; 82 #define V_fwlink_enable VNET(fwlink_enable) 83 84 int ipfw_chg_hook(SYSCTL_HANDLER_ARGS); 85 86 /* Forward declarations. */ 87 static int ipfw_divert(struct mbuf **, int, struct ipfw_rule_ref *, int); 88 int ipfw_check_packet(void *, struct mbuf **, struct ifnet *, int, 89 struct inpcb *); 90 int ipfw_check_frame(void *, struct mbuf **, struct ifnet *, int, 91 struct inpcb *); 92 93 #ifdef SYSCTL_NODE 94 95 SYSBEGIN(f1) 96 97 SYSCTL_DECL(_net_inet_ip_fw); 98 SYSCTL_PROC(_net_inet_ip_fw, OID_AUTO, enable, 99 CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, 100 &VNET_NAME(fw_enable), 0, ipfw_chg_hook, "I", "Enable ipfw"); 101 #ifdef INET6 102 SYSCTL_DECL(_net_inet6_ip6_fw); 103 SYSCTL_PROC(_net_inet6_ip6_fw, OID_AUTO, enable, 104 CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, 105 &VNET_NAME(fw6_enable), 0, ipfw_chg_hook, "I", "Enable ipfw+6"); 106 #endif /* INET6 */ 107 108 SYSCTL_DECL(_net_link_ether); 109 SYSCTL_PROC(_net_link_ether, OID_AUTO, ipfw, 110 CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, 111 &VNET_NAME(fwlink_enable), 0, ipfw_chg_hook, "I", 112 "Pass ether pkts through firewall"); 113 114 SYSEND 115 116 #endif /* SYSCTL_NODE */ 117 118 /* 119 * The pfilter hook to pass packets to ipfw_chk and then to 120 * dummynet, divert, netgraph or other modules. 121 * The packet may be consumed. 122 */ 123 int 124 ipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, 125 struct inpcb *inp) 126 { 127 struct ip_fw_args args; 128 struct m_tag *tag; 129 int ipfw; 130 int ret; 131 132 /* convert dir to IPFW values */ 133 dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT; 134 bzero(&args, sizeof(args)); 135 136 again: 137 /* 138 * extract and remove the tag if present. If we are left 139 * with onepass, optimize the outgoing path. 140 */ 141 tag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); 142 if (tag != NULL) { 143 args.rule = *((struct ipfw_rule_ref *)(tag+1)); 144 m_tag_delete(*m0, tag); 145 if (args.rule.info & IPFW_ONEPASS) 146 return (0); 147 } 148 149 args.m = *m0; 150 args.oif = dir == DIR_OUT ? ifp : NULL; 151 args.inp = inp; 152 153 ipfw = ipfw_chk(&args); 154 *m0 = args.m; 155 156 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", 157 __func__)); 158 159 /* breaking out of the switch means drop */ 160 ret = 0; /* default return value for pass */ 161 switch (ipfw) { 162 case IP_FW_PASS: 163 /* next_hop may be set by ipfw_chk */ 164 if (args.next_hop == NULL && args.next_hop6 == NULL) 165 break; /* pass */ 166 #if (!defined(INET6) && !defined(INET)) 167 ret = EACCES; 168 #else 169 { 170 struct m_tag *fwd_tag; 171 size_t len; 172 173 KASSERT(args.next_hop == NULL || args.next_hop6 == NULL, 174 ("%s: both next_hop=%p and next_hop6=%p not NULL", __func__, 175 args.next_hop, args.next_hop6)); 176 #ifdef INET6 177 if (args.next_hop6 != NULL) 178 len = sizeof(struct sockaddr_in6); 179 #endif 180 #ifdef INET 181 if (args.next_hop != NULL) 182 len = sizeof(struct sockaddr_in); 183 #endif 184 185 /* Incoming packets should not be tagged so we do not 186 * m_tag_find. Outgoing packets may be tagged, so we 187 * reuse the tag if present. 188 */ 189 fwd_tag = (dir == DIR_IN) ? NULL : 190 m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); 191 if (fwd_tag != NULL) { 192 m_tag_unlink(*m0, fwd_tag); 193 } else { 194 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, len, 195 M_NOWAIT); 196 if (fwd_tag == NULL) { 197 ret = EACCES; 198 break; /* i.e. drop */ 199 } 200 } 201 #ifdef INET6 202 if (args.next_hop6 != NULL) { 203 struct sockaddr_in6 *sa6; 204 205 sa6 = (struct sockaddr_in6 *)(fwd_tag + 1); 206 bcopy(args.next_hop6, sa6, len); 207 /* 208 * If nh6 address is link-local we should convert 209 * it to kernel internal form before doing any 210 * comparisons. 211 */ 212 if (sa6_embedscope(sa6, V_ip6_use_defzone) != 0) { 213 ret = EACCES; 214 break; 215 } 216 if (in6_localip(&sa6->sin6_addr)) 217 (*m0)->m_flags |= M_FASTFWD_OURS; 218 (*m0)->m_flags |= M_IP6_NEXTHOP; 219 } 220 #endif 221 #ifdef INET 222 if (args.next_hop != NULL) { 223 bcopy(args.next_hop, (fwd_tag+1), len); 224 if (in_localip(args.next_hop->sin_addr)) 225 (*m0)->m_flags |= M_FASTFWD_OURS; 226 (*m0)->m_flags |= M_IP_NEXTHOP; 227 } 228 #endif 229 m_tag_prepend(*m0, fwd_tag); 230 } 231 #endif /* INET || INET6 */ 232 break; 233 234 case IP_FW_DENY: 235 ret = EACCES; 236 break; /* i.e. drop */ 237 238 case IP_FW_DUMMYNET: 239 ret = EACCES; 240 if (ip_dn_io_ptr == NULL) 241 break; /* i.e. drop */ 242 if (mtod(*m0, struct ip *)->ip_v == 4) 243 ret = ip_dn_io_ptr(m0, dir, &args); 244 else if (mtod(*m0, struct ip *)->ip_v == 6) 245 ret = ip_dn_io_ptr(m0, dir | PROTO_IPV6, &args); 246 else 247 break; /* drop it */ 248 /* 249 * XXX should read the return value. 250 * dummynet normally eats the packet and sets *m0=NULL 251 * unless the packet can be sent immediately. In this 252 * case args is updated and we should re-run the 253 * check without clearing args. 254 */ 255 if (*m0 != NULL) 256 goto again; 257 break; 258 259 case IP_FW_TEE: 260 case IP_FW_DIVERT: 261 if (ip_divert_ptr == NULL) { 262 ret = EACCES; 263 break; /* i.e. drop */ 264 } 265 ret = ipfw_divert(m0, dir, &args.rule, 266 (ipfw == IP_FW_TEE) ? 1 : 0); 267 /* continue processing for the original packet (tee). */ 268 if (*m0) 269 goto again; 270 break; 271 272 case IP_FW_NGTEE: 273 case IP_FW_NETGRAPH: 274 if (ng_ipfw_input_p == NULL) { 275 ret = EACCES; 276 break; /* i.e. drop */ 277 } 278 ret = ng_ipfw_input_p(m0, dir, &args, 279 (ipfw == IP_FW_NGTEE) ? 1 : 0); 280 if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */ 281 goto again; /* continue with packet */ 282 break; 283 284 case IP_FW_NAT: 285 /* honor one-pass in case of successful nat */ 286 if (V_fw_one_pass) 287 break; /* ret is already 0 */ 288 goto again; 289 290 case IP_FW_REASS: 291 goto again; /* continue with packet */ 292 293 default: 294 KASSERT(0, ("%s: unknown retval", __func__)); 295 } 296 297 if (ret != 0) { 298 if (*m0) 299 FREE_PKT(*m0); 300 *m0 = NULL; 301 } 302 303 return ret; 304 } 305 306 /* 307 * ipfw processing for ethernet packets (in and out). 308 */ 309 int 310 ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, 311 struct inpcb *inp) 312 { 313 struct ether_header *eh; 314 struct ether_header save_eh; 315 struct mbuf *m; 316 int i, ret; 317 struct ip_fw_args args; 318 struct m_tag *mtag; 319 320 bzero(&args, sizeof(args)); 321 322 again: 323 /* fetch start point from rule, if any. remove the tag if present. */ 324 mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); 325 if (mtag != NULL) { 326 args.rule = *((struct ipfw_rule_ref *)(mtag+1)); 327 m_tag_delete(*m0, mtag); 328 if (args.rule.info & IPFW_ONEPASS) 329 return (0); 330 } 331 332 /* I need some amt of data to be contiguous */ 333 m = *m0; 334 i = min(m->m_pkthdr.len, max_protohdr); 335 if (m->m_len < i) { 336 m = m_pullup(m, i); 337 if (m == NULL) { 338 *m0 = m; 339 return (0); 340 } 341 } 342 eh = mtod(m, struct ether_header *); 343 save_eh = *eh; /* save copy for restore below */ 344 m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ 345 346 args.m = m; /* the packet we are looking at */ 347 args.oif = dir == PFIL_OUT ? ifp: NULL; /* destination, if any */ 348 args.next_hop = NULL; /* we do not support forward yet */ 349 args.next_hop6 = NULL; /* we do not support forward yet */ 350 args.eh = &save_eh; /* MAC header for bridged/MAC packets */ 351 args.inp = NULL; /* used by ipfw uid/gid/jail rules */ 352 i = ipfw_chk(&args); 353 m = args.m; 354 if (m != NULL) { 355 /* 356 * Restore Ethernet header, as needed, in case the 357 * mbuf chain was replaced by ipfw. 358 */ 359 M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); 360 if (m == NULL) { 361 *m0 = NULL; 362 return (0); 363 } 364 if (eh != mtod(m, struct ether_header *)) 365 bcopy(&save_eh, mtod(m, struct ether_header *), 366 ETHER_HDR_LEN); 367 } 368 *m0 = m; 369 370 ret = 0; 371 /* Check result of ipfw_chk() */ 372 switch (i) { 373 case IP_FW_PASS: 374 break; 375 376 case IP_FW_DENY: 377 ret = EACCES; 378 break; /* i.e. drop */ 379 380 case IP_FW_DUMMYNET: 381 ret = EACCES; 382 int dir2; 383 384 if (ip_dn_io_ptr == NULL) 385 break; /* i.e. drop */ 386 387 *m0 = NULL; 388 dir2 = (dir == PFIL_IN) ? DIR_IN : DIR_OUT; 389 ip_dn_io_ptr(&m, dir2 | PROTO_LAYER2, &args); 390 return 0; 391 392 case IP_FW_NGTEE: 393 case IP_FW_NETGRAPH: 394 if (ng_ipfw_input_p == NULL) { 395 ret = EACCES; 396 break; /* i.e. drop */ 397 } 398 ret = ng_ipfw_input_p(m0, (dir == PFIL_IN) ? DIR_IN : DIR_OUT, 399 &args, (i == IP_FW_NGTEE) ? 1 : 0); 400 if (i == IP_FW_NGTEE) /* ignore errors for NGTEE */ 401 goto again; /* continue with packet */ 402 break; 403 404 default: 405 KASSERT(0, ("%s: unknown retval", __func__)); 406 } 407 408 if (ret != 0) { 409 if (*m0) 410 FREE_PKT(*m0); 411 *m0 = NULL; 412 } 413 414 return ret; 415 } 416 417 /* do the divert, return 1 on error 0 on success */ 418 static int 419 ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule, 420 int tee) 421 { 422 /* 423 * ipfw_chk() has already tagged the packet with the divert tag. 424 * If tee is set, copy packet and return original. 425 * If not tee, consume packet and send it to divert socket. 426 */ 427 struct mbuf *clone; 428 struct ip *ip = mtod(*m0, struct ip *); 429 struct m_tag *tag; 430 431 /* Cloning needed for tee? */ 432 if (tee == 0) { 433 clone = *m0; /* use the original mbuf */ 434 *m0 = NULL; 435 } else { 436 clone = m_dup(*m0, M_NOWAIT); 437 /* If we cannot duplicate the mbuf, we sacrifice the divert 438 * chain and continue with the tee-ed packet. 439 */ 440 if (clone == NULL) 441 return 1; 442 } 443 444 /* 445 * Divert listeners can normally handle non-fragmented packets, 446 * but we can only reass in the non-tee case. 447 * This means that listeners on a tee rule may get fragments, 448 * and have to live with that. 449 * Note that we now have the 'reass' ipfw option so if we care 450 * we can do it before a 'tee'. 451 */ 452 if (!tee) switch (ip->ip_v) { 453 case IPVERSION: 454 if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) { 455 int hlen; 456 struct mbuf *reass; 457 458 reass = ip_reass(clone); /* Reassemble packet. */ 459 if (reass == NULL) 460 return 0; /* not an error */ 461 /* if reass = NULL then it was consumed by ip_reass */ 462 /* 463 * IP header checksum fixup after reassembly and leave header 464 * in network byte order. 465 */ 466 ip = mtod(reass, struct ip *); 467 hlen = ip->ip_hl << 2; 468 ip->ip_sum = 0; 469 if (hlen == sizeof(struct ip)) 470 ip->ip_sum = in_cksum_hdr(ip); 471 else 472 ip->ip_sum = in_cksum(reass, hlen); 473 clone = reass; 474 } 475 break; 476 #ifdef INET6 477 case IPV6_VERSION >> 4: 478 { 479 struct ip6_hdr *const ip6 = mtod(clone, struct ip6_hdr *); 480 481 if (ip6->ip6_nxt == IPPROTO_FRAGMENT) { 482 int nxt, off; 483 484 off = sizeof(struct ip6_hdr); 485 nxt = frag6_input(&clone, &off, 0); 486 if (nxt == IPPROTO_DONE) 487 return (0); 488 } 489 break; 490 } 491 #endif 492 } 493 494 /* attach a tag to the packet with the reinject info */ 495 tag = m_tag_alloc(MTAG_IPFW_RULE, 0, 496 sizeof(struct ipfw_rule_ref), M_NOWAIT); 497 if (tag == NULL) { 498 FREE_PKT(clone); 499 return 1; 500 } 501 *((struct ipfw_rule_ref *)(tag+1)) = *rule; 502 m_tag_prepend(clone, tag); 503 504 /* Do the dirty job... */ 505 ip_divert_ptr(clone, incoming); 506 return 0; 507 } 508 509 /* 510 * attach or detach hooks for a given protocol family 511 */ 512 static int 513 ipfw_hook(int onoff, int pf) 514 { 515 struct pfil_head *pfh; 516 pfil_func_t hook_func; 517 518 pfh = pfil_head_get(PFIL_TYPE_AF, pf); 519 if (pfh == NULL) 520 return ENOENT; 521 522 hook_func = (pf == AF_LINK) ? ipfw_check_frame : ipfw_check_packet; 523 524 (void) (onoff ? pfil_add_hook : pfil_remove_hook) 525 (hook_func, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh); 526 527 return 0; 528 } 529 530 int 531 ipfw_attach_hooks(int arg) 532 { 533 int error = 0; 534 535 if (arg == 0) /* detach */ 536 ipfw_hook(0, AF_INET); 537 else if (V_fw_enable && ipfw_hook(1, AF_INET) != 0) { 538 error = ENOENT; /* see ip_fw_pfil.c::ipfw_hook() */ 539 printf("ipfw_hook() error\n"); 540 } 541 #ifdef INET6 542 if (arg == 0) /* detach */ 543 ipfw_hook(0, AF_INET6); 544 else if (V_fw6_enable && ipfw_hook(1, AF_INET6) != 0) { 545 error = ENOENT; 546 printf("ipfw6_hook() error\n"); 547 } 548 #endif 549 if (arg == 0) /* detach */ 550 ipfw_hook(0, AF_LINK); 551 else if (V_fwlink_enable && ipfw_hook(1, AF_LINK) != 0) { 552 error = ENOENT; 553 printf("ipfw_link_hook() error\n"); 554 } 555 return error; 556 } 557 558 int 559 ipfw_chg_hook(SYSCTL_HANDLER_ARGS) 560 { 561 int newval; 562 int error; 563 int af; 564 565 if (arg1 == &V_fw_enable) 566 af = AF_INET; 567 #ifdef INET6 568 else if (arg1 == &V_fw6_enable) 569 af = AF_INET6; 570 #endif 571 else if (arg1 == &V_fwlink_enable) 572 af = AF_LINK; 573 else 574 return (EINVAL); 575 576 newval = *(int *)arg1; 577 /* Handle sysctl change */ 578 error = sysctl_handle_int(oidp, &newval, 0, req); 579 580 if (error) 581 return (error); 582 583 /* Formalize new value */ 584 newval = (newval) ? 1 : 0; 585 586 if (*(int *)arg1 == newval) 587 return (0); 588 589 error = ipfw_hook(newval, af); 590 if (error) 591 return (error); 592 *(int *)arg1 = newval; 593 594 return (0); 595 } 596 /* end of file */ 597