aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2024-09-13 15:07:06 +0000
committerKristof Provost <kp@FreeBSD.org>2024-10-01 07:55:13 +0000
commit288bec2b2bd10d80cdc35a687e8a373f5931c80d (patch)
tree641b1c8ddb160055cdfd99c7dfcce136b5f3a0fc
parentb35f0aa4952cf03f1b2093b110607c13dd2ec991 (diff)
downloadsrc-288bec2b2bd10d80cdc35a687e8a373f5931c80d.tar.gz
src-288bec2b2bd10d80cdc35a687e8a373f5931c80d.zip
pf: fold pf_test_fragment() into pf_test_rule()
Reduces code and fixes a bunch of bugs with fragment handling not being in sync with the rest of the ruleset. Much feedback from mpf, bluhm & markus Thanks to Tony Sarendal for help with testing ok bluhm; various previous versions ok henning, claudio, mpf, markus Note that while this changes the order of src addr/src port/dst addr/dst port skips this doesn't actually affect the kernel/userspace ABI. The kernel always recalculates skip steps. As a result we have to fix one of the pfctl parser tests. Note that this is an order change that does not affect what packets are acceppted or dropped. Obtained from: OpenBSD, mcbride <mcbride@openbsd.org>, 04c69899a7 Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D46705
-rw-r--r--sbin/pfctl/parse.y4
-rw-r--r--sbin/pfctl/pfctl_optimize.c2
-rw-r--r--sbin/pfctl/tests/files/pf0004.ok24
-rw-r--r--sys/net/pfvar.h2
-rw-r--r--sys/netpfil/pf/pf.c450
-rw-r--r--sys/netpfil/pf/pf.h4
6 files changed, 198 insertions, 288 deletions
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 9d95122f9826..ad25f1996d36 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -6073,10 +6073,10 @@ expand_rule(struct pfctl_rule *r,
LOOP_THROUGH(struct node_proto, proto, protos,
LOOP_THROUGH(struct node_icmp, icmp_type, icmp_types,
LOOP_THROUGH(struct node_host, src_host, src_hosts,
- LOOP_THROUGH(struct node_port, src_port, src_ports,
- LOOP_THROUGH(struct node_os, src_os, src_oses,
LOOP_THROUGH(struct node_host, dst_host, dst_hosts,
+ LOOP_THROUGH(struct node_port, src_port, src_ports,
LOOP_THROUGH(struct node_port, dst_port, dst_ports,
+ LOOP_THROUGH(struct node_os, src_os, src_oses,
LOOP_THROUGH(struct node_uid, uid, uids,
LOOP_THROUGH(struct node_gid, gid, gids,
diff --git a/sbin/pfctl/pfctl_optimize.c b/sbin/pfctl/pfctl_optimize.c
index 7440bf8a506d..9858f38b8671 100644
--- a/sbin/pfctl/pfctl_optimize.c
+++ b/sbin/pfctl/pfctl_optimize.c
@@ -249,8 +249,8 @@ static const char *skip_comparitors_names[PF_SKIP_COUNT];
{ "af", PF_SKIP_AF, skip_cmp_af }, \
{ "proto", PF_SKIP_PROTO, skip_cmp_proto }, \
{ "saddr", PF_SKIP_SRC_ADDR, skip_cmp_src_addr }, \
- { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port }, \
{ "daddr", PF_SKIP_DST_ADDR, skip_cmp_dst_addr }, \
+ { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port }, \
{ "dport", PF_SKIP_DST_PORT, skip_cmp_dst_port } \
}
diff --git a/sbin/pfctl/tests/files/pf0004.ok b/sbin/pfctl/tests/files/pf0004.ok
index 5fca4a50f7b1..87b71cdeff3d 100644
--- a/sbin/pfctl/tests/files/pf0004.ok
+++ b/sbin/pfctl/tests/files/pf0004.ok
@@ -15,48 +15,48 @@ block drop in proto tcp from any port >= 80 to any port 1024:2048
block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port = ircd
block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port = 6668
block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port 6669:65535
-block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = ircd
-block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6668
-block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port = ircd
block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port = 6668
block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port 6669:65535
+block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = ircd
+block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6668
+block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 12.34.56.78 port = ircd
block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 12.34.56.78 port = 6668
block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 12.34.56.78 port 6669:65535
block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port = ircd
block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port = 6668
block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port 6669:65535
-block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = ircd
-block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6668
-block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port = ircd
block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port = 6668
block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port 6669:65535
+block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = ircd
+block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6668
+block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 12.34.56.78 port = ircd
block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 12.34.56.78 port = 6668
block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 12.34.56.78 port 6669:65535
block drop in inet proto udp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port = 6667
block drop in inet proto udp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port = 6668
block drop in inet proto udp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port 6669:65535
-block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6667
-block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6668
-block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto udp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port = 6667
block drop in inet proto udp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port = 6668
block drop in inet proto udp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port 6669:65535
+block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6667
+block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6668
+block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto udp from 10.0.0.0/8 port = ftp to 12.34.56.78 port = 6667
block drop in inet proto udp from 10.0.0.0/8 port = ftp to 12.34.56.78 port = 6668
block drop in inet proto udp from 10.0.0.0/8 port = ftp to 12.34.56.78 port 6669:65535
block drop in inet proto udp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port = 6667
block drop in inet proto udp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port = 6668
block drop in inet proto udp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port 6669:65535
-block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6667
-block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6668
-block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto udp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port = 6667
block drop in inet proto udp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port = 6668
block drop in inet proto udp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port 6669:65535
+block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6667
+block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6668
+block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto udp from 172.16.0.0/12 port = ftp to 12.34.56.78 port = 6667
block drop in inet proto udp from 172.16.0.0/12 port = ftp to 12.34.56.78 port = 6668
block drop in inet proto udp from 172.16.0.0/12 port = ftp to 12.34.56.78 port 6669:65535
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index dfc42c16547f..79dcd0d65985 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1619,6 +1619,8 @@ struct pf_pdesc {
* state code. Easier than tags */
#define PFDESC_TCP_NORM 0x0001 /* TCP shall be statefully scrubbed */
#define PFDESC_IP_REAS 0x0002 /* IP frags would've been reassembled */
+ u_int16_t virtual_proto;
+#define PF_VPROTO_FRAGMENT 256
sa_family_t af;
u_int8_t proto;
u_int8_t tos;
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 19e0014ce4eb..ccfe1a0fcd96 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -333,9 +333,6 @@ static int pf_create_state(struct pf_krule *, struct pf_krule *,
static int pf_state_key_addr_setup(struct pf_pdesc *, struct mbuf *,
int, struct pf_state_key_cmp *, int, struct pf_addr *,
int, struct pf_addr *, int);
-static int pf_test_fragment(struct pf_krule **, struct pfi_kkif *,
- struct mbuf *, struct pf_pdesc *,
- struct pf_krule **, struct pf_kruleset **);
static int pf_tcp_track_full(struct pf_kstate **,
struct pfi_kkif *, struct mbuf *, int,
struct pf_pdesc *, u_short *, int *);
@@ -2962,13 +2959,13 @@ pf_calc_skip_steps(struct pf_krulequeue *rules)
if (cur->src.neg != prev->src.neg ||
pf_addr_wrap_neq(&cur->src.addr, &prev->src.addr))
PF_SET_SKIP_STEPS(PF_SKIP_SRC_ADDR);
+ if (cur->dst.neg != prev->dst.neg ||
+ pf_addr_wrap_neq(&cur->dst.addr, &prev->dst.addr))
+ PF_SET_SKIP_STEPS(PF_SKIP_DST_ADDR);
if (cur->src.port[0] != prev->src.port[0] ||
cur->src.port[1] != prev->src.port[1] ||
cur->src.port_op != prev->src.port_op)
PF_SET_SKIP_STEPS(PF_SKIP_SRC_PORT);
- if (cur->dst.neg != prev->dst.neg ||
- pf_addr_wrap_neq(&cur->dst.addr, &prev->dst.addr))
- PF_SET_SKIP_STEPS(PF_SKIP_DST_ADDR);
if (cur->dst.port[0] != prev->dst.port[0] ||
cur->dst.port[1] != prev->dst.port[1] ||
cur->dst.port_op != prev->dst.port_op)
@@ -4877,6 +4874,14 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
return (action);
}
+#define PF_TEST_ATTRIB(t, a)\
+ do { \
+ if (t) { \
+ r = a; \
+ goto nextrule; \
+ } \
+ } while (0)
+
static int
pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif,
struct mbuf *m, int off, struct pf_pdesc *pd, struct pf_krule **am,
@@ -4916,7 +4921,7 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif,
pd->lookup.done = 1;
}
- switch (pd->proto) {
+ switch (pd->virtual_proto) {
case IPPROTO_TCP:
sport = th->th_sport;
dport = th->th_dport;
@@ -5131,108 +5136,136 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif,
while (r != NULL) {
pf_counter_u64_add(&r->evaluations, 1);
- if (pfi_kkif_match(r->kif, kif) == r->ifnot)
- r = r->skip[PF_SKIP_IFP].ptr;
- else if (r->direction && r->direction != pd->dir)
- r = r->skip[PF_SKIP_DIR].ptr;
- else if (r->af && r->af != af)
- r = r->skip[PF_SKIP_AF].ptr;
- else if (r->proto && r->proto != pd->proto)
- r = r->skip[PF_SKIP_PROTO].ptr;
- else if (PF_MISMATCHAW(&r->src.addr, saddr, af,
- r->src.neg, kif, M_GETFIB(m)))
- r = r->skip[PF_SKIP_SRC_ADDR].ptr;
- /* tcp/udp only. port_op always 0 in other cases */
- else if (r->src.port_op && !pf_match_port(r->src.port_op,
- r->src.port[0], r->src.port[1], sport))
- r = r->skip[PF_SKIP_SRC_PORT].ptr;
- else if (PF_MISMATCHAW(&r->dst.addr, daddr, af,
- r->dst.neg, NULL, M_GETFIB(m)))
- r = r->skip[PF_SKIP_DST_ADDR].ptr;
- /* tcp/udp only. port_op always 0 in other cases */
- else if (r->dst.port_op && !pf_match_port(r->dst.port_op,
- r->dst.port[0], r->dst.port[1], dport))
- r = r->skip[PF_SKIP_DST_PORT].ptr;
- /* icmp only. type always 0 in other cases */
- else if (r->type && r->type != icmptype + 1)
- r = TAILQ_NEXT(r, entries);
- /* icmp only. type always 0 in other cases */
- else if (r->code && r->code != icmpcode + 1)
- r = TAILQ_NEXT(r, entries);
- else if (r->tos && !(r->tos == pd->tos))
- r = TAILQ_NEXT(r, entries);
- else if (r->rule_flag & PFRULE_FRAGMENT)
- r = TAILQ_NEXT(r, entries);
- else if (pd->proto == IPPROTO_TCP &&
- (r->flagset & th->th_flags) != r->flags)
- r = TAILQ_NEXT(r, entries);
- /* tcp/udp only. uid.op always 0 in other cases */
- else if (r->uid.op && (pd->lookup.done || (pd->lookup.done =
- pf_socket_lookup(pd, m), 1)) &&
- !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1],
- pd->lookup.uid))
- r = TAILQ_NEXT(r, entries);
- /* tcp/udp only. gid.op always 0 in other cases */
- else if (r->gid.op && (pd->lookup.done || (pd->lookup.done =
- pf_socket_lookup(pd, m), 1)) &&
- !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1],
- pd->lookup.gid))
- r = TAILQ_NEXT(r, entries);
- else if (r->prio &&
- !pf_match_ieee8021q_pcp(r->prio, m))
- r = TAILQ_NEXT(r, entries);
- else if (r->prob &&
- r->prob <= arc4random())
- r = TAILQ_NEXT(r, entries);
- else if (r->match_tag && !pf_match_tag(m, r, &tag,
- pd->pf_mtag ? pd->pf_mtag->tag : 0))
- r = TAILQ_NEXT(r, entries);
- else if (r->rcv_kif && !pf_match_rcvif(m, r))
- r = TAILQ_NEXT(r, entries);
- else if (r->os_fingerprint != PF_OSFP_ANY &&
- (pd->proto != IPPROTO_TCP || !pf_osfp_match(
+ PF_TEST_ATTRIB(pfi_kkif_match(r->kif, kif) == r->ifnot,
+ r->skip[PF_SKIP_IFP].ptr);
+ PF_TEST_ATTRIB(r->direction && r->direction != pd->dir,
+ r->skip[PF_SKIP_DIR].ptr);
+ PF_TEST_ATTRIB(r->af && r->af != af,
+ r->skip[PF_SKIP_AF].ptr);
+ PF_TEST_ATTRIB(r->proto && r->proto != pd->proto,
+ r->skip[PF_SKIP_PROTO].ptr);
+ PF_TEST_ATTRIB(PF_MISMATCHAW(&r->src.addr, saddr, af,
+ r->src.neg, kif, M_GETFIB(m)),
+ r->skip[PF_SKIP_SRC_ADDR].ptr);
+ PF_TEST_ATTRIB(PF_MISMATCHAW(&r->dst.addr, daddr, af,
+ r->dst.neg, NULL, M_GETFIB(m)),
+ r->skip[PF_SKIP_DST_ADDR].ptr);
+ switch (pd->virtual_proto) {
+ case PF_VPROTO_FRAGMENT:
+ /* tcp/udp only. port_op always 0 in other cases */
+ PF_TEST_ATTRIB((r->src.port_op || r->dst.port_op),
+ TAILQ_NEXT(r, entries));
+ PF_TEST_ATTRIB((pd->proto == IPPROTO_TCP && r->flagset),
+ TAILQ_NEXT(r, entries));
+ /* icmp only. type/code always 0 in other cases */
+ PF_TEST_ATTRIB((r->type || r->code),
+ TAILQ_NEXT(r, entries));
+ /* tcp/udp only. {uid|gid}.op always 0 in other cases */
+ PF_TEST_ATTRIB((r->gid.op || r->uid.op),
+ TAILQ_NEXT(r, entries));
+ break;
+
+ case IPPROTO_TCP:
+ PF_TEST_ATTRIB((r->flagset & th->th_flags) != r->flags,
+ TAILQ_NEXT(r, entries));
+ /* FALLTHROUGH */
+ case IPPROTO_SCTP:
+ case IPPROTO_UDP:
+ /* tcp/udp only. port_op always 0 in other cases */
+ PF_TEST_ATTRIB(r->src.port_op && !pf_match_port(r->src.port_op,
+ r->src.port[0], r->src.port[1], sport),
+ r->skip[PF_SKIP_SRC_PORT].ptr);
+ /* tcp/udp only. port_op always 0 in other cases */
+ PF_TEST_ATTRIB(r->dst.port_op && !pf_match_port(r->dst.port_op,
+ r->dst.port[0], r->dst.port[1], dport),
+ r->skip[PF_SKIP_DST_PORT].ptr);
+ /* tcp/udp only. uid.op always 0 in other cases */
+ PF_TEST_ATTRIB(r->uid.op && (pd->lookup.done || (pd->lookup.done =
+ pf_socket_lookup(pd, m), 1)) &&
+ !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1],
+ pd->lookup.uid),
+ TAILQ_NEXT(r, entries));
+ /* tcp/udp only. gid.op always 0 in other cases */
+ PF_TEST_ATTRIB(r->gid.op && (pd->lookup.done || (pd->lookup.done =
+ pf_socket_lookup(pd, m), 1)) &&
+ !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1],
+ pd->lookup.gid),
+ TAILQ_NEXT(r, entries));
+ break;
+
+ case IPPROTO_ICMP:
+ case IPPROTO_ICMPV6:
+ /* icmp only. type always 0 in other cases */
+ PF_TEST_ATTRIB(r->type && r->type != icmptype + 1,
+ TAILQ_NEXT(r, entries));
+ /* icmp only. type always 0 in other cases */
+ PF_TEST_ATTRIB(r->code && r->code != icmpcode + 1,
+ TAILQ_NEXT(r, entries));
+ break;
+
+ default:
+ break;
+ }
+ PF_TEST_ATTRIB(r->tos && !(r->tos == pd->tos),
+ TAILQ_NEXT(r, entries));
+ PF_TEST_ATTRIB(r->prio &&
+ !pf_match_ieee8021q_pcp(r->prio, m),
+ TAILQ_NEXT(r, entries));
+ PF_TEST_ATTRIB(r->prob &&
+ r->prob <= arc4random(),
+ TAILQ_NEXT(r, entries));
+ PF_TEST_ATTRIB(r->match_tag && !pf_match_tag(m, r, &tag,
+ pd->pf_mtag ? pd->pf_mtag->tag : 0),
+ TAILQ_NEXT(r, entries));
+ PF_TEST_ATTRIB(r->rcv_kif && !pf_match_rcvif(m, r),
+ TAILQ_NEXT(r, entries));
+ PF_TEST_ATTRIB((r->rule_flag & PFRULE_FRAGMENT &&
+ pd->virtual_proto != PF_VPROTO_FRAGMENT),
+ TAILQ_NEXT(r, entries));
+ PF_TEST_ATTRIB(r->os_fingerprint != PF_OSFP_ANY &&
+ (pd->virtual_proto != IPPROTO_TCP || !pf_osfp_match(
pf_osfp_fingerprint(pd, m, off, th),
- r->os_fingerprint)))
- r = TAILQ_NEXT(r, entries);
- else {
- if (r->tag)
- tag = r->tag;
- if (r->anchor == NULL) {
- if (r->action == PF_MATCH) {
- ri = malloc(sizeof(struct pf_krule_item), M_PF_RULE_ITEM, M_NOWAIT | M_ZERO);
- if (ri == NULL) {
- REASON_SET(&reason, PFRES_MEMORY);
- goto cleanup;
- }
- ri->r = r;
- SLIST_INSERT_HEAD(&match_rules, ri, entry);
- pf_counter_u64_critical_enter();
- pf_counter_u64_add_protected(&r->packets[pd->dir == PF_OUT], 1);
- pf_counter_u64_add_protected(&r->bytes[pd->dir == PF_OUT], pd->tot_len);
- pf_counter_u64_critical_exit();
- pf_rule_to_actions(r, &pd->act);
- if (r->log || pd->act.log & PF_LOG_MATCHES)
- PFLOG_PACKET(kif, m,
- r->action, PFRES_MATCH, r,
- a, ruleset, pd, 1);
- } else {
- match = 1;
- *rm = r;
- *am = a;
- *rsm = ruleset;
- if (pd->act.log & PF_LOG_MATCHES)
- PFLOG_PACKET(kif, m,
- r->action, PFRES_MATCH, r,
- a, ruleset, pd, 1);
+ r->os_fingerprint)),
+ TAILQ_NEXT(r, entries));
+ /* FALLTHROUGH */
+ if (r->tag)
+ tag = r->tag;
+ if (r->anchor == NULL) {
+ if (r->action == PF_MATCH) {
+ ri = malloc(sizeof(struct pf_krule_item), M_PF_RULE_ITEM, M_NOWAIT | M_ZERO);
+ if (ri == NULL) {
+ REASON_SET(&reason, PFRES_MEMORY);
+ goto cleanup;
}
- if ((*rm)->quick)
- break;
- r = TAILQ_NEXT(r, entries);
- } else
- pf_step_into_anchor(anchor_stack, &asd,
- &ruleset, PF_RULESET_FILTER, &r, &a,
- &match);
- }
+ ri->r = r;
+ SLIST_INSERT_HEAD(&match_rules, ri, entry);
+ pf_counter_u64_critical_enter();
+ pf_counter_u64_add_protected(&r->packets[pd->dir == PF_OUT], 1);
+ pf_counter_u64_add_protected(&r->bytes[pd->dir == PF_OUT], pd->tot_len);
+ pf_counter_u64_critical_exit();
+ pf_rule_to_actions(r, &pd->act);
+ if (r->log || pd->act.log & PF_LOG_MATCHES)
+ PFLOG_PACKET(kif, m,
+ r->action, PFRES_MATCH, r,
+ a, ruleset, pd, 1);
+ } else {
+ match = 1;
+ *rm = r;
+ *am = a;
+ *rsm = ruleset;
+ if (pd->act.log & PF_LOG_MATCHES)
+ PFLOG_PACKET(kif, m,
+ r->action, PFRES_MATCH, r,
+ a, ruleset, pd, 1);
+ }
+ if ((*rm)->quick)
+ break;
+ r = TAILQ_NEXT(r, entries);
+ } else
+ pf_step_into_anchor(anchor_stack, &asd,
+ &ruleset, PF_RULESET_FILTER, &r, &a,
+ &match);
+nextrule:
if (r == NULL && pf_step_out_of_anchor(anchor_stack, &asd,
&ruleset, PF_RULESET_FILTER, &r, &a, &match))
break;
@@ -5252,7 +5285,8 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif,
PFLOG_PACKET(kif, m, r->action, reason, r, a, ruleset, pd, 1);
}
- if ((r->action == PF_DROP) &&
+ if (pd->virtual_proto != PF_VPROTO_FRAGMENT &&
+ (r->action == PF_DROP) &&
((r->rule_flag & PFRULE_RETURNRST) ||
(r->rule_flag & PFRULE_RETURNICMP) ||
(r->rule_flag & PFRULE_RETURN))) {
@@ -5270,8 +5304,9 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif,
if (pd->act.rtableid >= 0)
M_SETFIB(m, pd->act.rtableid);
- if (!state_icmp && (r->keep_state || nr != NULL ||
- (pd->flags & PFDESC_TCP_NORM))) {
+ if (pd->virtual_proto != PF_VPROTO_FRAGMENT &&
+ (!state_icmp && (r->keep_state || nr != NULL ||
+ (pd->flags & PFDESC_TCP_NORM)))) {
int action;
action = pf_create_state(r, nr, a, pd, nsn, nk, sk, m, off,
sport, dport, &rewrite, kif, sm, tag, bproto_sum, bip_sum,
@@ -5596,133 +5631,6 @@ drop:
}
static int
-pf_test_fragment(struct pf_krule **rm, struct pfi_kkif *kif,
- struct mbuf *m, struct pf_pdesc *pd, struct pf_krule **am,
- struct pf_kruleset **rsm)
-{
- struct pf_krule *r, *a = NULL;
- struct pf_kruleset *ruleset = NULL;
- struct pf_krule_slist match_rules;
- struct pf_krule_item *ri;
- sa_family_t af = pd->af;
- u_short reason;
- int tag = -1;
- int asd = 0;
- int match = 0;
- struct pf_kanchor_stackframe anchor_stack[PF_ANCHOR_STACKSIZE];
-
- PF_RULES_RASSERT();
-
- r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr);
- SLIST_INIT(&match_rules);
- while (r != NULL) {
- pf_counter_u64_add(&r->evaluations, 1);
- if (pfi_kkif_match(r->kif, kif) == r->ifnot)
- r = r->skip[PF_SKIP_IFP].ptr;
- else if (r->direction && r->direction != pd->dir)
- r = r->skip[PF_SKIP_DIR].ptr;
- else if (r->af && r->af != af)
- r = r->skip[PF_SKIP_AF].ptr;
- else if (r->proto && r->proto != pd->proto)
- r = r->skip[PF_SKIP_PROTO].ptr;
- else if (PF_MISMATCHAW(&r->src.addr, pd->src, af,
- r->src.neg, kif, M_GETFIB(m)))
- r = r->skip[PF_SKIP_SRC_ADDR].ptr;
- else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af,
- r->dst.neg, NULL, M_GETFIB(m)))
- r = r->skip[PF_SKIP_DST_ADDR].ptr;
- else if (r->tos && !(r->tos == pd->tos))
- r = TAILQ_NEXT(r, entries);
- else if (r->os_fingerprint != PF_OSFP_ANY)
- r = TAILQ_NEXT(r, entries);
- else if (pd->proto == IPPROTO_UDP &&
- (r->src.port_op || r->dst.port_op))
- r = TAILQ_NEXT(r, entries);
- else if (pd->proto == IPPROTO_TCP &&
- (r->src.port_op || r->dst.port_op || r->flagset))
- r = TAILQ_NEXT(r, entries);
- else if ((pd->proto == IPPROTO_ICMP ||
- pd->proto == IPPROTO_ICMPV6) &&
- (r->type || r->code))
- r = TAILQ_NEXT(r, entries);
- else if (r->prio &&
- !pf_match_ieee8021q_pcp(r->prio, m))
- r = TAILQ_NEXT(r, entries);
- else if (r->prob && r->prob <=
- (arc4random() % (UINT_MAX - 1) + 1))
- r = TAILQ_NEXT(r, entries);
- else if (r->match_tag && !pf_match_tag(m, r, &tag,
- pd->pf_mtag ? pd->pf_mtag->tag : 0))
- r = TAILQ_NEXT(r, entries);
- else {
- if (r->anchor == NULL) {
- if (r->action == PF_MATCH) {
- ri = malloc(sizeof(struct pf_krule_item), M_PF_RULE_ITEM, M_NOWAIT | M_ZERO);
- if (ri == NULL) {
- REASON_SET(&reason, PFRES_MEMORY);
- goto cleanup;
- }
- ri->r = r;
- SLIST_INSERT_HEAD(&match_rules, ri, entry);
- pf_counter_u64_critical_enter();
- pf_counter_u64_add_protected(&r->packets[pd->dir == PF_OUT], 1);
- pf_counter_u64_add_protected(&r->bytes[pd->dir == PF_OUT], pd->tot_len);
- pf_counter_u64_critical_exit();
- pf_rule_to_actions(r, &pd->act);
- if (r->log)
- PFLOG_PACKET(kif, m,
- r->action, PFRES_MATCH, r,
- a, ruleset, pd, 1);
- } else {
- match = 1;
- *rm = r;
- *am = a;
- *rsm = ruleset;
- }
- if ((*rm)->quick)
- break;
- r = TAILQ_NEXT(r, entries);
- } else
- pf_step_into_anchor(anchor_stack, &asd,
- &ruleset, PF_RULESET_FILTER, &r, &a,
- &match);
- }
- if (r == NULL && pf_step_out_of_anchor(anchor_stack, &asd,
- &ruleset, PF_RULESET_FILTER, &r, &a, &match))
- break;
- }
- r = *rm;
- a = *am;
- ruleset = *rsm;
-
- REASON_SET(&reason, PFRES_MATCH);
-
- /* apply actions for last matching pass/block rule */
- pf_rule_to_actions(r, &pd->act);
-
- if (r->log)
- PFLOG_PACKET(kif, m, r->action, reason, r, a, ruleset, pd, 1);
-
- if (r->action != PF_PASS)
- return (PF_DROP);
-
- if (tag > 0 && pf_tag_packet(m, pd, tag)) {
- REASON_SET(&reason, PFRES_MEMORY);
- goto cleanup;
- }
-
- return (PF_PASS);
-
-cleanup:
- while ((ri = SLIST_FIRST(&match_rules))) {
- SLIST_REMOVE_HEAD(&match_rules, entry);
- free(ri, M_PF_RULE_ITEM);
- }
-
- return (PF_DROP);
-}
-
-static int
pf_tcp_track_full(struct pf_kstate **state, struct pfi_kkif *kif,
struct mbuf *m, int off, struct pf_pdesc *pd, u_short *reason,
int *copyback)
@@ -8556,7 +8464,8 @@ pf_dummynet_route(struct pf_pdesc *pd, struct pf_kstate *s,
static int
pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf *m,
u_short *action, u_short *reason, struct pfi_kkif *kif, struct pf_krule **a,
- struct pf_krule **r, struct pf_kruleset **ruleset, int *off, int *hdrlen,
+ struct pf_krule **r, struct pf_kstate **s, struct pf_kruleset **ruleset,
+ int *off, int *hdrlen, struct inpcb *inp,
struct pf_rule_actions *default_actions)
{
@@ -8589,7 +8498,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf *m,
pd->sport = pd->dport = NULL;
pd->ip_sum = &h->ip_sum;
pd->proto_sum = NULL;
- pd->proto = h->ip_p;
+ pd->virtual_proto = pd->proto = h->ip_p;
pd->dir = dir;
pd->sidx = (dir == PF_IN) ? 0 : 1;
pd->didx = (dir == PF_IN) ? 1 : 0;
@@ -8600,9 +8509,21 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf *m,
if (h->ip_hl > 5) /* has options */
pd->badopts++;
- /* fragments not reassembled handled later */
- if (h->ip_off & htons(IP_MF | IP_OFFMASK))
- return (0);
+ if (h->ip_off & htons(IP_MF | IP_OFFMASK)) {
+ /*
+ * handle fragments that aren't reassembled by
+ * normalization
+ */
+ pd->virtual_proto = PF_VPROTO_FRAGMENT;
+ if (kif == NULL || r == NULL) /* pflog */
+ *action = PF_DROP;
+ else
+ *action = pf_test_rule(r, s, kif, m, *off,
+ pd, a, ruleset, inp, *hdrlen);
+ if (*action != PF_PASS)
+ REASON_SET(reason, PFRES_FRAG);
+ return (-1);
+ }
break;
}
@@ -8624,17 +8545,19 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf *m,
pd->tos = IPV6_DSCP(h);
pd->tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr);
*off = ((caddr_t)h - m->m_data) + sizeof(struct ip6_hdr);
- pd->proto = h->ip6_nxt;
+ pd->virtual_proto = pd->proto = h->ip6_nxt;
pd->act.rtableid = -1;
do {
switch (pd->proto) {
case IPPROTO_FRAGMENT:
+ pd->virtual_proto = PF_VPROTO_FRAGMENT;
if (kif == NULL || r == NULL) /* pflog */
*action = PF_DROP;
else
- *action = pf_test_fragment(r, kif,
- m, pd, a, ruleset);
+ *action = pf_test_rule(r, s, kif, m, *off,
+ pd, a, ruleset, inp,
+ *hdrlen);
if (*action == PF_DROP)
REASON_SET(reason, PFRES_FRAG);
return (-1);
@@ -8682,7 +8605,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf *m,
*off += (opt6.ip6e_len + 2) * 4;
else
*off += (opt6.ip6e_len + 1) * 8;
- pd->proto = opt6.ip6e_nxt;
+ pd->virtual_proto = pd->proto = opt6.ip6e_nxt;
/* goto the next header */
break;
}
@@ -9000,7 +8923,7 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
}
if (pf_setup_pdesc(af, dir, &pd, m, &action, &reason, kif, &a, &r,
- &ruleset, &off, &hdrlen, default_actions) == -1) {
+ &s, &ruleset, &off, &hdrlen, inp, default_actions) == -1) {
if (action != PF_PASS)
pd.act.log |= PF_LOG_FORCE;
goto done;
@@ -9060,32 +8983,17 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
m_tag_delete(m, mtag);
}
- switch (af) {
-#ifdef INET
- case AF_INET:
- /* handle fragments that didn't get reassembled by normalization */
- if (h->ip_off & htons(IP_MF | IP_OFFMASK)) {
- action = pf_test_fragment(&r, kif, m, &pd, &a, &ruleset);
- goto done;
- }
- break;
-#endif
#ifdef INET6
- case AF_INET6:
- /*
- * we do not support jumbogram. if we keep going, zero ip6_plen
- * will do something bad, so drop the packet for now.
- */
- if (htons(h6->ip6_plen) == 0) {
- action = PF_DROP;
- REASON_SET(&reason, PFRES_NORM); /*XXX*/
- goto done;
- }
- break;
-#endif
- default:
- panic("Unknown af %d", af);
+ /*
+ * we do not support jumbogram. if we keep going, zero ip6_plen
+ * will do something bad, so drop the packet for now.
+ */
+ if (af == AF_INET6 && htons(h6->ip6_plen) == 0) {
+ action = PF_DROP;
+ REASON_SET(&reason, PFRES_NORM); /*XXX*/
+ goto done;
}
+#endif
switch (pd.proto) {
case IPPROTO_TCP: {
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
index 063abe83274b..77d4ea6c5e70 100644
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -491,8 +491,8 @@ struct pf_rule {
#define PF_SKIP_AF 2
#define PF_SKIP_PROTO 3
#define PF_SKIP_SRC_ADDR 4
-#define PF_SKIP_SRC_PORT 5
-#define PF_SKIP_DST_ADDR 6
+#define PF_SKIP_DST_ADDR 5
+#define PF_SKIP_SRC_PORT 6
#define PF_SKIP_DST_PORT 7
#define PF_SKIP_COUNT 8
union pf_rule_ptr skip[PF_SKIP_COUNT];