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 /* fetch start point from rule, if any. remove the tag if present. */ 321 mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); 322 if (mtag == NULL) { 323 args.rule.slot = 0; 324 } else { 325 args.rule = *((struct ipfw_rule_ref *)(mtag+1)); 326 m_tag_delete(*m0, mtag); 327 if (args.rule.info & IPFW_ONEPASS) 328 return (0); 329 } 330 331 /* I need some amt of data to be contiguous */ 332 m = *m0; 333 i = min(m->m_pkthdr.len, max_protohdr); 334 if (m->m_len < i) { 335 m = m_pullup(m, i); 336 if (m == NULL) { 337 *m0 = m; 338 return (0); 339 } 340 } 341 eh = mtod(m, struct ether_header *); 342 save_eh = *eh; /* save copy for restore below */ 343 m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ 344 345 args.m = m; /* the packet we are looking at */ 346 args.oif = dir == PFIL_OUT ? ifp: NULL; /* destination, if any */ 347 args.next_hop = NULL; /* we do not support forward yet */ 348 args.next_hop6 = NULL; /* we do not support forward yet */ 349 args.eh = &save_eh; /* MAC header for bridged/MAC packets */ 350 args.inp = NULL; /* used by ipfw uid/gid/jail rules */ 351 i = ipfw_chk(&args); 352 m = args.m; 353 if (m != NULL) { 354 /* 355 * Restore Ethernet header, as needed, in case the 356 * mbuf chain was replaced by ipfw. 357 */ 358 M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); 359 if (m == NULL) { 360 *m0 = NULL; 361 return (0); 362 } 363 if (eh != mtod(m, struct ether_header *)) 364 bcopy(&save_eh, mtod(m, struct ether_header *), 365 ETHER_HDR_LEN); 366 } 367 *m0 = m; 368 369 ret = 0; 370 /* Check result of ipfw_chk() */ 371 switch (i) { 372 case IP_FW_PASS: 373 break; 374 375 case IP_FW_DENY: 376 ret = EACCES; 377 break; /* i.e. drop */ 378 379 case IP_FW_DUMMYNET: 380 ret = EACCES; 381 382 if (ip_dn_io_ptr == NULL) 383 break; /* i.e. drop */ 384 385 *m0 = NULL; 386 dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT; 387 ip_dn_io_ptr(&m, dir | PROTO_LAYER2, &args); 388 return 0; 389 390 default: 391 KASSERT(0, ("%s: unknown retval", __func__)); 392 } 393 394 if (ret != 0) { 395 if (*m0) 396 FREE_PKT(*m0); 397 *m0 = NULL; 398 } 399 400 return ret; 401 } 402 403 /* do the divert, return 1 on error 0 on success */ 404 static int 405 ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule, 406 int tee) 407 { 408 /* 409 * ipfw_chk() has already tagged the packet with the divert tag. 410 * If tee is set, copy packet and return original. 411 * If not tee, consume packet and send it to divert socket. 412 */ 413 struct mbuf *clone; 414 struct ip *ip = mtod(*m0, struct ip *); 415 struct m_tag *tag; 416 417 /* Cloning needed for tee? */ 418 if (tee == 0) { 419 clone = *m0; /* use the original mbuf */ 420 *m0 = NULL; 421 } else { 422 clone = m_dup(*m0, M_NOWAIT); 423 /* If we cannot duplicate the mbuf, we sacrifice the divert 424 * chain and continue with the tee-ed packet. 425 */ 426 if (clone == NULL) 427 return 1; 428 } 429 430 /* 431 * Divert listeners can normally handle non-fragmented packets, 432 * but we can only reass in the non-tee case. 433 * This means that listeners on a tee rule may get fragments, 434 * and have to live with that. 435 * Note that we now have the 'reass' ipfw option so if we care 436 * we can do it before a 'tee'. 437 */ 438 if (!tee) switch (ip->ip_v) { 439 case IPVERSION: 440 if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) { 441 int hlen; 442 struct mbuf *reass; 443 444 reass = ip_reass(clone); /* Reassemble packet. */ 445 if (reass == NULL) 446 return 0; /* not an error */ 447 /* if reass = NULL then it was consumed by ip_reass */ 448 /* 449 * IP header checksum fixup after reassembly and leave header 450 * in network byte order. 451 */ 452 ip = mtod(reass, struct ip *); 453 hlen = ip->ip_hl << 2; 454 ip->ip_sum = 0; 455 if (hlen == sizeof(struct ip)) 456 ip->ip_sum = in_cksum_hdr(ip); 457 else 458 ip->ip_sum = in_cksum(reass, hlen); 459 clone = reass; 460 } 461 break; 462 #ifdef INET6 463 case IPV6_VERSION >> 4: 464 { 465 struct ip6_hdr *const ip6 = mtod(clone, struct ip6_hdr *); 466 467 if (ip6->ip6_nxt == IPPROTO_FRAGMENT) { 468 int nxt, off; 469 470 off = sizeof(struct ip6_hdr); 471 nxt = frag6_input(&clone, &off, 0); 472 if (nxt == IPPROTO_DONE) 473 return (0); 474 } 475 break; 476 } 477 #endif 478 } 479 480 /* attach a tag to the packet with the reinject info */ 481 tag = m_tag_alloc(MTAG_IPFW_RULE, 0, 482 sizeof(struct ipfw_rule_ref), M_NOWAIT); 483 if (tag == NULL) { 484 FREE_PKT(clone); 485 return 1; 486 } 487 *((struct ipfw_rule_ref *)(tag+1)) = *rule; 488 m_tag_prepend(clone, tag); 489 490 /* Do the dirty job... */ 491 ip_divert_ptr(clone, incoming); 492 return 0; 493 } 494 495 /* 496 * attach or detach hooks for a given protocol family 497 */ 498 static int 499 ipfw_hook(int onoff, int pf) 500 { 501 struct pfil_head *pfh; 502 pfil_func_t hook_func; 503 504 pfh = pfil_head_get(PFIL_TYPE_AF, pf); 505 if (pfh == NULL) 506 return ENOENT; 507 508 hook_func = (pf == AF_LINK) ? ipfw_check_frame : ipfw_check_packet; 509 510 (void) (onoff ? pfil_add_hook : pfil_remove_hook) 511 (hook_func, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh); 512 513 return 0; 514 } 515 516 int 517 ipfw_attach_hooks(int arg) 518 { 519 int error = 0; 520 521 if (arg == 0) /* detach */ 522 ipfw_hook(0, AF_INET); 523 else if (V_fw_enable && ipfw_hook(1, AF_INET) != 0) { 524 error = ENOENT; /* see ip_fw_pfil.c::ipfw_hook() */ 525 printf("ipfw_hook() error\n"); 526 } 527 #ifdef INET6 528 if (arg == 0) /* detach */ 529 ipfw_hook(0, AF_INET6); 530 else if (V_fw6_enable && ipfw_hook(1, AF_INET6) != 0) { 531 error = ENOENT; 532 printf("ipfw6_hook() error\n"); 533 } 534 #endif 535 if (arg == 0) /* detach */ 536 ipfw_hook(0, AF_LINK); 537 else if (V_fwlink_enable && ipfw_hook(1, AF_LINK) != 0) { 538 error = ENOENT; 539 printf("ipfw_link_hook() error\n"); 540 } 541 return error; 542 } 543 544 int 545 ipfw_chg_hook(SYSCTL_HANDLER_ARGS) 546 { 547 int newval; 548 int error; 549 int af; 550 551 if (arg1 == &V_fw_enable) 552 af = AF_INET; 553 #ifdef INET6 554 else if (arg1 == &V_fw6_enable) 555 af = AF_INET6; 556 #endif 557 else if (arg1 == &V_fwlink_enable) 558 af = AF_LINK; 559 else 560 return (EINVAL); 561 562 newval = *(int *)arg1; 563 /* Handle sysctl change */ 564 error = sysctl_handle_int(oidp, &newval, 0, req); 565 566 if (error) 567 return (error); 568 569 /* Formalize new value */ 570 newval = (newval) ? 1 : 0; 571 572 if (*(int *)arg1 == newval) 573 return (0); 574 575 error = ipfw_hook(newval, af); 576 if (error) 577 return (error); 578 *(int *)arg1 = newval; 579 580 return (0); 581 } 582 /* end of file */ 583