46#include <sys/socket.h> 47#include <sys/socketvar.h> 48#include <sys/sysctl.h> 49#include <sys/ucred.h> 50#include <sys/vimage.h> 51 52#include <net/if.h> 53#include <net/route.h> 54#include <net/pfil.h> 55 56#include <netinet/in.h> 57#include <netinet/in_systm.h> 58#include <netinet/in_var.h> 59#include <netinet/ip.h> 60#include <netinet/ip_var.h> 61#include <netinet/ip_fw.h> 62#include <netinet/ip_divert.h> 63#include <netinet/ip_dummynet.h> 64 65#include <netgraph/ng_ipfw.h> 66 67#include <machine/in_cksum.h> 68 69int fw_enable = 1; 70#ifdef INET6 71int fw6_enable = 1; 72#endif 73 74int ipfw_chg_hook(SYSCTL_HANDLER_ARGS); 75 76/* Dummynet hooks. */ 77ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL; 78 79/* Divert hooks. */ 80ip_divert_packet_t *ip_divert_ptr = NULL; 81 82/* ng_ipfw hooks. */ 83ng_ipfw_input_t *ng_ipfw_input_p = NULL; 84 85/* Forward declarations. */ 86static int ipfw_divert(struct mbuf **, int, int); 87#define DIV_DIR_IN 1 88#define DIV_DIR_OUT 0 89 90int 91ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, 92 struct inpcb *inp) 93{ 94 struct ip_fw_args args; 95 struct ng_ipfw_tag *ng_tag; 96 struct m_tag *dn_tag; 97 int ipfw = 0; 98 int divert; 99 int tee; 100#ifdef IPFIREWALL_FORWARD 101 struct m_tag *fwd_tag; 102#endif 103 104 KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!")); 105 106 bzero(&args, sizeof(args)); 107 108 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0, 109 NULL); 110 if (ng_tag != NULL) { 111 KASSERT(ng_tag->dir == NG_IPFW_IN, 112 ("ng_ipfw tag with wrong direction")); 113 args.rule = ng_tag->rule; 114 m_tag_delete(*m0, (struct m_tag *)ng_tag); 115 } 116 117again: 118 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL); 119 if (dn_tag != NULL){ 120 struct dn_pkt_tag *dt; 121 122 dt = (struct dn_pkt_tag *)(dn_tag+1); 123 args.rule = dt->rule; 124 125 m_tag_delete(*m0, dn_tag); 126 } 127 128 args.m = *m0; 129 args.inp = inp; 130 ipfw = ipfw_chk(&args); 131 *m0 = args.m; 132 tee = 0; 133 134 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", 135 __func__)); 136 137 switch (ipfw) { 138 case IP_FW_PASS: 139 if (args.next_hop == NULL) 140 goto pass; 141 142#ifdef IPFIREWALL_FORWARD 143 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, 144 sizeof(struct sockaddr_in), M_NOWAIT); 145 if (fwd_tag == NULL) 146 goto drop; 147 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in)); 148 m_tag_prepend(*m0, fwd_tag); 149 150 if (in_localip(args.next_hop->sin_addr)) 151 (*m0)->m_flags |= M_FASTFWD_OURS; 152 goto pass; 153#endif 154 break; /* not reached */ 155 156 case IP_FW_DENY: 157 goto drop; 158 break; /* not reached */ 159 160 case IP_FW_DUMMYNET: 161 if (!DUMMYNET_LOADED) 162 goto drop; 163 if (mtod(*m0, struct ip *)->ip_v == 4) 164 ip_dn_io_ptr(m0, DN_TO_IP_IN, &args); 165 else if (mtod(*m0, struct ip *)->ip_v == 6) 166 ip_dn_io_ptr(m0, DN_TO_IP6_IN, &args); 167 if (*m0 != NULL) 168 goto again; 169 return 0; /* packet consumed */ 170 171 case IP_FW_TEE: 172 tee = 1; 173 /* fall through */ 174 175 case IP_FW_DIVERT: 176 divert = ipfw_divert(m0, DIV_DIR_IN, tee); 177 if (divert) { 178 *m0 = NULL; 179 return 0; /* packet consumed */ 180 } else { 181 args.rule = NULL; 182 goto again; /* continue with packet */ 183 } 184 185 case IP_FW_NGTEE: 186 if (!NG_IPFW_LOADED) 187 goto drop; 188 (void)ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 1); 189 goto again; /* continue with packet */ 190 191 case IP_FW_NETGRAPH: 192 if (!NG_IPFW_LOADED) 193 goto drop; 194 return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0); 195 196 case IP_FW_NAT: 197 goto again; /* continue with packet */ 198 199 default: 200 KASSERT(0, ("%s: unknown retval", __func__)); 201 } 202 203drop: 204 if (*m0) 205 m_freem(*m0); 206 *m0 = NULL; 207 return (EACCES); 208pass: 209 return 0; /* not filtered */ 210} 211 212int 213ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, 214 struct inpcb *inp) 215{ 216 struct ip_fw_args args; 217 struct ng_ipfw_tag *ng_tag; 218 struct m_tag *dn_tag; 219 int ipfw = 0; 220 int divert; 221 int tee; 222#ifdef IPFIREWALL_FORWARD 223 struct m_tag *fwd_tag; 224#endif 225 226 KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!")); 227 228 bzero(&args, sizeof(args)); 229 230 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0, 231 NULL); 232 if (ng_tag != NULL) { 233 KASSERT(ng_tag->dir == NG_IPFW_OUT, 234 ("ng_ipfw tag with wrong direction")); 235 args.rule = ng_tag->rule; 236 m_tag_delete(*m0, (struct m_tag *)ng_tag); 237 } 238 239again: 240 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL); 241 if (dn_tag != NULL) { 242 struct dn_pkt_tag *dt; 243 244 dt = (struct dn_pkt_tag *)(dn_tag+1); 245 args.rule = dt->rule; 246 247 m_tag_delete(*m0, dn_tag); 248 } 249 250 args.m = *m0; 251 args.oif = ifp; 252 args.inp = inp; 253 ipfw = ipfw_chk(&args); 254 *m0 = args.m; 255 tee = 0; 256 257 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", 258 __func__)); 259 260 switch (ipfw) { 261 case IP_FW_PASS: 262 if (args.next_hop == NULL) 263 goto pass; 264#ifdef IPFIREWALL_FORWARD 265 /* Overwrite existing tag. */ 266 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); 267 if (fwd_tag == NULL) { 268 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, 269 sizeof(struct sockaddr_in), M_NOWAIT); 270 if (fwd_tag == NULL) 271 goto drop; 272 } else 273 m_tag_unlink(*m0, fwd_tag); 274 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in)); 275 m_tag_prepend(*m0, fwd_tag); 276 277 if (in_localip(args.next_hop->sin_addr)) 278 (*m0)->m_flags |= M_FASTFWD_OURS; 279 goto pass; 280#endif 281 break; /* not reached */ 282 283 case IP_FW_DENY: 284 goto drop; 285 break; /* not reached */ 286 287 case IP_FW_DUMMYNET: 288 if (!DUMMYNET_LOADED) 289 break; 290 if (mtod(*m0, struct ip *)->ip_v == 4) 291 ip_dn_io_ptr(m0, DN_TO_IP_OUT, &args); 292 else if (mtod(*m0, struct ip *)->ip_v == 6) 293 ip_dn_io_ptr(m0, DN_TO_IP6_OUT, &args); 294 if (*m0 != NULL) 295 goto again; 296 return 0; /* packet consumed */ 297 298 break; 299 300 case IP_FW_TEE: 301 tee = 1; 302 /* fall through */ 303 304 case IP_FW_DIVERT: 305 divert = ipfw_divert(m0, DIV_DIR_OUT, tee); 306 if (divert) { 307 *m0 = NULL; 308 return 0; /* packet consumed */ 309 } else { 310 args.rule = NULL; 311 goto again; /* continue with packet */ 312 } 313 314 case IP_FW_NGTEE: 315 if (!NG_IPFW_LOADED) 316 goto drop; 317 (void)ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 1); 318 goto again; /* continue with packet */ 319 320 case IP_FW_NETGRAPH: 321 if (!NG_IPFW_LOADED) 322 goto drop; 323 return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0); 324 325 case IP_FW_NAT: 326 goto again; /* continue with packet */ 327 328 default: 329 KASSERT(0, ("%s: unknown retval", __func__)); 330 } 331 332drop: 333 if (*m0) 334 m_freem(*m0); 335 *m0 = NULL; 336 return (EACCES); 337pass: 338 return 0; /* not filtered */ 339} 340 341static int 342ipfw_divert(struct mbuf **m, int incoming, int tee) 343{ 344 /* 345 * ipfw_chk() has already tagged the packet with the divert tag. 346 * If tee is set, copy packet and return original. 347 * If not tee, consume packet and send it to divert socket. 348 */ 349 struct mbuf *clone, *reass; 350 struct ip *ip; 351 int hlen; 352 353 reass = NULL; 354 355 /* Is divert module loaded? */ 356 if (ip_divert_ptr == NULL) 357 goto nodivert; 358 359 /* Cloning needed for tee? */ 360 if (tee) 361 clone = m_dup(*m, M_DONTWAIT); 362 else 363 clone = *m; 364 365 /* In case m_dup was unable to allocate mbufs. */ 366 if (clone == NULL) 367 goto teeout; 368 369 /* 370 * Divert listeners can only handle non-fragmented packets. 371 * However when tee is set we will *not* de-fragment the packets; 372 * Doing do would put the reassembly into double-jeopardy. On top 373 * of that someone doing a tee will probably want to get the packet 374 * in its original form. 375 */ 376 ip = mtod(clone, struct ip *); 377 if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) { 378 379 /* Reassemble packet. */ 380 reass = ip_reass(clone); 381 382 /* 383 * IP header checksum fixup after reassembly and leave header 384 * in network byte order. 385 */ 386 if (reass != NULL) { 387 ip = mtod(reass, struct ip *); 388 hlen = ip->ip_hl << 2; 389 ip->ip_len = htons(ip->ip_len); 390 ip->ip_off = htons(ip->ip_off); 391 ip->ip_sum = 0; 392 if (hlen == sizeof(struct ip)) 393 ip->ip_sum = in_cksum_hdr(ip); 394 else 395 ip->ip_sum = in_cksum(reass, hlen); 396 clone = reass; 397 } else 398 clone = NULL; 399 } else { 400 /* Convert header to network byte order. */ 401 ip->ip_len = htons(ip->ip_len); 402 ip->ip_off = htons(ip->ip_off); 403 } 404 405 /* Do the dirty job... */ 406 if (clone && ip_divert_ptr != NULL) 407 ip_divert_ptr(clone, incoming); 408 409teeout: 410 /* 411 * For tee we leave the divert tag attached to original packet. 412 * It will then continue rule evaluation after the tee rule. 413 */ 414 if (tee) 415 return 0; 416 417 /* Packet diverted and consumed */ 418 return 1; 419 420nodivert: 421 m_freem(*m); 422 return 1; 423} 424 425static int 426ipfw_hook(void) 427{ 428 struct pfil_head *pfh_inet; 429 430 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 431 if (pfh_inet == NULL) 432 return ENOENT; 433 434 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); 435 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); 436 437 return 0; 438} 439 440static int 441ipfw_unhook(void) 442{ 443 struct pfil_head *pfh_inet; 444 445 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 446 if (pfh_inet == NULL) 447 return ENOENT; 448 449 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); 450 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); 451 452 return 0; 453} 454 455#ifdef INET6 456static int 457ipfw6_hook(void) 458{ 459 struct pfil_head *pfh_inet6; 460 461 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); 462 if (pfh_inet6 == NULL) 463 return ENOENT; 464 465 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); 466 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); 467 468 return 0; 469} 470 471static int 472ipfw6_unhook(void) 473{ 474 struct pfil_head *pfh_inet6; 475 476 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); 477 if (pfh_inet6 == NULL) 478 return ENOENT; 479 480 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); 481 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); 482 483 return 0; 484} 485#endif /* INET6 */ 486 487int 488ipfw_chg_hook(SYSCTL_HANDLER_ARGS) 489{ 490 INIT_VNET_IPFW(curvnet); 491 int enable = *(int *)arg1; 492 int error; 493 494 error = sysctl_handle_int(oidp, &enable, 0, req); 495 if (error) 496 return (error); 497 498 enable = (enable) ? 1 : 0; 499 500 if (enable == *(int *)arg1) 501 return (0); 502 503 if (arg1 == &V_fw_enable) { 504 if (enable) 505 error = ipfw_hook(); 506 else 507 error = ipfw_unhook(); 508 } 509#ifdef INET6 510 if (arg1 == &V_fw6_enable) { 511 if (enable) 512 error = ipfw6_hook(); 513 else 514 error = ipfw6_unhook(); 515 } 516#endif 517 518 if (error) 519 return (error); 520 521 *(int *)arg1 = enable; 522 523 return (0); 524} 525 526static int 527ipfw_modevent(module_t mod, int type, void *unused) 528{ 529 int err = 0; 530 531 switch (type) { 532 case MOD_LOAD: 533 if ((err = ipfw_init()) != 0) { 534 printf("ipfw_init() error\n"); 535 break; 536 } 537 if ((err = ipfw_hook()) != 0) { 538 printf("ipfw_hook() error\n"); 539 break; 540 } 541#ifdef INET6 542 if ((err = ipfw6_hook()) != 0) { 543 printf("ipfw_hook() error\n"); 544 break; 545 } 546#endif 547 break; 548 549 case MOD_UNLOAD: 550 if ((err = ipfw_unhook()) > 0) 551 break; 552#ifdef INET6 553 if ((err = ipfw6_unhook()) > 0) 554 break; 555#endif 556 ipfw_destroy(); 557 break; 558 559 default: 560 return EOPNOTSUPP; 561 break; 562 } 563 return err; 564} 565 566static moduledata_t ipfwmod = { 567 "ipfw", 568 ipfw_modevent, 569 0 570}; 571DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY - 256); 572MODULE_VERSION(ipfw, 2);
| 48#include <sys/socket.h> 49#include <sys/socketvar.h> 50#include <sys/sysctl.h> 51#include <sys/ucred.h> 52#include <sys/vimage.h> 53 54#include <net/if.h> 55#include <net/route.h> 56#include <net/pfil.h> 57 58#include <netinet/in.h> 59#include <netinet/in_systm.h> 60#include <netinet/in_var.h> 61#include <netinet/ip.h> 62#include <netinet/ip_var.h> 63#include <netinet/ip_fw.h> 64#include <netinet/ip_divert.h> 65#include <netinet/ip_dummynet.h> 66 67#include <netgraph/ng_ipfw.h> 68 69#include <machine/in_cksum.h> 70 71int fw_enable = 1; 72#ifdef INET6 73int fw6_enable = 1; 74#endif 75 76int ipfw_chg_hook(SYSCTL_HANDLER_ARGS); 77 78/* Dummynet hooks. */ 79ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL; 80 81/* Divert hooks. */ 82ip_divert_packet_t *ip_divert_ptr = NULL; 83 84/* ng_ipfw hooks. */ 85ng_ipfw_input_t *ng_ipfw_input_p = NULL; 86 87/* Forward declarations. */ 88static int ipfw_divert(struct mbuf **, int, int); 89#define DIV_DIR_IN 1 90#define DIV_DIR_OUT 0 91 92int 93ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, 94 struct inpcb *inp) 95{ 96 struct ip_fw_args args; 97 struct ng_ipfw_tag *ng_tag; 98 struct m_tag *dn_tag; 99 int ipfw = 0; 100 int divert; 101 int tee; 102#ifdef IPFIREWALL_FORWARD 103 struct m_tag *fwd_tag; 104#endif 105 106 KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!")); 107 108 bzero(&args, sizeof(args)); 109 110 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0, 111 NULL); 112 if (ng_tag != NULL) { 113 KASSERT(ng_tag->dir == NG_IPFW_IN, 114 ("ng_ipfw tag with wrong direction")); 115 args.rule = ng_tag->rule; 116 m_tag_delete(*m0, (struct m_tag *)ng_tag); 117 } 118 119again: 120 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL); 121 if (dn_tag != NULL){ 122 struct dn_pkt_tag *dt; 123 124 dt = (struct dn_pkt_tag *)(dn_tag+1); 125 args.rule = dt->rule; 126 127 m_tag_delete(*m0, dn_tag); 128 } 129 130 args.m = *m0; 131 args.inp = inp; 132 ipfw = ipfw_chk(&args); 133 *m0 = args.m; 134 tee = 0; 135 136 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", 137 __func__)); 138 139 switch (ipfw) { 140 case IP_FW_PASS: 141 if (args.next_hop == NULL) 142 goto pass; 143 144#ifdef IPFIREWALL_FORWARD 145 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, 146 sizeof(struct sockaddr_in), M_NOWAIT); 147 if (fwd_tag == NULL) 148 goto drop; 149 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in)); 150 m_tag_prepend(*m0, fwd_tag); 151 152 if (in_localip(args.next_hop->sin_addr)) 153 (*m0)->m_flags |= M_FASTFWD_OURS; 154 goto pass; 155#endif 156 break; /* not reached */ 157 158 case IP_FW_DENY: 159 goto drop; 160 break; /* not reached */ 161 162 case IP_FW_DUMMYNET: 163 if (!DUMMYNET_LOADED) 164 goto drop; 165 if (mtod(*m0, struct ip *)->ip_v == 4) 166 ip_dn_io_ptr(m0, DN_TO_IP_IN, &args); 167 else if (mtod(*m0, struct ip *)->ip_v == 6) 168 ip_dn_io_ptr(m0, DN_TO_IP6_IN, &args); 169 if (*m0 != NULL) 170 goto again; 171 return 0; /* packet consumed */ 172 173 case IP_FW_TEE: 174 tee = 1; 175 /* fall through */ 176 177 case IP_FW_DIVERT: 178 divert = ipfw_divert(m0, DIV_DIR_IN, tee); 179 if (divert) { 180 *m0 = NULL; 181 return 0; /* packet consumed */ 182 } else { 183 args.rule = NULL; 184 goto again; /* continue with packet */ 185 } 186 187 case IP_FW_NGTEE: 188 if (!NG_IPFW_LOADED) 189 goto drop; 190 (void)ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 1); 191 goto again; /* continue with packet */ 192 193 case IP_FW_NETGRAPH: 194 if (!NG_IPFW_LOADED) 195 goto drop; 196 return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0); 197 198 case IP_FW_NAT: 199 goto again; /* continue with packet */ 200 201 default: 202 KASSERT(0, ("%s: unknown retval", __func__)); 203 } 204 205drop: 206 if (*m0) 207 m_freem(*m0); 208 *m0 = NULL; 209 return (EACCES); 210pass: 211 return 0; /* not filtered */ 212} 213 214int 215ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, 216 struct inpcb *inp) 217{ 218 struct ip_fw_args args; 219 struct ng_ipfw_tag *ng_tag; 220 struct m_tag *dn_tag; 221 int ipfw = 0; 222 int divert; 223 int tee; 224#ifdef IPFIREWALL_FORWARD 225 struct m_tag *fwd_tag; 226#endif 227 228 KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!")); 229 230 bzero(&args, sizeof(args)); 231 232 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0, 233 NULL); 234 if (ng_tag != NULL) { 235 KASSERT(ng_tag->dir == NG_IPFW_OUT, 236 ("ng_ipfw tag with wrong direction")); 237 args.rule = ng_tag->rule; 238 m_tag_delete(*m0, (struct m_tag *)ng_tag); 239 } 240 241again: 242 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL); 243 if (dn_tag != NULL) { 244 struct dn_pkt_tag *dt; 245 246 dt = (struct dn_pkt_tag *)(dn_tag+1); 247 args.rule = dt->rule; 248 249 m_tag_delete(*m0, dn_tag); 250 } 251 252 args.m = *m0; 253 args.oif = ifp; 254 args.inp = inp; 255 ipfw = ipfw_chk(&args); 256 *m0 = args.m; 257 tee = 0; 258 259 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", 260 __func__)); 261 262 switch (ipfw) { 263 case IP_FW_PASS: 264 if (args.next_hop == NULL) 265 goto pass; 266#ifdef IPFIREWALL_FORWARD 267 /* Overwrite existing tag. */ 268 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); 269 if (fwd_tag == NULL) { 270 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, 271 sizeof(struct sockaddr_in), M_NOWAIT); 272 if (fwd_tag == NULL) 273 goto drop; 274 } else 275 m_tag_unlink(*m0, fwd_tag); 276 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in)); 277 m_tag_prepend(*m0, fwd_tag); 278 279 if (in_localip(args.next_hop->sin_addr)) 280 (*m0)->m_flags |= M_FASTFWD_OURS; 281 goto pass; 282#endif 283 break; /* not reached */ 284 285 case IP_FW_DENY: 286 goto drop; 287 break; /* not reached */ 288 289 case IP_FW_DUMMYNET: 290 if (!DUMMYNET_LOADED) 291 break; 292 if (mtod(*m0, struct ip *)->ip_v == 4) 293 ip_dn_io_ptr(m0, DN_TO_IP_OUT, &args); 294 else if (mtod(*m0, struct ip *)->ip_v == 6) 295 ip_dn_io_ptr(m0, DN_TO_IP6_OUT, &args); 296 if (*m0 != NULL) 297 goto again; 298 return 0; /* packet consumed */ 299 300 break; 301 302 case IP_FW_TEE: 303 tee = 1; 304 /* fall through */ 305 306 case IP_FW_DIVERT: 307 divert = ipfw_divert(m0, DIV_DIR_OUT, tee); 308 if (divert) { 309 *m0 = NULL; 310 return 0; /* packet consumed */ 311 } else { 312 args.rule = NULL; 313 goto again; /* continue with packet */ 314 } 315 316 case IP_FW_NGTEE: 317 if (!NG_IPFW_LOADED) 318 goto drop; 319 (void)ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 1); 320 goto again; /* continue with packet */ 321 322 case IP_FW_NETGRAPH: 323 if (!NG_IPFW_LOADED) 324 goto drop; 325 return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0); 326 327 case IP_FW_NAT: 328 goto again; /* continue with packet */ 329 330 default: 331 KASSERT(0, ("%s: unknown retval", __func__)); 332 } 333 334drop: 335 if (*m0) 336 m_freem(*m0); 337 *m0 = NULL; 338 return (EACCES); 339pass: 340 return 0; /* not filtered */ 341} 342 343static int 344ipfw_divert(struct mbuf **m, int incoming, int tee) 345{ 346 /* 347 * ipfw_chk() has already tagged the packet with the divert tag. 348 * If tee is set, copy packet and return original. 349 * If not tee, consume packet and send it to divert socket. 350 */ 351 struct mbuf *clone, *reass; 352 struct ip *ip; 353 int hlen; 354 355 reass = NULL; 356 357 /* Is divert module loaded? */ 358 if (ip_divert_ptr == NULL) 359 goto nodivert; 360 361 /* Cloning needed for tee? */ 362 if (tee) 363 clone = m_dup(*m, M_DONTWAIT); 364 else 365 clone = *m; 366 367 /* In case m_dup was unable to allocate mbufs. */ 368 if (clone == NULL) 369 goto teeout; 370 371 /* 372 * Divert listeners can only handle non-fragmented packets. 373 * However when tee is set we will *not* de-fragment the packets; 374 * Doing do would put the reassembly into double-jeopardy. On top 375 * of that someone doing a tee will probably want to get the packet 376 * in its original form. 377 */ 378 ip = mtod(clone, struct ip *); 379 if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) { 380 381 /* Reassemble packet. */ 382 reass = ip_reass(clone); 383 384 /* 385 * IP header checksum fixup after reassembly and leave header 386 * in network byte order. 387 */ 388 if (reass != NULL) { 389 ip = mtod(reass, struct ip *); 390 hlen = ip->ip_hl << 2; 391 ip->ip_len = htons(ip->ip_len); 392 ip->ip_off = htons(ip->ip_off); 393 ip->ip_sum = 0; 394 if (hlen == sizeof(struct ip)) 395 ip->ip_sum = in_cksum_hdr(ip); 396 else 397 ip->ip_sum = in_cksum(reass, hlen); 398 clone = reass; 399 } else 400 clone = NULL; 401 } else { 402 /* Convert header to network byte order. */ 403 ip->ip_len = htons(ip->ip_len); 404 ip->ip_off = htons(ip->ip_off); 405 } 406 407 /* Do the dirty job... */ 408 if (clone && ip_divert_ptr != NULL) 409 ip_divert_ptr(clone, incoming); 410 411teeout: 412 /* 413 * For tee we leave the divert tag attached to original packet. 414 * It will then continue rule evaluation after the tee rule. 415 */ 416 if (tee) 417 return 0; 418 419 /* Packet diverted and consumed */ 420 return 1; 421 422nodivert: 423 m_freem(*m); 424 return 1; 425} 426 427static int 428ipfw_hook(void) 429{ 430 struct pfil_head *pfh_inet; 431 432 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 433 if (pfh_inet == NULL) 434 return ENOENT; 435 436 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); 437 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); 438 439 return 0; 440} 441 442static int 443ipfw_unhook(void) 444{ 445 struct pfil_head *pfh_inet; 446 447 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 448 if (pfh_inet == NULL) 449 return ENOENT; 450 451 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); 452 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); 453 454 return 0; 455} 456 457#ifdef INET6 458static int 459ipfw6_hook(void) 460{ 461 struct pfil_head *pfh_inet6; 462 463 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); 464 if (pfh_inet6 == NULL) 465 return ENOENT; 466 467 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); 468 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); 469 470 return 0; 471} 472 473static int 474ipfw6_unhook(void) 475{ 476 struct pfil_head *pfh_inet6; 477 478 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); 479 if (pfh_inet6 == NULL) 480 return ENOENT; 481 482 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); 483 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); 484 485 return 0; 486} 487#endif /* INET6 */ 488 489int 490ipfw_chg_hook(SYSCTL_HANDLER_ARGS) 491{ 492 INIT_VNET_IPFW(curvnet); 493 int enable = *(int *)arg1; 494 int error; 495 496 error = sysctl_handle_int(oidp, &enable, 0, req); 497 if (error) 498 return (error); 499 500 enable = (enable) ? 1 : 0; 501 502 if (enable == *(int *)arg1) 503 return (0); 504 505 if (arg1 == &V_fw_enable) { 506 if (enable) 507 error = ipfw_hook(); 508 else 509 error = ipfw_unhook(); 510 } 511#ifdef INET6 512 if (arg1 == &V_fw6_enable) { 513 if (enable) 514 error = ipfw6_hook(); 515 else 516 error = ipfw6_unhook(); 517 } 518#endif 519 520 if (error) 521 return (error); 522 523 *(int *)arg1 = enable; 524 525 return (0); 526} 527 528static int 529ipfw_modevent(module_t mod, int type, void *unused) 530{ 531 int err = 0; 532 533 switch (type) { 534 case MOD_LOAD: 535 if ((err = ipfw_init()) != 0) { 536 printf("ipfw_init() error\n"); 537 break; 538 } 539 if ((err = ipfw_hook()) != 0) { 540 printf("ipfw_hook() error\n"); 541 break; 542 } 543#ifdef INET6 544 if ((err = ipfw6_hook()) != 0) { 545 printf("ipfw_hook() error\n"); 546 break; 547 } 548#endif 549 break; 550 551 case MOD_UNLOAD: 552 if ((err = ipfw_unhook()) > 0) 553 break; 554#ifdef INET6 555 if ((err = ipfw6_unhook()) > 0) 556 break; 557#endif 558 ipfw_destroy(); 559 break; 560 561 default: 562 return EOPNOTSUPP; 563 break; 564 } 565 return err; 566} 567 568static moduledata_t ipfwmod = { 569 "ipfw", 570 ipfw_modevent, 571 0 572}; 573DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY - 256); 574MODULE_VERSION(ipfw, 2);
|