diff options
Diffstat (limited to 'sys/netpfil/pf/pf.c')
-rw-r--r-- | sys/netpfil/pf/pf.c | 922 |
1 files changed, 794 insertions, 128 deletions
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 48da880..ebda220 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -62,6 +62,8 @@ __FBSDID("$FreeBSD$"); #include <net/if.h> #include <net/if_types.h> +#include <net/ethernet.h> +#include <net/if_vlan_var.h> #include <net/route.h> #include <net/radix_mpath.h> #include <net/vnet.h> @@ -86,6 +88,9 @@ __FBSDID("$FreeBSD$"); #include <netinet/udp_var.h> #include <netpfil/ipfw/ip_fw_private.h> /* XXX: only for DIR_IN/DIR_OUT */ +#include <netinet/ip_fw.h> +#include <netinet/ip_dummynet.h> +#include <netinet/ip_divert.h> #ifdef INET6 #include <netinet/ip6.h> @@ -226,6 +231,8 @@ static int pf_state_key_attach(struct pf_state_key *, static void pf_state_key_detach(struct pf_state *, int); static int pf_state_key_ctor(void *, int, void *, int); static u_int32_t pf_tcp_iss(struct pf_pdesc *); +void pf_rule_to_actions(struct pf_rule *, + struct pf_rule_actions *); static int pf_test_rule(struct pf_rule **, struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, struct pf_pdesc *, struct pf_rule **, @@ -258,7 +265,8 @@ static int pf_test_state_icmp(struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, u_short *); static int pf_test_state_other(struct pf_state **, int, - struct pfi_kif *, struct mbuf *, struct pf_pdesc *); + struct pfi_kif *, struct mbuf *, int, + struct pf_pdesc *); static u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t, sa_family_t); static u_int16_t pf_get_mss(struct mbuf *, int, u_int16_t, @@ -281,6 +289,10 @@ static u_int pf_purge_expired_states(u_int, int); static void pf_purge_unlinked_rules(void); static int pf_mtag_uminit(void *, int, int); static void pf_mtag_free(struct m_tag *); +static void pf_packet_redo_nat(struct mbuf *, struct pf_pdesc *, + int, struct pf_state *, int); +static void pf_packet_undo_nat(struct mbuf *, struct pf_pdesc *, + int, struct pf_state *, int); #ifdef INET static void pf_route(struct mbuf **, struct pf_rule *, int, struct ifnet *, struct pf_state *, @@ -300,28 +312,36 @@ VNET_DECLARE(int, pf_end_threads); VNET_DEFINE(struct pf_limit, pf_limits[PF_LIMIT_MAX]); -#define PACKET_LOOPED(pd) ((pd)->pf_mtag && \ - (pd)->pf_mtag->flags & PF_PACKET_LOOPED) +#define PACKET_LOOPED(mtag) ((mtag)->flags & PF_PACKET_LOOPED) + +#define PF_DIVERT_MAXPACKETS_REACHED() \ +do { \ + if (r->spare2 && \ + s->packets[dir == PF_OUT] > r->spare2) \ + /* fake that divert already happened */ \ + pd.pf_mtag->flags |= PF_PACKET_LOOPED; \ +} while(0) #define STATE_LOOKUP(i, k, d, s, pd) \ do { \ (s) = pf_find_state((i), (k), (d)); \ if ((s) == NULL) \ return (PF_DROP); \ - if (PACKET_LOOPED(pd)) \ + if (PACKET_LOOPED(pd->pf_mtag)) { \ + if ((s)->key[PF_SK_WIRE] != (s)->key[PF_SK_STACK]) { \ + pf_packet_redo_nat(m, pd, off, s, direction); \ + } \ return (PF_PASS); \ + } \ if ((d) == PF_OUT && \ (((s)->rule.ptr->rt == PF_ROUTETO && \ - (s)->rule.ptr->direction == PF_OUT) || \ - ((s)->rule.ptr->rt == PF_REPLYTO && \ - (s)->rule.ptr->direction == PF_IN)) && \ + (s)->rule.ptr->direction == PF_OUT)) && \ (s)->rt_kif != NULL && \ (s)->rt_kif != (i)) \ return (PF_PASS); \ } while (0) -#define BOUND_IFACE(r, k) \ - ((r)->rule_flag & PFRULE_IFBOUND) ? (k) : V_pfi_all +#define BOUND_IFACE(r, k) k #define STATE_INC_COUNTERS(s) \ do { \ @@ -407,6 +427,160 @@ pf_addr_cmp(struct pf_addr *a, struct pf_addr *b, sa_family_t af) return (0); } +static void +pf_packet_undo_nat(struct mbuf *m, struct pf_pdesc *pd, int off, + struct pf_state *state, int direction) +{ + struct pf_state_key *nk; + + if (state == NULL || state->nat_rule.ptr == NULL) + return; + + if (state->nat_rule.ptr->action == PF_RDR || + state->nat_rule.ptr->action == PF_BINAT) + nk = (state)->key[PF_SK_WIRE]; + else + nk = (state)->key[PF_SK_STACK]; + + switch (pd->proto) { + case IPPROTO_TCP: { + struct tcphdr *th = pd->hdr.tcp; + + if (direction == PF_OUT) { + pf_change_ap(m, pd->src, &th->th_sport, pd->ip_sum, + &th->th_sum, &nk->addr[pd->sidx], + nk->port[pd->sidx], 0, pd->af); + } else { + pf_change_ap(m, pd->dst, &th->th_dport, pd->ip_sum, + &th->th_sum, &nk->addr[pd->didx], + nk->port[pd->didx], 0, pd->af); + } + m_copyback(m, off, sizeof(*th), (caddr_t)th); + } + break; + case IPPROTO_UDP: { + struct udphdr *uh = pd->hdr.udp; + + if (direction == PF_OUT) { + pf_change_ap(m, pd->src, &uh->uh_sport, pd->ip_sum, + &uh->uh_sum, &nk->addr[pd->sidx], + nk->port[pd->sidx], 1, pd->af); + } else { + pf_change_ap(m, pd->dst, &uh->uh_dport, pd->ip_sum, + &uh->uh_sum, &nk->addr[pd->didx], + nk->port[pd->didx], 1, pd->af); + } + m_copyback(m, off, sizeof(*uh), (caddr_t)uh); + } + break; + /* case IPPROTO_ICMP: */ + /* XXX: If we want to do this for icmp is probably wrong!?! */ + /* break; */ + default: + if (direction == PF_OUT) { + switch (pd->af) { + case AF_INET: + pf_change_a(&pd->src->v4.s_addr, + pd->ip_sum, nk->addr[pd->sidx].v4.s_addr, + 0); + break; + case AF_INET6: + PF_ACPY(pd->src, &nk->addr[pd->sidx], pd->af); + break; + } + } else { + switch (pd->af) { + case AF_INET: + pf_change_a(&pd->dst->v4.s_addr, + pd->ip_sum, nk->addr[pd->didx].v4.s_addr, + 0); + break; + case AF_INET6: + PF_ACPY(pd->dst, &nk->addr[pd->didx], pd->af); + break; + } + } + break; + } +} + +static void +pf_packet_redo_nat(struct mbuf *m, struct pf_pdesc *pd, int off, + struct pf_state *state, int direction) +{ + struct pf_state_key *nk; + + if (state == NULL || state->nat_rule.ptr == NULL) + return; + + if (state->nat_rule.ptr->action == PF_RDR || + state->nat_rule.ptr->action == PF_BINAT) + nk = (state)->key[PF_SK_STACK]; + else + nk = (state)->key[PF_SK_WIRE]; + + switch (pd->proto) { + case IPPROTO_TCP: { + struct tcphdr *th = pd->hdr.tcp; + + if (direction == PF_OUT) { + pf_change_ap(m, pd->src, &th->th_sport, pd->ip_sum, + &th->th_sum, &nk->addr[pd->sidx], + nk->port[pd->sidx], 0, pd->af); + } else { + pf_change_ap(m, pd->dst, &th->th_dport, pd->ip_sum, + &th->th_sum, &nk->addr[pd->didx], + nk->port[pd->didx], 0, pd->af); + } + m_copyback(m, off, sizeof(*th), (caddr_t)th); + } + break; + case IPPROTO_UDP: { + struct udphdr *uh = pd->hdr.udp; + + if (direction == PF_OUT) { + pf_change_ap(m, pd->src, &uh->uh_sport, pd->ip_sum, + &uh->uh_sum, &nk->addr[pd->sidx], + nk->port[pd->sidx], 1, pd->af); + } else { + pf_change_ap(m, pd->dst, &uh->uh_dport, pd->ip_sum, + &uh->uh_sum, &nk->addr[pd->didx], + nk->port[pd->didx], 1, pd->af); + } + m_copyback(m, off, sizeof(*uh), (caddr_t)uh); + } + break; + /* case IPPROTO_ICMP: */ + /* XXX: If we want to do this for icmp is probably wrong!?! */ + /* break; */ + default: + if (direction == PF_OUT) { + switch (pd->af) { + case AF_INET: + pf_change_a(&pd->src->v4.s_addr, + pd->ip_sum, nk->addr[pd->sidx].v4.s_addr, + 0); + break; + case AF_INET6: + PF_ACPY(pd->src, &nk->addr[pd->sidx], pd->af); + break; + } + } else { + switch (pd->af) { + case AF_INET: + pf_change_a(&pd->dst->v4.s_addr, + pd->ip_sum, nk->addr[pd->didx].v4.s_addr, + 0); + break; + case AF_INET6: + PF_ACPY(pd->dst, &nk->addr[pd->didx], pd->af); + break; + } + } + break; + } +} + static __inline uint32_t pf_hashkey(struct pf_state_key *sk) { @@ -440,6 +614,20 @@ pf_hashsrc(struct pf_addr *addr, sa_family_t af) return (h & pf_srchashmask); } +#ifdef ALTQ +static int +pf_state_hash(struct pf_state *s) +{ + u_int32_t hv = (intptr_t)s / sizeof(*s); + + hv ^= crc32(&s->src, sizeof(s->src)); + hv ^= crc32(&s->dst, sizeof(s->dst)); + if (hv == 0) + hv = 1; + return (hv); +} +#endif + #ifdef INET6 void pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af) @@ -1285,7 +1473,7 @@ pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int dir) /* List is sorted, if-bound states before floating ones. */ TAILQ_FOREACH(s, &sk->states[idx], key_list[idx]) - if (s->kif == V_pfi_all || s->kif == kif) { + { PF_STATE_LOCK(s); PF_HASHROW_UNLOCK(kh); if (s->timeout >= PFTM_MAX) { @@ -1971,9 +2159,9 @@ pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2) switch (aw1->type) { case PF_ADDR_ADDRMASK: case PF_ADDR_RANGE: - if (PF_ANEQ(&aw1->v.a.addr, &aw2->v.a.addr, 0)) + if (PF_ANEQ(&aw1->v.a.addr, &aw2->v.a.addr, AF_INET6)) return (1); - if (PF_ANEQ(&aw1->v.a.mask, &aw2->v.a.mask, 0)) + if (PF_ANEQ(&aw1->v.a.mask, &aw2->v.a.mask, AF_INET6)) return (1); return (0); case PF_ADDR_DYNIFTL: @@ -2440,6 +2628,26 @@ pf_send_tcp(struct mbuf *replyto, const struct pf_rule *r, sa_family_t af, pf_send(pfse); } +int +pf_ieee8021q_setpcp(struct mbuf *m, struct pf_rule *r) +{ + struct m_tag *mtag; + + KASSERT(r->ieee8021q_pcp.setpcp & SETPCP_VALID, + ("%s with invalid setpcp", __func__)); + + mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_OUT, NULL); + if (mtag == NULL) { + mtag = m_tag_alloc(MTAG_8021Q, MTAG_8021Q_PCP_OUT, + sizeof(uint8_t), M_NOWAIT); + if (mtag == NULL) + return (ENOMEM); + m_tag_prepend(m, mtag); + } + *(uint8_t *)(mtag + 1) = (r->ieee8021q_pcp.setpcp & SETPCP_PCP_MASK); + return (0); +} + static void pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af, struct pf_rule *r) @@ -2613,6 +2821,37 @@ pf_match_port(u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p) return (pf_match(op, a1, a2, p)); } +int +pf_match_ieee8021q_pcp(u_int8_t op, u_int8_t pcp1, u_int8_t pcp2, + struct mbuf *m) +{ + struct m_tag *mtag; + uint8_t mpcp; + + /* + * Packets without 802.1q headers are treated as having a PCP of 0 + * (best effort). + */ + mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_IN, NULL); + if (mtag != NULL) + mpcp = *(uint8_t *)(mtag + 1); + else + mpcp = IEEE8021Q_PCP_BE; + + /* + * 802.1q uses a non-traditional ordering, in which 1 < 0, allowing + * default 0-tagged ("best effort") traffic to take precedence over + * 1-tagged ("background") traffic. Renumber both PCP arguments + * before making a comparison so that we can use boring arithmetic + * operators. + */ + pcp1 = ((pcp1 == 0) ? 1 : ((pcp1 == 1) ? 0 : pcp1)); + pcp2 = ((pcp2 == 0) ? 1 : ((pcp2 == 1) ? 0 : pcp2)); + mpcp = ((mpcp == 0) ? 1 : ((mpcp == 1) ? 0 : mpcp)); + return (pf_match(op, pcp1, pcp2, mpcp)); +} + +#ifdef PF_USER_INFO static int pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u) { @@ -2628,6 +2867,7 @@ pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g) return (0); return (pf_match(op, a1, a2, g)); } +#endif int pf_match_tag(struct mbuf *m, struct pf_rule *r, int *tag, int mtag) @@ -2821,6 +3061,22 @@ pf_addr_inc(struct pf_addr *addr, sa_family_t af) } #endif /* INET6 */ +void +pf_rule_to_actions(struct pf_rule *r, struct pf_rule_actions *a) +{ + if (r->qid) + a->qid = r->qid; + if (r->pqid) + a->pqid = r->pqid; + if (r->pdnpipe) + a->pdnpipe = r->pdnpipe; + if (r->dnpipe) + a->dnpipe = r->dnpipe; + if (r->free_flags & PFRULE_DN_IS_PIPE) + a->flags |= PFRULE_DN_IS_PIPE; +} + +#ifdef PF_USER_INFO int pf_socket_lookup(int direction, struct pf_pdesc *pd, struct mbuf *m) { @@ -2900,6 +3156,7 @@ pf_socket_lookup(int direction, struct pf_pdesc *pd, struct mbuf *m) return (1); } +#endif static u_int8_t pf_get_wscale(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) @@ -3091,12 +3348,14 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, PF_RULES_RASSERT(); +#ifdef PF_USER_INFO if (inp != NULL) { INP_LOCK_ASSERT(inp); pd->lookup.uid = inp->inp_cred->cr_uid; pd->lookup.gid = inp->inp_cred->cr_groups[0]; pd->lookup.done = 1; } +#endif switch (pd->proto) { case IPPROTO_TCP: @@ -3307,7 +3566,11 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, /* 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)) + else if ((r->rule_flag & PFRULE_TOS) && r->tos && + !(r->tos == pd->tos)) + r = TAILQ_NEXT(r, entries); + else if ((r->rule_flag & PFRULE_DSCP) && r->tos && + !(r->tos == (pd->tos & DSCP_MASK))) r = TAILQ_NEXT(r, entries); else if (r->rule_flag & PFRULE_FRAGMENT) r = TAILQ_NEXT(r, entries); @@ -3315,6 +3578,7 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, (r->flagset & th->th_flags) != r->flags) r = TAILQ_NEXT(r, entries); /* tcp/udp only. uid.op always 0 in other cases */ +#ifdef PF_USER_INFO else if (r->uid.op && (pd->lookup.done || (pd->lookup.done = pf_socket_lookup(direction, pd, m), 1)) && !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1], @@ -3326,6 +3590,11 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], pd->lookup.gid)) r = TAILQ_NEXT(r, entries); +#endif + else if (r->ieee8021q_pcp.op && + !pf_match_ieee8021q_pcp(r->ieee8021q_pcp.op, + r->ieee8021q_pcp.pcp[0], r->ieee8021q_pcp.pcp[1], m)) + r = TAILQ_NEXT(r, entries); else if (r->prob && r->prob <= arc4random()) r = TAILQ_NEXT(r, entries); @@ -3343,10 +3612,20 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, if (r->rtableid >= 0) rtableid = r->rtableid; if (r->anchor == NULL) { - match = 1; - *rm = r; - *am = a; - *rsm = ruleset; + if (r->action == PF_MATCH) { + r->packets[direction == PF_OUT]++; + r->bytes[direction == PF_OUT] += pd->tot_len; + pf_rule_to_actions(r, &pd->act); + if (r->log) + PFLOG_PACKET(kif, m, af, + direction, 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); @@ -3365,6 +3644,9 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, REASON_SET(&reason, PFRES_MATCH); + /* apply actions for last matching pass/block rule */ + pf_rule_to_actions(r, &pd->act); + if (r->log || (nr != NULL && nr->log)) { if (rewrite) m_copyback(m, off, hdrlen, pd->hdr.any); @@ -3538,6 +3820,11 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, s->state_flags |= PFSTATE_SLOPPY; s->log = r->log & PF_LOG_ALL; s->sync_state = PFSYNC_S_NONE; + s->qid = pd->act.qid; + s->pqid = pd->act.pqid; + s->pdnpipe = pd->act.pdnpipe; + s->dnpipe = pd->act.dnpipe; + s->state_flags |= pd->act.flags; if (nr != NULL) s->log |= nr->log & PF_LOG_ALL; switch (pd->proto) { @@ -3776,6 +4063,9 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, r = r->skip[PF_SKIP_DST_ADDR].ptr; else if (r->tos && !(r->tos == pd->tos)) r = TAILQ_NEXT(r, entries); + else if ((r->rule_flag & PFRULE_DSCP) && r->tos && + !(r->tos == (pd->tos & DSCP_MASK))) + r = TAILQ_NEXT(r, entries); else if (r->os_fingerprint != PF_OSFP_ANY) r = TAILQ_NEXT(r, entries); else if (pd->proto == IPPROTO_UDP && @@ -3788,6 +4078,10 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, pd->proto == IPPROTO_ICMPV6) && (r->type || r->code)) r = TAILQ_NEXT(r, entries); + else if (r->ieee8021q_pcp.op && + !pf_match_ieee8021q_pcp(r->ieee8021q_pcp.op, + r->ieee8021q_pcp.pcp[0], r->ieee8021q_pcp.pcp[1], m)) + r = TAILQ_NEXT(r, entries); else if (r->prob && r->prob <= (arc4random() % (UINT_MAX - 1) + 1)) r = TAILQ_NEXT(r, entries); @@ -3796,10 +4090,20 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, r = TAILQ_NEXT(r, entries); else { if (r->anchor == NULL) { - match = 1; - *rm = r; - *am = a; - *rsm = ruleset; + if (r->action == PF_MATCH) { + r->packets[direction == PF_OUT]++; + r->bytes[direction == PF_OUT] += pd->tot_len; + pf_rule_to_actions(r, &pd->act); + if (r->log) + PFLOG_PACKET(kif, m, af, + direction, 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); @@ -3818,6 +4122,9 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, 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, af, direction, reason, r, a, ruleset, pd, 1); @@ -5061,7 +5368,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, static int pf_test_state_other(struct pf_state **state, int direction, struct pfi_kif *kif, - struct mbuf *m, struct pf_pdesc *pd) + struct mbuf *m, int off, struct pf_pdesc *pd) { struct pf_state_peer *src, *dst; struct pf_state_key_cmp key; @@ -5339,6 +5646,12 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, ip = mtod(m0, struct ip *); + if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { + if (s) + PF_STATE_UNLOCK(s); + return; + } + bzero(&dst, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_len = sizeof(dst); @@ -5386,7 +5699,71 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, if (ifp == NULL) goto bad; - if (oifp != ifp) { + else if (r->rt == PF_REPLYTO || (r->rt == PF_ROUTETO && ifp->if_type == IFT_ENC)) { + /* XXX: Copied from ifaof_ifpforaddr() since it mostly will not return NULL! */ + struct sockaddr_in inaddr; + struct sockaddr *addr; + struct ifaddr *ifa; + char *cp, *cp2, *cp3; + char *cplim; + + inaddr.sin_addr = ip->ip_dst; + inaddr.sin_family = AF_INET; + inaddr.sin_len = sizeof(inaddr); + inaddr.sin_port = 0; + addr = (struct sockaddr *)&inaddr; + + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + if (ifa->ifa_netmask == 0) { + if ((bcmp(addr, ifa->ifa_addr, addr->sa_len) == 0) || + (ifa->ifa_dstaddr && + (bcmp(addr, ifa->ifa_dstaddr, addr->sa_len) == 0))) { + IF_ADDR_RUNLOCK(ifp); + return; + } + continue; + } + if (ifp->if_flags & IFF_POINTOPOINT) { + if (bcmp(addr, ifa->ifa_dstaddr, addr->sa_len) == 0) { + IF_ADDR_RUNLOCK(ifp); + return; + } + } else { + cp = addr->sa_data; + cp2 = ifa->ifa_addr->sa_data; + cp3 = ifa->ifa_netmask->sa_data; + cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; + for (; cp3 < cplim; cp3++) + if ((*cp++ ^ *cp2++) & *cp3) + break; + if (cp3 == cplim) { + IF_ADDR_RUNLOCK(ifp); + return; + } + } + } + IF_ADDR_RUNLOCK(ifp); + } + else if (r->rt == PF_ROUTETO && r->direction == dir && in_localip(ip->ip_dst)) + return; + + if (s != NULL && r->rt == PF_REPLYTO) { + /* + * Send it out since it came from state recorded ifp(rt_addr). + * Routing table lookup might have chosen not correct interface! + */ + } else if (oifp != ifp) { + if (in_broadcast(ip->ip_dst, oifp)) /* XXX: LOCKING of address list?! */ + return; + + if (s && r->rt == PF_ROUTETO && pd->nat_rule != NULL && + r->direction == PF_OUT && r->direction == dir && pd->pf_mtag->routed < 2) { + pf_packet_undo_nat(m0, pd, ntohs(ip->ip_off), s, dir); + } + if (pf_test(PF_OUT, ifp, &m0, NULL) != PF_PASS) goto bad; else if (m0 == NULL) @@ -5440,6 +5817,9 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, error = EMSGSIZE; KMOD_IPSTAT_INC(ips_cantfrag); if (r->rt != PF_DUPTO) { + if (s && pd->nat_rule != NULL) + pf_packet_undo_nat(m0, pd, ntohs(ip->ip_off), s, dir); + icmp_error(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, 0, ifp->if_mtu); goto done; @@ -5519,6 +5899,12 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, ip6 = mtod(m0, struct ip6_hdr *); + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) { + if (s) + PF_STATE_UNLOCK(s); + return; + } + bzero(&dst, sizeof(dst)); dst.sin6_family = AF_INET6; dst.sin6_len = sizeof(dst); @@ -5558,9 +5944,71 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, if (ifp == NULL) goto bad; + else if (r->rt == PF_REPLYTO) { + /* XXX: Copied from ifaof_ifpforaddr() since it mostly will not return NULL! */ + struct sockaddr_in6 inaddr6; + struct sockaddr *addr; + struct ifaddr *ifa; + char *cp, *cp2, *cp3; + char *cplim; + + inaddr6.sin6_addr = ip6->ip6_dst; + inaddr6.sin6_family = AF_INET6; + inaddr6.sin6_len = sizeof(inaddr6); + inaddr6.sin6_port = 0; + inaddr6.sin6_flowinfo = 0; + addr = (struct sockaddr *)&inaddr6; + + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (ifa->ifa_netmask == 0) { + if ((bcmp(addr, ifa->ifa_addr, addr->sa_len) == 0) || + (ifa->ifa_dstaddr && + (bcmp(addr, ifa->ifa_dstaddr, addr->sa_len) == 0))) { + IF_ADDR_RUNLOCK(ifp); + return; + } + continue; + } + if (ifp->if_flags & IFF_POINTOPOINT) { + if (bcmp(addr, ifa->ifa_dstaddr, addr->sa_len) == 0) { + IF_ADDR_RUNLOCK(ifp); + return; + } + } else { + cp = addr->sa_data; + cp2 = ifa->ifa_addr->sa_data; + cp3 = ifa->ifa_netmask->sa_data; + cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; + for (; cp3 < cplim; cp3++) + if ((*cp++ ^ *cp2++) & *cp3) + break; + if (cp3 == cplim) { + IF_ADDR_RUNLOCK(ifp); + return; + } + } + } + IF_ADDR_RUNLOCK(ifp); + } else if (r->rt == PF_ROUTETO && r->direction == dir && in6_localaddr(&ip6->ip6_dst)) + return; + + if (s != NULL && r->rt == PF_REPLYTO) { + /* + * Send it out since it came from state recorded ifp(rt_addr). + * Routing table lookup might have chosen not correct interface! + */ + } else if (oifp != ifp) { + + if (s && r->rt == PF_ROUTETO && pd->nat_rule != NULL && + r->direction == PF_OUT && r->direction == dir && pd->pf_mtag->routed < 2) { + int ip_off = ((caddr_t)ip6 - m0->m_data) + sizeof(struct ip6_hdr); + pf_packet_undo_nat(m0, pd, ip_off, s, dir); + } - if (oifp != ifp) { - if (pf_test6(PF_FWD, ifp, &m0, NULL) != PF_PASS) + if (pf_test6(PF_OUT, ifp, &m0, NULL) != PF_PASS) goto bad; else if (m0 == NULL) goto done; @@ -5593,9 +6041,12 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, nd6_output(ifp, ifp, m0, &dst, NULL); else { in6_ifstat_inc(ifp, ifs6_in_toobig); - if (r->rt != PF_DUPTO) + if (r->rt != PF_DUPTO) { + if (s && pd->nat_rule != NULL) + pf_packet_undo_nat(m0, pd, ((caddr_t)ip6 - m0->m_data) + sizeof(struct ip6_hdr), s, dir); + icmp6_error(m0, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); - else + } else goto bad; } @@ -5761,7 +6212,11 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) struct pf_state *s = NULL; struct pf_ruleset *ruleset = NULL; struct pf_pdesc pd; - int off, dirndx, pqid = 0; + int off = 0, dirndx, pqid = 0; + int loopedfrom = 0; + u_int16_t divertcookie = 0; + u_int8_t divflags = 0; + struct ip_fw_args dnflow; M_ASSERTPKTHDR(m); @@ -5783,26 +6238,33 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) if (m->m_flags & M_SKIP_FIREWALL) return (PF_PASS); - pd.pf_mtag = pf_find_mtag(m); + pd.pf_mtag = pf_get_mtag(m); + if (pd.pf_mtag == NULL) { + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: dropping packet due to failed memory allocation for tags\n")); + return PF_DROP; + } PF_RULES_RLOCK(); - if (ip_divert_ptr != NULL && + if ((ip_divert_ptr != NULL || ip_dn_io_ptr != NULL) && ((ipfwtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL)) != NULL)) { struct ipfw_rule_ref *rr = (struct ipfw_rule_ref *)(ipfwtag+1); - if (rr->info & IPFW_IS_DIVERT && rr->rulenum == 0) { - if (pd.pf_mtag == NULL && - ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { - action = PF_DROP; - goto done; - } - pd.pf_mtag->flags |= PF_PACKET_LOOPED; - m_tag_delete(m, ipfwtag); + pd.pf_mtag->flags |= PF_PACKET_LOOPED; + if (rr->info & IPFW_IS_DUMMYNET) + loopedfrom = 1; + if (rr->info & IPFW_IS_DIVERT) { + divertcookie = rr->rulenum; + divflags = (u_int8_t)(divertcookie >> 8); + divertcookie &= ~PFSTATE_DIVERT_MASK; } if (pd.pf_mtag && pd.pf_mtag->flags & PF_FASTFWD_OURS_PRESENT) { m->m_flags |= M_FASTFWD_OURS; pd.pf_mtag->flags &= ~PF_FASTFWD_OURS_PRESENT; } + m_tag_delete(m, ipfwtag); } else if (pf_normalize_ip(m0, dir, kif, &reason, &pd) != PF_PASS) { /* We do IP header normalization and packet reassembly here */ action = PF_DROP; @@ -5845,6 +6307,10 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) struct tcphdr th; pd.hdr.tcp = &th; + dnflow.f_id._flags = th.th_flags; + dnflow.f_id.dst_port = ntohs(th.th_dport); + dnflow.f_id.src_port = ntohs(th.th_sport); + if (!pf_pull_hdr(m, off, &th, sizeof(th), &action, &reason, AF_INET)) { log = action != PF_PASS; @@ -5859,8 +6325,6 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); r = s->rule.ptr; a = s->anchor.ptr; log = s->log; @@ -5874,6 +6338,9 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) struct udphdr uh; pd.hdr.udp = &uh; + dnflow.f_id.dst_port = ntohs(uh.uh_dport); + dnflow.f_id.src_port = ntohs(uh.uh_sport); + if (!pf_pull_hdr(m, off, &uh, sizeof(uh), &action, &reason, AF_INET)) { log = action != PF_PASS; @@ -5888,8 +6355,6 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) } action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); r = s->rule.ptr; a = s->anchor.ptr; log = s->log; @@ -5911,8 +6376,6 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); r = s->rule.ptr; a = s->anchor.ptr; log = s->log; @@ -5932,10 +6395,8 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) #endif default: - action = pf_test_state_other(&s, dir, kif, m, &pd); + action = pf_test_state_other(&s, dir, kif, m, off, &pd); if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); r = s->rule.ptr; a = s->anchor.ptr; log = s->log; @@ -5956,6 +6417,17 @@ done: ("pf: dropping packet with ip options\n")); } + if (s) { + PF_DIVERT_MAXPACKETS_REACHED(); + + if (divflags) { + s->divert_cookie = divertcookie; + s->local_flags |= divflags; + } else { + divertcookie = s->divert_cookie; + divflags = s->local_flags; + } + } if (s && s->tag > 0 && pf_tag_packet(m, &pd, s->tag)) { action = PF_DROP; REASON_SET(&reason, PFRES_MEMORY); @@ -5963,23 +6435,160 @@ done: if (r->rtableid >= 0) M_SETFIB(m, r->rtableid); + if ((r->ieee8021q_pcp.setpcp & SETPCP_VALID) && + pf_ieee8021q_setpcp(m, r)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate 802.1q mtag\n")); + } + #ifdef ALTQ - if (action == PF_PASS && r->qid) { - if (pd.pf_mtag == NULL && - ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); + if (s && s->qid) { + pd.act.pqid = s->pqid; + pd.act.qid = s->qid; + } else if (r->qid) { + pd.act.pqid = r->pqid; + pd.act.qid = r->qid; + } + if (action == PF_PASS && pd.act.qid) { + if (s != NULL) + pd.pf_mtag->qid_hash = pf_state_hash(s); + if (pqid || (pd.tos & IPTOS_LOWDELAY)) + pd.pf_mtag->qid = pd.act.pqid; + else + pd.pf_mtag->qid = pd.act.qid; + /* Add hints for ecn. */ + pd.pf_mtag->hdr = h; + } +#endif /* ALTQ */ + + if (divflags & PFSTATE_DIVERT_TAG) + pd.pf_mtag->tag = divertcookie; + else if (divflags & PFSTATE_DIVERT_ALTQ) + pd.pf_mtag->qid = divertcookie; + else if (divflags & PFSTATE_DIVERT_ACTION) { + struct pf_rule *dlr; + action = PF_DROP; + if (s) + pf_unlink_state(s, PF_ENTER_LOCKED); + REASON_SET(&reason, PFRES_DIVERT); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: changing action to with overload from divert.\n")); + dlr = r; + PFLOG_PACKET(kif, m, AF_INET, dir, reason, dlr, a, + ruleset, &pd, (s == NULL)); + m_freem(*m0); + *m0 = NULL; + /* NOTE: Fake this to avoid divert giving errors to the application. */ + return (PF_PASS); + } + + if (divflags & PFSTATE_DIVERT_DNCOOKIE) { + pd.act.dnpipe = divertcookie; + pd.act.pdnpipe = divertcookie; + pd.act.flags |= PFRULE_DN_IS_PIPE; + } else if (s && (s->dnpipe || s->pdnpipe)) { + pd.act.dnpipe = s->dnpipe; + pd.act.pdnpipe = s->pdnpipe; + pd.act.flags = s->state_flags; + } else if (r->dnpipe || r->pdnpipe) { + pd.act.dnpipe = r->dnpipe; + pd.act.dnpipe = r->pdnpipe; + pd.act.flags = r->free_flags; + } + + if (pd.act.dnpipe && ip_dn_io_ptr != NULL && loopedfrom != 1) { + if (dir != r->direction && pd.act.pdnpipe) { + dnflow.rule.info = pd.act.pdnpipe; + } else if (dir == r->direction) { + dnflow.rule.info = pd.act.dnpipe; + } else + goto continueprocessing; + + if (pd.act.flags & PFRULE_DN_IS_PIPE) + dnflow.rule.info |= IPFW_IS_PIPE; + dnflow.f_id.addr_type = 4; /* IPv4 type */ + dnflow.f_id.proto = pd.proto; + if (dir == PF_OUT && s != NULL && s->nat_rule.ptr != NULL && + s->nat_rule.ptr->action == PF_NAT) + dnflow.f_id.src_ip = + ntohl(s->key[(s->direction == PF_IN)]-> + addr[(s->direction == PF_OUT)].v4.s_addr); + else + dnflow.f_id.src_ip = ntohl(h->ip_src.s_addr); + dnflow.f_id.dst_ip = ntohl(h->ip_dst.s_addr); + dnflow.f_id.extra = dnflow.rule.info; + + if (m->m_flags & M_FASTFWD_OURS) { + pd.pf_mtag->flags |= PF_FASTFWD_OURS_PRESENT; + m->m_flags &= ~M_FASTFWD_OURS; + } + + if (s != NULL && s->nat_rule.ptr) + pf_packet_undo_nat(m, &pd, off, s, dir); + + ip_dn_io_ptr(m0, + (dir == PF_IN) ? DIR_IN : DIR_OUT, + &dnflow); + /* This is dummynet fast io processing */ + if (*m0 != NULL) { + m_tag_delete(*m0, m_tag_first(*m0)); + pd.pf_mtag->flags &= ~PF_PACKET_LOOPED; + if (s != NULL && s->nat_rule.ptr) + pf_packet_redo_nat(m, &pd, off, s, dir); } else { - if (pqid || (pd.tos & IPTOS_LOWDELAY)) - pd.pf_mtag->qid = r->pqid; - else - pd.pf_mtag->qid = r->qid; - /* Add hints for ecn. */ - pd.pf_mtag->hdr = h; + *m0 = NULL; + if (s) + PF_STATE_UNLOCK(s); + return (action); } + } +continueprocessing: + + if (action == PF_PASS && r->divert.port && ip_divert_ptr != NULL && + !PACKET_LOOPED(&pd)) { + if (!r->spare2 || + (s && s->packets[dir == PF_OUT] <= r->spare2)) { + ipfwtag = m_tag_alloc(MTAG_IPFW_RULE, 0, + sizeof(struct ipfw_rule_ref), M_NOWAIT | M_ZERO); + if (ipfwtag != NULL) { + ((struct ipfw_rule_ref *)(ipfwtag+1))->info = + ntohs(r->divert.port); + ((struct ipfw_rule_ref *)(ipfwtag+1))->rulenum = dir; + + if (s) + PF_STATE_UNLOCK(s); + + m_tag_prepend(m, ipfwtag); + if (m->m_flags & M_FASTFWD_OURS) { + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate tag\n")); + } + pd.pf_mtag->flags |= PF_FASTFWD_OURS_PRESENT; + m->m_flags &= ~M_FASTFWD_OURS; + } + ip_divert_ptr(*m0, dir == PF_IN ? DIR_IN : DIR_OUT); + *m0 = NULL; + return (action); + } else { + /* XXX: ipfw has the same behaviour! */ + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate divert tag\n")); + } + } } -#endif /* ALTQ */ /* * connections redirected to loopback should not match sockets @@ -5990,50 +6599,17 @@ done: pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && (s->nat_rule.ptr->action == PF_RDR || s->nat_rule.ptr->action == PF_BINAT) && - (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) + (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) { m->m_flags |= M_SKIP_FIREWALL; - if (action == PF_PASS && r->divert.port && ip_divert_ptr != NULL && - !PACKET_LOOPED(&pd)) { + if (PACKET_LOOPED(pd.pf_mtag) && !loopedfrom) + m->m_flags |= M_FASTFWD_OURS; + } - ipfwtag = m_tag_alloc(MTAG_IPFW_RULE, 0, - sizeof(struct ipfw_rule_ref), M_NOWAIT | M_ZERO); - if (ipfwtag != NULL) { - ((struct ipfw_rule_ref *)(ipfwtag+1))->info = - ntohs(r->divert.port); - ((struct ipfw_rule_ref *)(ipfwtag+1))->rulenum = dir; + pd.pf_mtag->flags &= ~PF_PACKET_LOOPED; - if (s) - PF_STATE_UNLOCK(s); - - m_tag_prepend(m, ipfwtag); - if (m->m_flags & M_FASTFWD_OURS) { - if (pd.pf_mtag == NULL && - ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); - log = 1; - DPFPRINTF(PF_DEBUG_MISC, - ("pf: failed to allocate tag\n")); - } else { - pd.pf_mtag->flags |= - PF_FASTFWD_OURS_PRESENT; - m->m_flags &= ~M_FASTFWD_OURS; - } - } - ip_divert_ptr(*m0, dir == PF_IN ? DIR_IN : DIR_OUT); - *m0 = NULL; - - return (action); - } else { - /* XXX: ipfw has the same behaviour! */ - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); - log = 1; - DPFPRINTF(PF_DEBUG_MISC, - ("pf: failed to allocate divert tag\n")); - } - } + if (action == PF_PASS && s != NULL && pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); if (log) { struct pf_rule *lr; @@ -6134,7 +6710,10 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) struct pf_state *s = NULL; struct pf_ruleset *ruleset = NULL; struct pf_pdesc pd; - int off, terminal = 0, dirndx, rh_cnt = 0; + int off = 0, terminal = 0, dirndx, rh_cnt = 0; + int loopedfrom = 0; + struct m_tag *dn_tag; + struct ip_fw_args dnflow; int fwdir = dir; M_ASSERTPKTHDR(m); @@ -6145,18 +6724,27 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) * We do need to be careful about bridges. If the * net.link.bridge.pfil_bridge sysctl is set we can be filtering on a * bridge, so if the input interface is a bridge member and the output - * interface is its bridge we're not actually forwarding but bridging. + * interface is its bridge or a member of the same bridge we're not + * actually forwarding but bridging. */ - if (dir == PF_OUT && m->m_pkthdr.rcvif && ifp != m->m_pkthdr.rcvif - && (m->m_pkthdr.rcvif->if_bridge == NULL - || m->m_pkthdr.rcvif->if_bridge != ifp->if_softc)) + if (dir == PF_OUT && m->m_pkthdr.rcvif && ifp != m->m_pkthdr.rcvif && + (m->m_pkthdr.rcvif->if_bridge == NULL || + (m->m_pkthdr.rcvif->if_bridge != ifp->if_softc && + m->m_pkthdr.rcvif->if_bridge != ifp->if_bridge))) fwdir = PF_FWD; if (!V_pf_status.running) return (PF_PASS); memset(&pd, 0, sizeof(pd)); - pd.pf_mtag = pf_find_mtag(m); + pd.pf_mtag = pf_get_mtag(m); + if (pd.pf_mtag == NULL) { + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: dropping packet due to failed memory allocation for tags\n")); + return PF_DROP; + } if (pd.pf_mtag && pd.pf_mtag->flags & PF_TAG_GENERATED) return (PF_PASS); @@ -6175,8 +6763,20 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) PF_RULES_RLOCK(); + if (((ip_dn_io_ptr != NULL) || (ip_divert_ptr != NULL)) && + ((dn_tag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL)) != NULL)) { + struct ipfw_rule_ref *rr = (struct ipfw_rule_ref *)(dn_tag+1); + pd.pf_mtag->flags |= PF_PACKET_LOOPED; + if (rr->info & IPFW_IS_DUMMYNET) + loopedfrom = 1; + if (pd.pf_mtag->flags & PF_FASTFWD_OURS_PRESENT) { + m->m_flags |= M_FASTFWD_OURS; + pd.pf_mtag->flags &= ~PF_FASTFWD_OURS_PRESENT; + } + m_tag_delete(m, dn_tag); + } /* We do IP header normalization and packet reassembly here */ - if (pf_normalize_ip6(m0, dir, kif, &reason, &pd) != PF_PASS) { + else if (pf_normalize_ip6(m0, dir, kif, &reason, &pd) != PF_PASS) { action = PF_DROP; goto done; } @@ -6285,6 +6885,10 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) struct tcphdr th; pd.hdr.tcp = &th; + dnflow.f_id._flags = th.th_flags; + dnflow.f_id.dst_port = th.th_dport; + dnflow.f_id.src_port = th.th_sport; + if (!pf_pull_hdr(m, off, &th, sizeof(th), &action, &reason, AF_INET6)) { log = action != PF_PASS; @@ -6297,8 +6901,6 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); r = s->rule.ptr; a = s->anchor.ptr; log = s->log; @@ -6312,6 +6914,9 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) struct udphdr uh; pd.hdr.udp = &uh; + dnflow.f_id.dst_port = uh.uh_dport; + dnflow.f_id.src_port = uh.uh_sport; + if (!pf_pull_hdr(m, off, &uh, sizeof(uh), &action, &reason, AF_INET6)) { log = action != PF_PASS; @@ -6326,8 +6931,6 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) } action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); r = s->rule.ptr; a = s->anchor.ptr; log = s->log; @@ -6356,8 +6959,6 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); r = s->rule.ptr; a = s->anchor.ptr; log = s->log; @@ -6368,10 +6969,8 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) } default: - action = pf_test_state_other(&s, dir, kif, m, &pd); + action = pf_test_state_other(&s, dir, kif, m, off, &pd); if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); r = s->rule.ptr; a = s->anchor.ptr; log = s->log; @@ -6405,23 +7004,90 @@ done: if (r->rtableid >= 0) M_SETFIB(m, r->rtableid); + if ((r->ieee8021q_pcp.setpcp & SETPCP_VALID) && + pf_ieee8021q_setpcp(m, r)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate 802.1q mtag\n")); + } + #ifdef ALTQ - if (action == PF_PASS && r->qid) { - if (pd.pf_mtag == NULL && - ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); - } else { - if (pd.tos & IPTOS_LOWDELAY) - pd.pf_mtag->qid = r->pqid; - else - pd.pf_mtag->qid = r->qid; - /* Add hints for ecn. */ - pd.pf_mtag->hdr = h; - } + if (s && s->qid) { + pd.act.pqid = s->pqid; + pd.act.qid = s->qid; + } else if (r->qid) { + pd.act.pqid = r->pqid; + pd.act.qid = r->qid; + } + if (action == PF_PASS && pd.act.qid) { + if (s != NULL) + pd.pf_mtag->qid_hash = pf_state_hash(s); + if (pd.tos & IPTOS_LOWDELAY) + pd.pf_mtag->qid = pd.act.pqid; + else + pd.pf_mtag->qid = pd.act.qid; + /* Add hints for ecn. */ + pd.pf_mtag->hdr = h; } #endif /* ALTQ */ + if (s && (s->dnpipe || s->pdnpipe)) { + pd.act.dnpipe = s->dnpipe; + pd.act.pdnpipe = s->pdnpipe; + pd.act.flags = s->state_flags; + } else if (r->dnpipe || r->pdnpipe) { + pd.act.dnpipe = r->dnpipe; + pd.act.dnpipe = r->pdnpipe; + pd.act.flags = r->free_flags; + } + if ((pd.act.dnpipe || pd.act.pdnpipe) && ip_dn_io_ptr != NULL && loopedfrom != 1) { + if (dir != r->direction && pd.act.pdnpipe) { + dnflow.rule.info = pd.act.pdnpipe; + } else if (dir == r->direction && pd.act.dnpipe) { + dnflow.rule.info = pd.act.dnpipe; + } else + goto continueprocessing6; + + if (pd.act.flags & PFRULE_DN_IS_PIPE) + dnflow.rule.info |= IPFW_IS_PIPE; + dnflow.f_id.addr_type = 6; /* IPv4 type */ + dnflow.f_id.proto = pd.proto; + dnflow.f_id.src_ip = 0; + dnflow.f_id.dst_ip = 0; + if (dir == PF_OUT && s != NULL && s->nat_rule.ptr != NULL && + s->nat_rule.ptr->action == PF_NAT) + dnflow.f_id.src_ip6 = s->key[(s->direction == PF_IN)]->addr[0].v6; + else + dnflow.f_id.src_ip6 = h->ip6_src; + dnflow.f_id.dst_ip6 = h->ip6_dst; + + if (s != NULL && s->nat_rule.ptr) + pf_packet_undo_nat(m, &pd, off, s, dir); + + ip_dn_io_ptr(m0, + ((dir == PF_IN) ? DIR_IN : DIR_OUT) | PROTO_IPV6, + &dnflow); + /* This is dummynet fast io processing */ + if (*m0 != NULL) { + m_tag_delete(*m0, m_tag_first(*m0)); + pd.pf_mtag->flags &= ~PF_PACKET_LOOPED; + if (s != NULL && s->nat_rule.ptr) + pf_packet_redo_nat(m, &pd, off, s, dir); + } else { + *m0 = NULL; + if (s) + PF_STATE_UNLOCK(s); + return (action); + } + } else + pd.pf_mtag->flags &= ~PF_PACKET_LOOPED; +continueprocessing6: + + if (action == PF_PASS && s != NULL && pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); + if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP || pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && (s->nat_rule.ptr->action == PF_RDR || |