aboutsummaryrefslogtreecommitdiff
path: root/sys/netpfil/pf/pf.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netpfil/pf/pf.c')
-rw-r--r--sys/netpfil/pf/pf.c364
1 files changed, 251 insertions, 113 deletions
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 127b29320acb..c669be47b063 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -119,8 +119,6 @@
#include <machine/in_cksum.h>
#include <security/mac/mac_framework.h>
-#define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x
-
SDT_PROVIDER_DEFINE(pf);
SDT_PROBE_DEFINE2(pf, , test, reason_set, "int", "int");
SDT_PROBE_DEFINE4(pf, ip, test, done, "int", "int", "struct pf_krule *",
@@ -161,6 +159,7 @@ SDT_PROBE_DEFINE2(pf, eth, test_rule, match, "int", "struct pf_keth_rule *");
SDT_PROBE_DEFINE2(pf, eth, test_rule, final_match,
"int", "struct pf_keth_rule *");
SDT_PROBE_DEFINE2(pf, purge, state, rowcount, "int", "size_t");
+SDT_PROBE_DEFINE2(pf, , log, log, "int", "const char *");
/*
* Global variables
@@ -375,6 +374,8 @@ static u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t,
int, u_int16_t);
static int pf_check_proto_cksum(struct mbuf *, int, int,
u_int8_t, sa_family_t);
+static int pf_walk_option(struct pf_pdesc *, struct ip *,
+ int, int, u_short *);
static int pf_walk_header(struct pf_pdesc *, struct ip *, u_short *);
#ifdef INET6
static int pf_walk_option6(struct pf_pdesc *, struct ip6_hdr *,
@@ -4579,7 +4580,7 @@ pf_match_port(u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p)
static int
pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u)
{
- if (u == UID_MAX && op != PF_OP_EQ && op != PF_OP_NE)
+ if (u == -1 && op != PF_OP_EQ && op != PF_OP_NE)
return (0);
return (pf_match(op, a1, a2, u));
}
@@ -4587,7 +4588,7 @@ pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u)
static int
pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g)
{
- if (g == GID_MAX && op != PF_OP_EQ && op != PF_OP_NE)
+ if (g == -1 && op != PF_OP_EQ && op != PF_OP_NE)
return (0);
return (pf_match(op, a1, a2, g));
}
@@ -4615,8 +4616,8 @@ pf_match_rcvif(struct mbuf *m, struct pf_krule *r)
if (kif == NULL) {
DPFPRINTF(PF_DEBUG_URGENT,
- ("%s: kif == NULL, @%d via %s\n", __func__, r->nr,
- r->rcv_ifname));
+ "%s: kif == NULL, @%d via %s", __func__, r->nr,
+ r->rcv_ifname);
return (0);
}
@@ -4914,8 +4915,8 @@ pf_socket_lookup(struct pf_pdesc *pd)
struct inpcbinfo *pi;
struct inpcb *inp;
- pd->lookup.uid = UID_MAX;
- pd->lookup.gid = GID_MAX;
+ pd->lookup.uid = -1;
+ pd->lookup.gid = -1;
switch (pd->proto) {
case IPPROTO_TCP:
@@ -5242,8 +5243,8 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
if (__predict_false(m->m_len < sizeof(struct ether_header)) &&
(m = *m0 = m_pullup(*m0, sizeof(struct ether_header))) == NULL) {
DPFPRINTF(PF_DEBUG_URGENT,
- ("%s: m_len < sizeof(struct ether_header)"
- ", pullup failed\n", __func__));
+ "%s: m_len < sizeof(struct ether_header)"
+ ", pullup failed", __func__);
return (PF_DROP);
}
e = mtod(m, struct ether_header *);
@@ -5901,18 +5902,17 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
M_SETFIB(pd->m, pd->act.rtableid);
if (r->rt) {
- struct pf_ksrc_node *sn = NULL;
- struct pf_srchash *snh = NULL;
/*
* Set act.rt here instead of in pf_rule_to_actions() because
* it is applied only from the last pass rule.
*/
pd->act.rt = r->rt;
- /* Don't use REASON_SET, pf_map_addr increases the reason counters */
- ctx.reason = pf_map_addr_sn(pd->af, r, pd->src, &pd->act.rt_addr,
- &pd->act.rt_kif, NULL, &sn, &snh, &(r->route), PF_SN_ROUTE);
- if (ctx.reason != 0)
+ if ((transerror = pf_map_addr_sn(pd->af, r, pd->src,
+ &pd->act.rt_addr, &pd->act.rt_kif, NULL, &(r->route),
+ PF_SN_ROUTE)) != PFRES_MATCH) {
+ REASON_SET(&ctx.reason, transerror);
goto cleanup;
+ }
}
if (pd->virtual_proto != PF_VPROTO_FRAGMENT &&
@@ -6056,9 +6056,16 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx,
/* src node for translation rule */
if (ctx->nr != NULL) {
KASSERT(ctx->nat_pool != NULL, ("%s: nat_pool is NULL", __func__));
+ /*
+ * The NAT addresses are chosen during ruleset parsing.
+ * The new afto code stores post-nat addresses in nsaddr.
+ * The old nat code (also used for new nat-to rules) creates
+ * state keys and stores addresses in them.
+ */
if ((ctx->nat_pool->opts & PF_POOL_STICKYADDR) &&
(sn_reason = pf_insert_src_node(sns, snhs, ctx->nr,
- &ctx->sk->addr[pd->sidx], pd->af, &ctx->nk->addr[1], NULL,
+ ctx->sk ? &(ctx->sk->addr[pd->sidx]) : pd->src, pd->af,
+ ctx->nk ? &(ctx->nk->addr[1]) : &(pd->nsaddr), NULL,
PF_SN_NAT)) != 0 ) {
REASON_SET(&ctx->reason, sn_reason);
goto csfailed;
@@ -6162,8 +6169,8 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx,
&s->src, &s->dst, &ctx->rewrite)) {
/* This really shouldn't happen!!! */
DPFPRINTF(PF_DEBUG_URGENT,
- ("%s: tcp normalize failed on first "
- "pkt\n", __func__));
+ "%s: tcp normalize failed on first "
+ "pkt", __func__);
goto csfailed;
}
} else if (pd->proto == IPPROTO_SCTP) {
@@ -6213,7 +6220,7 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx,
if (ctx->tag > 0)
s->tag = ctx->tag;
if (pd->proto == IPPROTO_TCP && (tcp_get_flags(th) & (TH_SYN|TH_ACK)) ==
- TH_SYN && r->keep_state == PF_STATE_SYNPROXY) {
+ TH_SYN && r->keep_state == PF_STATE_SYNPROXY && pd->dir == PF_IN) {
pf_set_protostate(s, PF_PEER_SRC, PF_TCPS_PROXY_SRC);
pf_undo_nat(ctx->nr, pd, bip_sum);
s->src.seqhi = arc4random();
@@ -7959,8 +7966,8 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
if (!pf_pull_hdr(pd->m, ipoff2, &h2, sizeof(h2),
NULL, reason, pd2.af)) {
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: ICMP error message too short "
- "(ip)\n"));
+ "pf: ICMP error message too short "
+ "(ip)");
return (PF_DROP);
}
/*
@@ -7990,8 +7997,8 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
if (!pf_pull_hdr(pd->m, ipoff2, &h2_6, sizeof(h2_6),
NULL, reason, pd2.af)) {
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: ICMP error message too short "
- "(ip6)\n"));
+ "pf: ICMP error message too short "
+ "(ip6)");
return (PF_DROP);
}
pd2.off = ipoff2;
@@ -8043,8 +8050,8 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
if (!pf_pull_hdr(pd->m, pd2.off, th, 8, NULL, reason,
pd2.af)) {
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: ICMP error message too short "
- "(tcp)\n"));
+ "pf: ICMP error message too short "
+ "(tcp)");
return (PF_DROP);
}
pd2.pcksum = &pd2.hdr.tcp.th_sum;
@@ -8238,8 +8245,8 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
if (!pf_pull_hdr(pd->m, pd2.off, uh, sizeof(*uh),
NULL, reason, pd2.af)) {
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: ICMP error message too short "
- "(udp)\n"));
+ "pf: ICMP error message too short "
+ "(udp)");
return (PF_DROP);
}
pd2.pcksum = &pd2.hdr.udp.uh_sum;
@@ -8370,8 +8377,8 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
if (! pf_pull_hdr(pd->m, pd2.off, sh, sizeof(*sh), NULL, reason,
pd2.af)) {
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: ICMP error message too short "
- "(sctp)\n"));
+ "pf: ICMP error message too short "
+ "(sctp)");
return (PF_DROP);
}
pd2.pcksum = &pd2.sctp_dummy_sum;
@@ -8401,8 +8408,8 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
if (src->scrub->pfss_v_tag != sh->v_tag) {
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: ICMP error message has incorrect "
- "SCTP v_tag\n"));
+ "pf: ICMP error message has incorrect "
+ "SCTP v_tag");
return (PF_DROP);
}
@@ -8525,8 +8532,8 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
if (!pf_pull_hdr(pd->m, pd2.off, iih, ICMP_MINLEN,
NULL, reason, pd2.af)) {
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: ICMP error message too short i"
- "(icmp)\n"));
+ "pf: ICMP error message too short i"
+ "(icmp)");
return (PF_DROP);
}
pd2.pcksum = &pd2.hdr.icmp.icmp_cksum;
@@ -8645,8 +8652,8 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
if (!pf_pull_hdr(pd->m, pd2.off, iih,
sizeof(struct icmp6_hdr), NULL, reason, pd2.af)) {
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: ICMP error message too short "
- "(icmp6)\n"));
+ "pf: ICMP error message too short "
+ "(icmp6)");
return (PF_DROP);
}
pd2.pcksum = &pd2.hdr.icmp6.icmp6_cksum;
@@ -9062,6 +9069,9 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
goto bad;
}
+ if (r->rt == PF_DUPTO)
+ skip_test = true;
+
if (pd->dir == PF_IN && !skip_test) {
if (pf_test(AF_INET, PF_OUT, PFIL_FWD, ifp, &m0, inp,
&pd->act) != PF_PASS) {
@@ -9073,7 +9083,7 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
}
if (m0->m_len < sizeof(struct ip)) {
DPFPRINTF(PF_DEBUG_URGENT,
- ("%s: m0->m_len < sizeof(struct ip)\n", __func__));
+ "%s: m0->m_len < sizeof(struct ip)", __func__);
SDT_PROBE1(pf, ip, route_to, drop, __LINE__);
goto bad;
}
@@ -9364,6 +9374,9 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
goto bad;
}
+ if (r->rt == PF_DUPTO)
+ skip_test = true;
+
if (pd->dir == PF_IN && !skip_test) {
if (pf_test(AF_INET6, PF_OUT, PFIL_FWD | PF_PFIL_NOREFRAGMENT,
ifp, &m0, inp, &pd->act) != PF_PASS) {
@@ -9375,8 +9388,8 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
}
if (m0->m_len < sizeof(struct ip6_hdr)) {
DPFPRINTF(PF_DEBUG_URGENT,
- ("%s: m0->m_len < sizeof(struct ip6_hdr)\n",
- __func__));
+ "%s: m0->m_len < sizeof(struct ip6_hdr)",
+ __func__);
SDT_PROBE1(pf, ip6, route_to, drop, __LINE__);
goto bad;
}
@@ -9671,7 +9684,7 @@ pf_test_eth(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0,
if (kif == NULL) {
DPFPRINTF(PF_DEBUG_URGENT,
- ("%s: kif == NULL, if_xname %s\n", __func__, ifp->if_xname));
+ "%s: kif == NULL, if_xname %s", __func__, ifp->if_xname);
return (PF_DROP);
}
if (kif->pfik_flags & PFI_IFLAG_SKIP)
@@ -9786,6 +9799,62 @@ pf_dummynet_route(struct pf_pdesc *pd, struct pf_kstate *s,
}
static int
+pf_walk_option(struct pf_pdesc *pd, struct ip *h, int off, int end,
+ u_short *reason)
+{
+ uint8_t type, length, opts[15 * 4 - sizeof(struct ip)];
+
+ /* IP header in payload of ICMP packet may be too short */
+ if (pd->m->m_pkthdr.len < end) {
+ DPFPRINTF(PF_DEBUG_MISC, "IP option too short");
+ REASON_SET(reason, PFRES_SHORT);
+ return (PF_DROP);
+ }
+
+ MPASS(end - off <= sizeof(opts));
+ m_copydata(pd->m, off, end - off, opts);
+ end -= off;
+ off = 0;
+
+ while (off < end) {
+ type = opts[off];
+ if (type == IPOPT_EOL)
+ break;
+ if (type == IPOPT_NOP) {
+ off++;
+ continue;
+ }
+ if (off + 2 > end) {
+ DPFPRINTF(PF_DEBUG_MISC, "IP length opt");
+ REASON_SET(reason, PFRES_IPOPTIONS);
+ return (PF_DROP);
+ }
+ length = opts[off + 1];
+ if (length < 2) {
+ DPFPRINTF(PF_DEBUG_MISC, "IP short opt");
+ REASON_SET(reason, PFRES_IPOPTIONS);
+ return (PF_DROP);
+ }
+ if (off + length > end) {
+ DPFPRINTF(PF_DEBUG_MISC, "IP long opt");
+ REASON_SET(reason, PFRES_IPOPTIONS);
+ return (PF_DROP);
+ }
+ switch (type) {
+ case IPOPT_RA:
+ pd->badopts |= PF_OPT_ROUTER_ALERT;
+ break;
+ default:
+ pd->badopts |= PF_OPT_OTHER;
+ break;
+ }
+ off += length;
+ }
+
+ return (PF_PASS);
+}
+
+static int
pf_walk_header(struct pf_pdesc *pd, struct ip *h, u_short *reason)
{
struct ah ext;
@@ -9797,11 +9866,28 @@ pf_walk_header(struct pf_pdesc *pd, struct ip *h, u_short *reason)
REASON_SET(reason, PFRES_SHORT);
return (PF_DROP);
}
- if (hlen != sizeof(struct ip))
- pd->badopts++;
+ if (hlen != sizeof(struct ip)) {
+ if (pf_walk_option(pd, h, pd->off + sizeof(struct ip),
+ pd->off + hlen, reason) != PF_PASS)
+ return (PF_DROP);
+ /* header options which contain only padding is fishy */
+ if (pd->badopts == 0)
+ pd->badopts |= PF_OPT_OTHER;
+ }
end = pd->off + ntohs(h->ip_len);
pd->off += hlen;
pd->proto = h->ip_p;
+ /* IGMP packets have router alert options, allow them */
+ if (pd->proto == IPPROTO_IGMP) {
+ /* According to RFC 1112 ttl must be set to 1. */
+ if ((h->ip_ttl != 1) ||
+ !IN_MULTICAST(ntohl(h->ip_dst.s_addr))) {
+ DPFPRINTF(PF_DEBUG_MISC, "Invalid IGMP");
+ REASON_SET(reason, PFRES_IPOPTIONS);
+ return (PF_DROP);
+ }
+ pd->badopts &= ~PF_OPT_ROUTER_ALERT;
+ }
/* stop walking over non initial fragments */
if ((h->ip_off & htons(IP_OFFMASK)) != 0)
return (PF_PASS);
@@ -9814,7 +9900,7 @@ pf_walk_header(struct pf_pdesc *pd, struct ip *h, u_short *reason)
return (PF_PASS);
if (!pf_pull_hdr(pd->m, pd->off, &ext, sizeof(ext),
NULL, reason, AF_INET)) {
- DPFPRINTF(PF_DEBUG_MISC, ("IP short exthdr"));
+ DPFPRINTF(PF_DEBUG_MISC, "IP short exthdr");
return (PF_DROP);
}
pd->off += (ext.ah_len + 2) * 4;
@@ -9824,7 +9910,7 @@ pf_walk_header(struct pf_pdesc *pd, struct ip *h, u_short *reason)
return (PF_PASS);
}
}
- DPFPRINTF(PF_DEBUG_MISC, ("IPv4 nested authentication header limit"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv4 nested authentication header limit");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
@@ -9840,7 +9926,7 @@ pf_walk_option6(struct pf_pdesc *pd, struct ip6_hdr *h, int off, int end,
while (off < end) {
if (!pf_pull_hdr(pd->m, off, &opt.ip6o_type,
sizeof(opt.ip6o_type), NULL, reason, AF_INET6)) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short opt type"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 short opt type");
return (PF_DROP);
}
if (opt.ip6o_type == IP6OPT_PAD1) {
@@ -9849,41 +9935,48 @@ pf_walk_option6(struct pf_pdesc *pd, struct ip6_hdr *h, int off, int end,
}
if (!pf_pull_hdr(pd->m, off, &opt, sizeof(opt), NULL,
reason, AF_INET6)) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short opt"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 short opt");
return (PF_DROP);
}
if (off + sizeof(opt) + opt.ip6o_len > end) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 long opt"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 long opt");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
switch (opt.ip6o_type) {
+ case IP6OPT_PADN:
+ break;
case IP6OPT_JUMBO:
+ pd->badopts |= PF_OPT_JUMBO;
if (pd->jumbolen != 0) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 multiple jumbo"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 multiple jumbo");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
if (ntohs(h->ip6_plen) != 0) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 bad jumbo plen"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 bad jumbo plen");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
if (!pf_pull_hdr(pd->m, off, &jumbo, sizeof(jumbo), NULL,
reason, AF_INET6)) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short jumbo"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 short jumbo");
return (PF_DROP);
}
memcpy(&pd->jumbolen, jumbo.ip6oj_jumbo_len,
sizeof(pd->jumbolen));
pd->jumbolen = ntohl(pd->jumbolen);
if (pd->jumbolen < IPV6_MAXPACKET) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short jumbolen"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 short jumbolen");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
break;
+ case IP6OPT_ROUTER_ALERT:
+ pd->badopts |= PF_OPT_ROUTER_ALERT;
+ break;
default:
+ pd->badopts |= PF_OPT_OTHER;
break;
}
off += sizeof(opt) + opt.ip6o_len;
@@ -9897,6 +9990,7 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
{
struct ip6_frag frag;
struct ip6_ext ext;
+ struct icmp6_hdr icmp6;
struct ip6_rthdr rthdr;
uint32_t end;
int hdr_cnt, fraghdr_cnt = 0, rthdr_cnt = 0;
@@ -9908,27 +10002,40 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
for (hdr_cnt = 0; hdr_cnt < PF_HDR_LIMIT; hdr_cnt++) {
switch (pd->proto) {
case IPPROTO_ROUTING:
- case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
- pd->badopts++;
+ pd->badopts |= PF_OPT_OTHER;
+ break;
+ case IPPROTO_HOPOPTS:
+ if (!pf_pull_hdr(pd->m, pd->off, &ext, sizeof(ext),
+ NULL, reason, AF_INET6)) {
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 short exthdr");
+ return (PF_DROP);
+ }
+ if (pf_walk_option6(pd, h, pd->off + sizeof(ext),
+ pd->off + (ext.ip6e_len + 1) * 8,
+ reason) != PF_PASS)
+ return (PF_DROP);
+ /* option header which contains only padding is fishy */
+ if (pd->badopts == 0)
+ pd->badopts |= PF_OPT_OTHER;
break;
}
switch (pd->proto) {
case IPPROTO_FRAGMENT:
if (fraghdr_cnt++) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 multiple fragment"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 multiple fragment");
REASON_SET(reason, PFRES_FRAG);
return (PF_DROP);
}
/* jumbo payload packets cannot be fragmented */
if (pd->jumbolen != 0) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 fragmented jumbo"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 fragmented jumbo");
REASON_SET(reason, PFRES_FRAG);
return (PF_DROP);
}
if (!pf_pull_hdr(pd->m, pd->off, &frag, sizeof(frag),
NULL, reason, AF_INET6)) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short fragment"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 short fragment");
return (PF_DROP);
}
/* stop walking over non initial fragments */
@@ -9944,7 +10051,7 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
break;
case IPPROTO_ROUTING:
if (rthdr_cnt++) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 multiple rthdr"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 multiple rthdr");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
@@ -9956,11 +10063,11 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
}
if (!pf_pull_hdr(pd->m, pd->off, &rthdr, sizeof(rthdr),
NULL, reason, AF_INET6)) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short rthdr"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 short rthdr");
return (PF_DROP);
}
if (rthdr.ip6r_type == IPV6_RTHDR_TYPE_0) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 rthdr0"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 rthdr0");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
@@ -9968,7 +10075,7 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
case IPPROTO_HOPOPTS:
/* RFC2460 4.1: Hop-by-Hop only after IPv6 header */
if (pd->proto == IPPROTO_HOPOPTS && hdr_cnt > 0) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 hopopts not first"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 hopopts not first");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
@@ -9977,7 +10084,7 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
case IPPROTO_DSTOPTS:
if (!pf_pull_hdr(pd->m, pd->off, &ext, sizeof(ext),
NULL, reason, AF_INET6)) {
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short exthdr"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 short exthdr");
return (PF_DROP);
}
/* fragments may be short */
@@ -9989,18 +10096,11 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
/* reassembly needs the ext header before the frag */
if (pd->fragoff == 0)
pd->extoff = pd->off;
- if (pd->proto == IPPROTO_HOPOPTS && pd->fragoff == 0) {
- if (pf_walk_option6(pd, h,
- pd->off + sizeof(ext),
- pd->off + (ext.ip6e_len + 1) * 8, reason)
- != PF_PASS)
- return (PF_DROP);
- if (ntohs(h->ip6_plen) == 0 && pd->jumbolen != 0) {
- DPFPRINTF(PF_DEBUG_MISC,
- ("IPv6 missing jumbo"));
- REASON_SET(reason, PFRES_IPOPTIONS);
- return (PF_DROP);
- }
+ if (pd->proto == IPPROTO_HOPOPTS && pd->fragoff == 0 &&
+ ntohs(h->ip6_plen) == 0 && pd->jumbolen != 0) {
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 missing jumbo");
+ REASON_SET(reason, PFRES_IPOPTIONS);
+ return (PF_DROP);
}
if (pd->proto == IPPROTO_AH)
pd->off += (ext.ip6e_len + 2) * 4;
@@ -10008,10 +10108,45 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
pd->off += (ext.ip6e_len + 1) * 8;
pd->proto = ext.ip6e_nxt;
break;
+ case IPPROTO_ICMPV6:
+ /* fragments may be short, ignore inner header then */
+ if (pd->fragoff != 0 && end < pd->off + sizeof(icmp6)) {
+ pd->off = pd->fragoff;
+ pd->proto = IPPROTO_FRAGMENT;
+ return (PF_PASS);
+ }
+ if (!pf_pull_hdr(pd->m, pd->off, &icmp6, sizeof(icmp6),
+ NULL, reason, AF_INET6)) {
+ DPFPRINTF(PF_DEBUG_MISC,
+ "IPv6 short icmp6hdr");
+ return (PF_DROP);
+ }
+ /* ICMP multicast packets have router alert options */
+ switch (icmp6.icmp6_type) {
+ case MLD_LISTENER_QUERY:
+ case MLD_LISTENER_REPORT:
+ case MLD_LISTENER_DONE:
+ case MLDV2_LISTENER_REPORT:
+ /*
+ * According to RFC 2710 all MLD messages are
+ * sent with hop-limit (ttl) set to 1, and link
+ * local source address. If either one is
+ * missing then MLD message is invalid and
+ * should be discarded.
+ */
+ if ((h->ip6_hlim != 1) ||
+ !IN6_IS_ADDR_LINKLOCAL(&h->ip6_src)) {
+ DPFPRINTF(PF_DEBUG_MISC, "Invalid MLD");
+ REASON_SET(reason, PFRES_IPOPTIONS);
+ return (PF_DROP);
+ }
+ pd->badopts &= ~PF_OPT_ROUTER_ALERT;
+ break;
+ }
+ return (PF_PASS);
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_SCTP:
- case IPPROTO_ICMPV6:
/* fragments may be short, ignore inner header then */
if (pd->fragoff != 0 && end < pd->off +
(pd->proto == IPPROTO_TCP ? sizeof(struct tcphdr) :
@@ -10026,7 +10161,7 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
return (PF_PASS);
}
}
- DPFPRINTF(PF_DEBUG_MISC, ("IPv6 nested extension header limit"));
+ DPFPRINTF(PF_DEBUG_MISC, "IPv6 nested extension header limit");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
@@ -10052,6 +10187,8 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0,
pd->didx = (dir == PF_IN) ? 1 : 0;
pd->af = pd->naf = af;
+ PF_RULES_ASSERT();
+
TAILQ_INIT(&pd->sctp_multihome_jobs);
if (default_actions != NULL)
memcpy(&pd->act, default_actions, sizeof(pd->act));
@@ -10069,8 +10206,15 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0,
if (__predict_false((*m0)->m_len < sizeof(struct ip)) &&
(pd->m = *m0 = m_pullup(*m0, sizeof(struct ip))) == NULL) {
DPFPRINTF(PF_DEBUG_URGENT,
- ("%s: m_len < sizeof(struct ip), pullup failed\n",
- __func__));
+ "%s: m_len < sizeof(struct ip), pullup failed",
+ __func__);
+ *action = PF_DROP;
+ REASON_SET(reason, PFRES_SHORT);
+ return (-1);
+ }
+
+ h = mtod(pd->m, struct ip *);
+ if (pd->m->m_pkthdr.len < ntohs(h->ip_len)) {
*action = PF_DROP;
REASON_SET(reason, PFRES_SHORT);
return (-1);
@@ -10083,13 +10227,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0,
return (-1);
}
*m0 = pd->m;
-
h = mtod(pd->m, struct ip *);
- if (pd->m->m_pkthdr.len < ntohs(h->ip_len)) {
- *action = PF_DROP;
- REASON_SET(reason, PFRES_SHORT);
- return (-1);
- }
if (pf_walk_header(pd, h, reason) != PF_PASS) {
*action = PF_DROP;
@@ -10119,14 +10257,29 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0,
if (__predict_false((*m0)->m_len < sizeof(struct ip6_hdr)) &&
(pd->m = *m0 = m_pullup(*m0, sizeof(struct ip6_hdr))) == NULL) {
DPFPRINTF(PF_DEBUG_URGENT,
- ("%s: m_len < sizeof(struct ip6_hdr)"
- ", pullup failed\n", __func__));
+ "%s: m_len < sizeof(struct ip6_hdr)"
+ ", pullup failed", __func__);
*action = PF_DROP;
REASON_SET(reason, PFRES_SHORT);
return (-1);
}
h = mtod(pd->m, struct ip6_hdr *);
+ if (pd->m->m_pkthdr.len <
+ sizeof(struct ip6_hdr) + ntohs(h->ip6_plen)) {
+ *action = PF_DROP;
+ REASON_SET(reason, PFRES_SHORT);
+ return (-1);
+ }
+
+ /*
+ * we do not support jumbogram. if we keep going, zero ip6_plen
+ * will do something bad, so drop the packet for now.
+ */
+ if (htons(h->ip6_plen) == 0) {
+ *action = PF_DROP;
+ return (-1);
+ }
if (pf_walk_header6(pd, h, reason) != PF_PASS) {
*action = PF_DROP;
@@ -10147,15 +10300,6 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0,
pd->virtual_proto = (pd->fragoff != 0) ?
PF_VPROTO_FRAGMENT : pd->proto;
- /*
- * we do not support jumbogram. if we keep going, zero ip6_plen
- * will do something bad, so drop the packet for now.
- */
- if (htons(h->ip6_plen) == 0) {
- *action = PF_DROP;
- return (-1);
- }
-
/* We do IP header normalization and packet reassembly here */
if (pf_normalize_ip6(pd->fragoff, reason, pd) !=
PF_PASS) {
@@ -10465,35 +10609,30 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
PF_RULES_RLOCK_TRACKER;
KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: bad direction %d\n", __func__, dir));
M_ASSERTPKTHDR(*m0);
+ NET_EPOCH_ASSERT();
if (!V_pf_status.running)
return (PF_PASS);
- PF_RULES_RLOCK();
-
kif = (struct pfi_kkif *)ifp->if_pf_kif;
if (__predict_false(kif == NULL)) {
DPFPRINTF(PF_DEBUG_URGENT,
- ("%s: kif == NULL, if_xname %s\n",
- __func__, ifp->if_xname));
- PF_RULES_RUNLOCK();
+ "%s: kif == NULL, if_xname %s",
+ __func__, ifp->if_xname);
return (PF_DROP);
}
if (kif->pfik_flags & PFI_IFLAG_SKIP) {
- PF_RULES_RUNLOCK();
return (PF_PASS);
}
if ((*m0)->m_flags & M_SKIP_FIREWALL) {
- PF_RULES_RUNLOCK();
return (PF_PASS);
}
if (__predict_false(! M_WRITABLE(*m0))) {
*m0 = m_unshare(*m0, M_NOWAIT);
if (*m0 == NULL) {
- PF_RULES_RUNLOCK();
return (PF_DROP);
}
}
@@ -10506,12 +10645,10 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
ifp = ifnet_byindexgen(pd.pf_mtag->if_index,
pd.pf_mtag->if_idxgen);
if (ifp == NULL || ifp->if_flags & IFF_DYING) {
- PF_RULES_RUNLOCK();
m_freem(*m0);
*m0 = NULL;
return (PF_PASS);
}
- PF_RULES_RUNLOCK();
(ifp->if_output)(ifp, *m0, sintosa(&pd.pf_mtag->dst), NULL);
*m0 = NULL;
return (PF_PASS);
@@ -10526,11 +10663,12 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
/* But only once. We may see the packet multiple times (e.g.
* PFIL_IN/PFIL_OUT). */
pf_dummynet_flag_remove(pd.m, pd.pf_mtag);
- PF_RULES_RUNLOCK();
return (PF_PASS);
}
+ PF_RULES_RLOCK();
+
if (pf_setup_pdesc(af, dir, &pd, m0, &action, &reason,
kif, default_actions) == -1) {
if (action != PF_PASS)
@@ -10685,14 +10823,14 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
action = PF_DROP;
REASON_SET(&reason, PFRES_NORM);
DPFPRINTF(PF_DEBUG_MISC,
- ("dropping IPv6 packet with ICMPv4 payload"));
+ "dropping IPv6 packet with ICMPv4 payload");
break;
}
if (pd.virtual_proto == IPPROTO_ICMPV6 && af != AF_INET6) {
action = PF_DROP;
REASON_SET(&reason, PFRES_NORM);
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: dropping IPv4 packet with ICMPv6 payload\n"));
+ "pf: dropping IPv4 packet with ICMPv6 payload");
break;
}
action = pf_test_state_icmp(&s, &pd, &reason);
@@ -10718,12 +10856,12 @@ done:
if (s)
memcpy(&pd.act, &s->act, sizeof(s->act));
- if (action == PF_PASS && pd.badopts && !pd.act.allow_opts) {
+ if (action == PF_PASS && pd.badopts != 0 && !pd.act.allow_opts) {
action = PF_DROP;
REASON_SET(&reason, PFRES_IPOPTIONS);
pd.act.log = PF_LOG_FORCE;
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: dropping packet with dangerous headers\n"));
+ "pf: dropping packet with dangerous headers");
}
if (pd.act.max_pkt_size && pd.act.max_pkt_size &&
@@ -10732,7 +10870,7 @@ done:
REASON_SET(&reason, PFRES_NORM);
pd.act.log = PF_LOG_FORCE;
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: dropping overly long packet\n"));
+ "pf: dropping overly long packet");
}
if (s) {
@@ -10764,7 +10902,7 @@ done:
REASON_SET(&reason, PFRES_MEMORY);
pd.act.log = PF_LOG_FORCE;
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: failed to allocate 802.1q mtag\n"));
+ "pf: failed to allocate 802.1q mtag");
}
}
@@ -10821,7 +10959,7 @@ done:
REASON_SET(&reason, PFRES_MEMORY);
pd.act.log = PF_LOG_FORCE;
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: failed to allocate tag\n"));
+ "pf: failed to allocate tag");
} else {
pd.pf_mtag->flags |=
PF_MTAG_FLAG_FASTFWD_OURS_PRESENT;
@@ -10838,7 +10976,7 @@ done:
REASON_SET(&reason, PFRES_MEMORY);
pd.act.log = PF_LOG_FORCE;
DPFPRINTF(PF_DEBUG_MISC,
- ("pf: failed to allocate divert tag\n"));
+ "pf: failed to allocate divert tag");
}
}
/* XXX: Anybody working on it?! */