diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index d163106b7e8d..4cc2f9e98776 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -866,6 +866,13 @@ in any subsequent forwarding decisions. Initially this is limited to the values 0 through 15, see .Xr setfib 8 . Processing continues at the next rule. +.It Cm reass +Queue and reassemble ip fragments. +If the packet is not fragmented, counters are updated and processing continues with the next rule. +If the packet is the last logical fragment, the packet is reassembled and, if +.Va net.inet.ip.fw.one_pass +is set to 0, processing continues with the next rule, else packet is allowed to pass and search terminates. +If the packet is a fragment in the middle, it is consumed and processing stops immediately. .El .Ss RULE BODY The body of a rule contains zero or more patterns (such as diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index c0dfac31a395..9d95ec46a47e 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -211,6 +211,7 @@ static struct _s_x rule_actions[] = { { "check-state", TOK_CHECKSTATE }, { "//", TOK_COMMENT }, { "nat", TOK_NAT }, + { "reass", TOK_REASS }, { "setfib", TOK_SETFIB }, { NULL, 0 } /* terminator */ }; @@ -1089,6 +1090,10 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) case O_SETFIB: PRINT_UINT_ARG("setfib ", cmd->arg1); break; + + case O_REASS: + printf("reass"); + break; default: printf("** unrecognized action %d len %d ", @@ -2781,6 +2786,10 @@ ipfw_add(int ac, char *av[]) ac--; av++; break; } + + case TOK_REASS: + action->opcode = O_REASS; + break; default: errx(EX_DATAERR, "invalid action %s\n", av[-1]); diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index 442e7b6db4cf..508bb6cf6eaf 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -95,6 +95,7 @@ enum tokens { TOK_UNREACH, TOK_CHECKSTATE, TOK_NAT, + TOK_REASS, TOK_ALTQ, TOK_LOG, diff --git a/sbin/ipfw/main.c b/sbin/ipfw/main.c index a83dd5fe17cc..39160578a6d9 100644 --- a/sbin/ipfw/main.c +++ b/sbin/ipfw/main.c @@ -54,7 +54,7 @@ help(void) "RULE-BODY: check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n" "ACTION: check-state | allow | count | deny | unreach{,6} CODE |\n" " skipto N | {divert|tee} PORT | forward ADDR |\n" -" pipe N | queue N | nat N | setfib FIB\n" +" pipe N | queue N | nat N | setfib FIB | reass\n" "PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n" "ADDR: [ MAC dst src ether_type ] \n" " [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 0cf1a526bde4..35e7e126d2ec 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -139,7 +139,8 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ O_FORWARD_IP, /* fwd sockaddr */ O_FORWARD_MAC, /* fwd mac */ O_NAT, /* nope */ - + O_REASS, /* none */ + /* * More opcodes. */ @@ -574,6 +575,7 @@ enum { IP_FW_NETGRAPH, IP_FW_NGTEE, IP_FW_NAT, + IP_FW_REASS, }; /* flags for divert mtag */ diff --git a/sys/netinet/ip_fw2.c b/sys/netinet/ip_fw2.c index 1405a27287c2..fa6e6f12c510 100644 --- a/sys/netinet/ip_fw2.c +++ b/sys/netinet/ip_fw2.c @@ -898,6 +898,9 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args, case O_NAT: action = "Nat"; break; + case O_REASS: + action = "Reass"; + break; default: action = "UNKNOWN"; break; @@ -3375,6 +3378,55 @@ do { \ goto done; } + case O_REASS: { + int ip_off; + + f->pcnt++; + f->bcnt += pktlen; + ip_off = (args->eh != NULL) ? ntohs(ip->ip_off) : ip->ip_off; + if (ip_off & (IP_MF | IP_OFFMASK)) { + /* + * ip_reass() expects len & off in host + * byte order: fix them in case we come + * from layer2. + */ + if (args->eh != NULL) { + ip->ip_len = ntohs(ip->ip_len); + ip->ip_off = ntohs(ip->ip_off); + } + + m = ip_reass(m); + args->m = m; + + /* + * IP header checksum fixup after + * reassembly and leave header + * in network byte order. + */ + if (m != NULL) { + int hlen; + + ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; + /* revert len & off for layer2 pkts */ + if (args->eh != NULL) + ip->ip_len = htons(ip->ip_len); + ip->ip_sum = 0; + if (hlen == sizeof(struct ip)) + ip->ip_sum = in_cksum_hdr(ip); + else + ip->ip_sum = in_cksum(m, hlen); + retval = IP_FW_REASS; + args->rule = f; + goto done; + } else { + retval = IP_FW_DENY; + goto done; + } + } + goto next_rule; + } + default: panic("-- unknown opcode %d\n", cmd->opcode); } /* end of switch() on opcodes */ @@ -4024,6 +4076,7 @@ check_ipfw_struct(struct ip_fw *rule, int size) case O_UNREACH6: #endif case O_SKIPTO: + case O_REASS: check_size: if (cmdlen != F_INSN_SIZE(ipfw_insn)) goto bad_size; diff --git a/sys/netinet/ip_fw_pfil.c b/sys/netinet/ip_fw_pfil.c index 1e6d2b0e0e81..11560a7e122b 100644 --- a/sys/netinet/ip_fw_pfil.c +++ b/sys/netinet/ip_fw_pfil.c @@ -200,6 +200,9 @@ ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, case IP_FW_NAT: goto again; /* continue with packet */ + case IP_FW_REASS: + goto again; + default: KASSERT(0, ("%s: unknown retval", __func__)); } @@ -329,6 +332,9 @@ ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, case IP_FW_NAT: goto again; /* continue with packet */ + case IP_FW_REASS: + goto again; + default: KASSERT(0, ("%s: unknown retval", __func__)); }