so pflog has several issues right now, the worst being that it logs
the "wrong" addresses, as in, before rewriting is apllied.
before we broke it several releases ago pflog kept rewriting the live
mbuf all the time and immediately, and then trying to un-do the
rewriting if needed... horrible, complex, was full of bugs, fortunately
dead.
we don't want an extra copy of the mbuf (-chain) either, just to pass
it to bpf which then copies it into the bpf buffer. so apply the
rewrite to the bpf buffer hiding as data storage behind a fake mbuf ;)
works.
kinda strange sideeffect: if snaplen is smaller then pflog header size
+ ip header size + protocol header size, i cannot do the rewrite. we
need to raise the snaplen for pflog by default since the pflog header
grows a lot (that is mostly preparation for upcomimg work). for now pls
use -s 160 or more on tcpdump. oh and when the snaplen is too small i
don't do the rewrite and pass out the packet unmodified... not sure
what else we could do but dropping, which would be inconsistent with
other pcap stuff.
this touches the regular code patches (aka non-pflog) quite a bit too,
there is some heavy code refactoring. watch you state tables
carefully.
almost completely written at c2k10 in canada and just made work here in
japan ;)
and now quickly senidng out before the super duper net dies again. all
typos to be blamed on latency.
Index: sys/net/bpf.c
===================================================================
RCS file: /cvs/src/sys/net/bpf.c,v
retrieving revision 1.75
diff -u -p -r1.75 bpf.c
--- sys/net/bpf.c 9 Nov 2009 17:53:39 -0000 1.75
+++ sys/net/bpf.c 20 Sep 2010 08:54:41 -0000
@@ -64,6 +64,11 @@
#include <net/if_vlan_var.h>
#endif
+#include "pflog.h"
+#if NPFLOG > 0
+#include <net/if_pflog.h>
+#endif
+
#define BPF_BUFSIZE 32768
#define PRINET 26 /* interruptible */
@@ -1290,6 +1295,45 @@ bpf_mtap_ether(caddr_t arg, struct mbuf
m->m_data -= ETHER_HDR_LEN;
#endif
}
+
+void
+bpf_mtap_pflog(caddr_t arg, caddr_t data, struct mbuf *m)
+{
+#if NPFLOG > 0
+ struct m_hdr mh;
+ struct bpf_if *bp = (struct bpf_if *)arg;
+ struct bpf_d *d;
+ size_t pktlen, slen;
+ struct mbuf *m0;
+
+ if (m == NULL)
+ return;
+
+ mh.mh_flags = 0;
+ mh.mh_next = m;
+ mh.mh_len = PFLOG_HDRLEN;
+ mh.mh_data = data;
+
+ pktlen = mh.mh_len;
+ for (m0 = m; m0 != 0; m0 = m0->m_next)
+ pktlen += m0->m_len;
+
+ for (d = bp->bif_dlist; d != 0; d = d->bd_next) {
+ ++d->bd_rcount;
+ if ((BPF_DIRECTION_OUT & d->bd_dirfilt) != 0)
+ slen = 0;
+ else
+ slen = bpf_filter(d->bd_rfilter, (u_char *)&mh,
+ pktlen, 0);
+
+ if (slen == 0)
+ continue;
+
+ bpf_catchpacket(d, (u_char *)&mh, pktlen, slen, pflog_bpfcopy);
+ }
+#endif
+}
+
/*
* Move the packet data from interface memory (pkt) into the
Index: sys/net/bpf.h
===================================================================
RCS file: /cvs/src/sys/net/bpf.h,v
retrieving revision 1.39
diff -u -p -r1.39 bpf.h
--- sys/net/bpf.h 26 Jun 2010 16:49:01 -0000 1.39
+++ sys/net/bpf.h 20 Sep 2010 08:54:41 -0000
@@ -273,6 +273,7 @@ void bpf_mtap(caddr_t, struct mbuf *, u
void bpf_mtap_hdr(caddr_t, caddr_t, u_int, struct mbuf *, u_int);
void bpf_mtap_af(caddr_t, u_int32_t, struct mbuf *, u_int);
void bpf_mtap_ether(caddr_t, struct mbuf *, u_int);
+void bpf_mtap_pflog(caddr_t, caddr_t, struct mbuf *);
void bpfattach(caddr_t *, struct ifnet *, u_int, u_int);
void bpfdetach(struct ifnet *);
void bpfilterattach(int);
Index: sys/net/if_pflog.c
===================================================================
RCS file: /cvs/src/sys/net/if_pflog.c,v
retrieving revision 1.28
diff -u -p -r1.28 if_pflog.c
--- sys/net/if_pflog.c 12 Jan 2010 02:47:07 -0000 1.28
+++ sys/net/if_pflog.c 20 Sep 2010 08:54:41 -0000
@@ -53,6 +53,9 @@
#include <netinet/in_var.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
#endif
#ifdef INET6
@@ -60,6 +63,7 @@
#include <netinet/in.h>
#endif
#include <netinet6/nd6.h>
+#include <netinet/icmp6.h>
#endif /* INET6 */
#include <net/pfvar.h>
@@ -86,6 +90,7 @@ struct if_clone pflog_cloner =
IF_CLONE_INITIALIZER("pflog", pflog_clone_create, pflog_clone_destroy);
struct ifnet *pflogifs[PFLOGIFS_MAX]; /* for fast access */
+struct mbuf *mfake = NULL;
void
pflogattach(int npflog)
@@ -94,6 +99,8 @@ pflogattach(int npflog)
LIST_INIT(&pflogif_list);
for (i = 0; i < PFLOGIFS_MAX; i++)
pflogifs[i] = NULL;
+ if (mfake == NULL)
+ mfake = m_get(M_DONTWAIT, MT_HEADER);
if_clone_attach(&pflog_cloner);
}
@@ -246,21 +253,88 @@ pflog_packet(struct pfi_kif *kif, struct
hdr.rule_pid = rm->cpid;
hdr.dir = dir;
-#ifdef INET
- if (af == AF_INET && dir == PF_OUT) {
- struct ip *ip;
-
- ip = mtod(m, struct ip *);
- ip->ip_sum = 0;
- ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
- }
-#endif /* INET */
+ PF_ACPY(&hdr.saddr, &pd->nsaddr, pd->af);
+ PF_ACPY(&hdr.daddr, &pd->ndaddr, pd->af);
+ hdr.sport = pd->nsport;
+ hdr.dport = pd->ndport;
ifn->if_opackets++;
ifn->if_obytes += m->m_pkthdr.len;
- bpf_mtap_hdr(ifn->if_bpf, (char *)&hdr, PFLOG_HDRLEN, m,
- BPF_DIRECTION_OUT);
+
+ bpf_mtap_pflog(ifn->if_bpf, (caddr_t)&hdr, m);
#endif
return (0);
+}
+
+void
+pflog_bpfcopy(const void *src_arg, void *dst_arg, size_t len)
+{
+ const struct mbuf *m;
+ struct pfloghdr *pfloghdr;
+ u_int count;
+ u_char *dst;
+ u_short action, reason;
+ int off = 0, hdrlen = 0;
+ union {
+ struct tcphdr tcp;
+ struct udphdr udp;
+ struct icmp icmp;
+#ifdef INET6
+ struct icmp6_hdr icmp6;
+#endif /* INET6 */
+ } pf_hdrs;
+
+ struct pf_pdesc pd;
+
+ m = src_arg;
+ dst = dst_arg;
+
+ if (m == NULL)
+ panic("pflog_bpfcopy got no mbuf");
+
+ /* first mbuf holds struct pfloghdr */
+ pfloghdr = mtod(m, struct pfloghdr *);
+ count = min(m->m_len, len);
+ bcopy(pfloghdr, dst, count);
+ dst += count;
+ len -= count;
+ m = m->m_next;
+
+ /* second mbuf is pkthdr */
+ if (len > 0) {
+ if (m == NULL)
+ panic("no second mbuf");
+ bcopy(m, mfake, sizeof(*mfake));
+ mfake->m_flags &= ~(M_EXT|M_CLUSTER);
+ mfake->m_next = NULL;
+ mfake->m_nextpkt = NULL;
+ mfake->m_data = dst;
+ mfake->m_len = len;
+ } else
+ return;
+
+ while (len > 0) {
+ if (m == 0)
+ panic("bpf_mcopy");
+ count = min(m->m_len, len);
+ bcopy(mtod(m, caddr_t), (caddr_t)dst, count);
+ m = m->m_next;
+ dst += count;
+ len -= count;
+ }
+
+ if (mfake->m_flags & M_PKTHDR)
+ mfake->m_pkthdr.len = min(mfake->m_pkthdr.len, mfake->m_len);
+
+ /* rewrite addresses if needed */
+ memset(&pd, 0, sizeof(pd));
+ pd.hdr.any = &pf_hdrs;
+ if (pf_setup_pdesc(pfloghdr->af, pfloghdr->dir, &pd, mfake, &action,
+ &reason, NULL, NULL, NULL, NULL, &off, &hdrlen) == -1)
+ return;
+ if (pf_translate(&pd, &pfloghdr->saddr, pfloghdr->sport,
+ &pfloghdr->daddr, pfloghdr->dport, 0, pfloghdr->dir, mfake))
+ m_copyback(mfake, off, min(mfake->m_len - off, hdrlen),
+ pd.hdr.any, M_NOWAIT);
}
Index: sys/net/if_pflog.h
===================================================================
RCS file: /cvs/src/sys/net/if_pflog.h,v
retrieving revision 1.15
diff -u -p -r1.15 if_pflog.h
--- sys/net/if_pflog.h 26 Jun 2010 16:49:01 -0000 1.15
+++ sys/net/if_pflog.h 20 Sep 2010 08:54:41 -0000
@@ -27,6 +27,8 @@
#ifndef _NET_IF_PFLOG_H_
#define _NET_IF_PFLOG_H_
+#include <net/pfvar.h>
+
#define PFLOGIFS_MAX 16
struct pflog_softc {
@@ -52,13 +54,19 @@ struct pfloghdr {
pid_t rule_pid;
u_int8_t dir;
u_int8_t pad[3];
+ struct pf_addr saddr;
+ struct pf_addr daddr;
+ u_int16_t sport;
+ u_int16_t dport;
};
#define PFLOG_HDRLEN sizeof(struct pfloghdr)
-/* minus pad, also used as a signature */
-#define PFLOG_REAL_HDRLEN offsetof(struct pfloghdr, pad)
+/* used to be minus pad, also used as a signature */
+#define PFLOG_REAL_HDRLEN PFLOG_HDRLEN
+#define PFLOG_OLD_HDRLEN offsetof(struct pfloghdr, pad)
#ifdef _KERNEL
+void pflog_bpfcopy(const void *, void *, size_t);
#if NPFLOG > 0
#define PFLOG_PACKET(i,x,a,b,c,d,e,f,g,h)
pflog_packet(i,a,b,c,d,e,f,g,h)
Index: sys/net/pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.696
diff -u -p -r1.696 pf.c
--- sys/net/pf.c 5 Aug 2010 17:21:19 -0000 1.696
+++ sys/net/pf.c 20 Sep 2010 08:54:41 -0000
@@ -124,6 +124,17 @@ struct pf_anchor_stackframe {
struct pf_anchor *child;
} pf_anchor_stack[64];
+/* cannot fold into pf_pdesc directly, unknown storage size outside pf.c */
+union {
+ struct tcphdr tcp;
+ struct udphdr udp;
+ struct icmp icmp;
+#ifdef INET6
+ struct icmp6_hdr icmp6;
+#endif /* INET6 */
+} pf_hdrs;
+
+
struct pool pf_src_tree_pl, pf_rule_pl;
struct pool pf_state_pl, pf_state_key_pl, pf_state_item_pl;
struct pool pf_altq_pl, pf_rule_item_pl, pf_sn_item_pl;
@@ -164,7 +175,7 @@ void pf_rule_to_actions(struct
pf_rul
int pf_test_rule(struct pf_rule **, struct pf_state **,
int, struct pfi_kif *, struct mbuf *, int,
void *, struct pf_pdesc *, struct pf_rule **,
- struct pf_ruleset **, struct ifqueue *);
+ struct pf_ruleset **, struct ifqueue *, int);
static __inline int pf_create_state(struct pf_rule *, struct pf_rule *,
struct pf_pdesc *, struct pf_state_key **,
struct pf_state_key **, struct mbuf *, int,
@@ -173,9 +184,6 @@ static __inline int pf_create_state(str
struct pf_rule_actions *, struct pf_src_node *[]);
int pf_state_key_setup(struct pf_pdesc *, struct
pf_state_key **, struct pf_state_key **, int);
-void pf_translate(struct pf_pdesc *, struct pf_addr *,
- u_int16_t, struct pf_addr *, u_int16_t, u_int16_t,
- int, struct mbuf *);
int pf_test_fragment(struct pf_rule **, int,
struct pfi_kif *, struct mbuf *, void *,
struct pf_pdesc *, struct pf_rule **,
@@ -2767,7 +2775,7 @@ int
pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction,
struct pfi_kif *kif, struct mbuf *m, int off, void *h,
struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm,
- struct ifqueue *ifq)
+ struct ifqueue *ifq, int hdrlen)
{
struct pf_rule *lastr = NULL;
sa_family_t af = pd->af;
@@ -2780,7 +2788,7 @@ pf_test_rule(struct pf_rule **rm, struct
struct pf_state_key *skw = NULL, *sks = NULL;
struct pf_rule_actions act;
u_short reason;
- int rewrite = 0, hdrlen = 0;
+ int rewrite = 0;
int tag = -1;
int asd = 0;
int match = 0;
@@ -2806,18 +2814,15 @@ pf_test_rule(struct pf_rule **rm, struct
case IPPROTO_TCP:
pd->nsport = th->th_sport;
pd->ndport = th->th_dport;
- hdrlen = sizeof(*th);
break;
case IPPROTO_UDP:
pd->nsport = pd->hdr.udp->uh_sport;
pd->ndport = pd->hdr.udp->uh_dport;
- hdrlen = sizeof(*pd->hdr.udp);
break;
#ifdef INET
case IPPROTO_ICMP:
if (pd->af != AF_INET)
break;
- hdrlen = ICMP_MINLEN;
icmptype = pd->hdr.icmp->icmp_type;
icmpcode = pd->hdr.icmp->icmp_code;
state_icmp = pf_icmp_mapping(pd, icmptype,
@@ -2835,7 +2840,6 @@ pf_test_rule(struct pf_rule **rm, struct
case IPPROTO_ICMPV6:
if (af != AF_INET6)
break;
- hdrlen = sizeof(*pd->hdr.icmp6);
icmptype = pd->hdr.icmp6->icmp6_type;
icmpcode = pd->hdr.icmp6->icmp6_code;
state_icmp = pf_icmp_mapping(pd, icmptype,
@@ -2850,7 +2854,7 @@ pf_test_rule(struct pf_rule **rm, struct
break;
#endif /* INET6 */
default:
- pd->nsport = pd->ndport = hdrlen = 0;
+ pd->nsport = pd->ndport;
break;
}
@@ -3068,11 +3072,10 @@ pf_test_rule(struct pf_rule **rm, struct
sk = sks;
else
sk = skw;
- pf_translate(pd,
+ rewrite += pf_translate(pd,
&sk->addr[pd->sidx], sk->port[pd->sidx],
&sk->addr[pd->didx], sk->port[pd->didx],
virtual_type, icmp_dir, m);
- rewrite = 1;
}
} else {
while ((ri = SLIST_FIRST(&rules))) {
@@ -3302,41 +3305,60 @@ csfailed:
return (PF_DROP);
}
-void
+int
pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport,
struct pf_addr *daddr, u_int16_t dport, u_int16_t virtual_type,
int icmp_dir, struct mbuf *m)
{
+ /*
+ * when called from bpf_mtap_pflog, there are extra constraints:
+ * -mbuf is faked, m_data is the bpf buffer
+ * -pd is not fully set up
+ */
+ int rewrite = 0;
+
if (PF_ANEQ(daddr, pd->dst, pd->af))
pd->destchg = 1;
switch (pd->proto) {
case IPPROTO_TCP:
- if (PF_ANEQ(saddr, pd->src, pd->af) || *pd->sport != sport)
+ if (PF_ANEQ(saddr, pd->src, pd->af) || *pd->sport != sport) {
pf_change_ap(pd->src, pd->sport, pd->ip_sum,
- &pd->hdr.tcp->th_sum, saddr, sport, 0, pd->af);
- if (PF_ANEQ(daddr, pd->dst, pd->af) || *pd->dport != dport)
+ &pd->hdr.tcp->th_sum, saddr, sport, 0, pd->af);
+ rewrite = 1;
+ }
+ if (PF_ANEQ(daddr, pd->dst, pd->af) || *pd->dport != dport) {
pf_change_ap(pd->dst, pd->dport, pd->ip_sum,
&pd->hdr.tcp->th_sum, daddr, dport, 0, pd->af);
+ rewrite = 1;
+ }
break;
case IPPROTO_UDP:
- if (PF_ANEQ(saddr, pd->src, pd->af) || *pd->sport != sport)
+ if (PF_ANEQ(saddr, pd->src, pd->af) || *pd->sport != sport) {
pf_change_ap(pd->src, pd->sport, pd->ip_sum,
&pd->hdr.udp->uh_sum, saddr, sport, 1, pd->af);
- if (PF_ANEQ(daddr, pd->dst, pd->af) || *pd->dport != dport)
+ rewrite = 1;
+ }
+ if (PF_ANEQ(daddr, pd->dst, pd->af) || *pd->dport != dport) {
pf_change_ap(pd->dst, pd->dport, pd->ip_sum,
&pd->hdr.udp->uh_sum, daddr, dport, 1, pd->af);
+ rewrite = 1;
+ }
break;
#ifdef INET
case IPPROTO_ICMP:
- if (PF_ANEQ(saddr, pd->src, pd->af))
+ if (PF_ANEQ(saddr, pd->src, pd->af)) {
pf_change_a(&pd->src->v4.s_addr, pd->ip_sum,
saddr->v4.s_addr, 0);
- if (PF_ANEQ(daddr, pd->dst, pd->af))
+ rewrite = 1;
+ }
+ if (PF_ANEQ(daddr, pd->dst, pd->af)) {
pf_change_a(&pd->dst->v4.s_addr, pd->ip_sum,
daddr->v4.s_addr, 0);
+ rewrite = 1;
+ }
if (virtual_type == htons(ICMP_ECHO)) {
u_int16_t icmpid = (icmp_dir == PF_IN) ? sport : dport;
@@ -3345,6 +3367,7 @@ pf_translate(struct pf_pdesc *pd, struct
pd->hdr.icmp->icmp_cksum,
pd->hdr.icmp->icmp_id, icmpid, 0);
pd->hdr.icmp->icmp_id = icmpid;
+ rewrite = 1;
}
}
break;
@@ -3353,12 +3376,16 @@ pf_translate(struct pf_pdesc *pd, struct
#ifdef INET6
case IPPROTO_ICMPV6:
if (pd->af == AF_INET6) {
- if (PF_ANEQ(saddr, pd->src, pd->af))
+ if (PF_ANEQ(saddr, pd->src, pd->af)) {
pf_change_a6(pd->src,
&pd->hdr.icmp6->icmp6_cksum, saddr, 0);
- if (PF_ANEQ(daddr, pd->dst, pd->af))
+ rewrite = 1;
+ }
+ if (PF_ANEQ(daddr, pd->dst, pd->af)) {
pf_change_a6(pd->dst,
&pd->hdr.icmp6->icmp6_cksum, daddr, 0);
+ rewrite = 1;
+ }
break;
}
/* FALLTHROUGH */
@@ -3368,24 +3395,33 @@ pf_translate(struct pf_pdesc *pd, struct
switch (pd->af) {
#ifdef INET
case AF_INET:
- if (PF_ANEQ(saddr, pd->src, pd->af))
+ if (PF_ANEQ(saddr, pd->src, pd->af)) {
pf_change_a(&pd->src->v4.s_addr, pd->ip_sum,
saddr->v4.s_addr, 0);
- if (PF_ANEQ(daddr, pd->dst, pd->af))
+ rewrite = 1;
+ }
+ if (PF_ANEQ(daddr, pd->dst, pd->af)) {
pf_change_a(&pd->dst->v4.s_addr, pd->ip_sum,
daddr->v4.s_addr, 0);
+ rewrite = 1;
+ }
break;
#endif /* INET */
#ifdef INET6
case AF_INET6:
- if (PF_ANEQ(saddr, pd->src, pd->af))
+ if (PF_ANEQ(saddr, pd->src, pd->af)) {
pf_change_a6(pd->src, pd->ip_sum, saddr, 0);
- if (PF_ANEQ(daddr, pd->dst, pd->af))
+ rewrite = 1;
+ }
+ if (PF_ANEQ(daddr, pd->dst, pd->af)) {
pf_change_a6(pd->dst, pd->ip_sum, daddr, 0);
+ rewrite = 1;
+ }
break;
#endif /* INET6 */
}
}
+ return (rewrite);
}
int
@@ -5517,6 +5553,236 @@ pf_get_divert(struct mbuf *m)
return ((struct pf_divert *)(mtag + 1));
}
+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_kif *kif, struct pf_rule **a,
+ struct pf_rule **r, struct pf_ruleset **ruleset, int *off, int *hdrlen)
+{
+ if (pd->hdr.any == NULL)
+ panic("pf_setup_pdesc: no storage for headers provided");
+
+ switch (af) {
+#ifdef INET
+ case AF_INET: {
+ struct ip *h;
+
+ h = mtod(m, struct ip *);
+ *off = h->ip_hl << 2;
+ if (*off < (int)sizeof(*h)) {
+ *action = PF_DROP;
+ REASON_SET(reason, PFRES_SHORT);
+ return (-1);
+ }
+ pd->src = (struct pf_addr *)&h->ip_src;
+ pd->dst = (struct pf_addr *)&h->ip_dst;
+ pd->sport = pd->dport = NULL;
+ pd->ip_sum = &h->ip_sum;
+ pd->proto_sum = NULL;
+ pd->proto = h->ip_p;
+ pd->dir = dir;
+ pd->sidx = (dir == PF_IN) ? 0 : 1;
+ pd->didx = (dir == PF_IN) ? 1 : 0;
+ pd->af = AF_INET;
+ pd->tos = h->ip_tos;
+ pd->tot_len = ntohs(h->ip_len);
+ pd->rdomain = rtable_l2(m->m_pkthdr.rdomain);
+
+ /* fragments not reassembled handled later */
+ if (h->ip_off & htons(IP_MF | IP_OFFMASK))
+ return (0);
+
+ switch (h->ip_p) {
+ case IPPROTO_TCP: {
+ struct tcphdr *th = pd->hdr.tcp;
+
+ if (!pf_pull_hdr(m, *off, th, sizeof(*th),
+ action, reason, AF_INET))
+ return (-1);
+ *hdrlen = sizeof(*th);
+ pd->p_len = pd->tot_len - *off - (th->th_off << 2);
+ pd->sport = &th->th_sport;
+ pd->dport = &th->th_dport;
+ break;
+ }
+ case IPPROTO_UDP: {
+ struct udphdr *uh = pd->hdr.udp;
+
+ if (!pf_pull_hdr(m, *off, uh, sizeof(*uh),
+ action, reason, AF_INET))
+ return (-1);
+ *hdrlen = sizeof(*uh);
+ if (uh->uh_dport == 0 ||
+ ntohs(uh->uh_ulen) > m->m_pkthdr.len - *off ||
+ ntohs(uh->uh_ulen) < sizeof(struct udphdr)) {
+ *action = PF_DROP;
+ REASON_SET(reason, PFRES_SHORT);
+ return (-1);
+ }
+ pd->sport = &uh->uh_sport;
+ pd->dport = &uh->uh_dport;
+ break;
+ }
+ case IPPROTO_ICMP: {
+ if (!pf_pull_hdr(m, *off, pd->hdr.icmp, ICMP_MINLEN,
+ action, reason, AF_INET))
+ return (-1);
+ *hdrlen = ICMP_MINLEN;
+ break;
+ }
+ }
+ break;
+ }
+#endif
+#ifdef INET6
+ case AF_INET6: {
+ struct ip6_hdr *h;
+ int terminal = 0;
+
+ h = mtod(m, struct ip6_hdr *);
+ pd->src = (struct pf_addr *)&h->ip6_src;
+ pd->dst = (struct pf_addr *)&h->ip6_dst;
+ pd->sport = pd->dport = NULL;
+ pd->ip_sum = NULL;
+ pd->proto_sum = NULL;
+ pd->dir = dir;
+ pd->sidx = (dir == PF_IN) ? 0 : 1;
+ pd->didx = (dir == PF_IN) ? 1 : 0;
+ pd->af = AF_INET6;
+ pd->tos = 0;
+ 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;
+ do {
+ switch (pd->proto) {
+ case IPPROTO_FRAGMENT:
+ if (kif == NULL || r == NULL) /* pflog */
+ *action = PF_DROP;
+ else
+ *action = pf_test_fragment(r, dir, kif,
+ m, h, pd, a, ruleset);
+ if (*action == PF_DROP)
+ REASON_SET(reason, PFRES_FRAG);
+ return (-1);
+ case IPPROTO_ROUTING: {
+ struct ip6_rthdr rthdr;
+
+ if (pd->rh_cnt++) {
+ DPFPRINTF(LOG_NOTICE,
+ "pf: IPv6 more than one rthdr");
+ *action = PF_DROP;
+ REASON_SET(reason, PFRES_IPOPTIONS);
+ return (-1);
+ }
+ if (!pf_pull_hdr(m, *off, &rthdr, sizeof(rthdr),
+ NULL, reason, pd->af)) {
+ DPFPRINTF(LOG_NOTICE,
+ "pf: IPv6 short rthdr");
+ *action = PF_DROP;
+ REASON_SET(reason, PFRES_SHORT);
+ return (-1);
+ }
+ if (rthdr.ip6r_type == IPV6_RTHDR_TYPE_0) {
+ DPFPRINTF(LOG_NOTICE,
+ "pf: IPv6 rthdr0");
+ *action = PF_DROP;
+ REASON_SET(reason, PFRES_IPOPTIONS);
+ return (-1);
+ }
+ /* FALLTHROUGH */
+ }
+ case IPPROTO_AH:
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS: {
+ /* get next header and header length */
+ struct ip6_ext opt6;
+
+ if (!pf_pull_hdr(m, *off, &opt6, sizeof(opt6),
+ NULL, reason, pd->af)) {
+ DPFPRINTF(LOG_NOTICE,
+ "pf: IPv6 short opt");
+ *action = PF_DROP;
+ return (-1);
+ }
+ if (pd->proto == IPPROTO_AH)
+ *off += (opt6.ip6e_len + 2) * 4;
+ else
+ *off += (opt6.ip6e_len + 1) * 8;
+ pd->proto = opt6.ip6e_nxt;
+ /* goto the next header */
+ break;
+ }
+ default:
+ terminal++;
+ break;
+ }
+ } while (!terminal);
+
+ switch (pd->proto) {
+ case IPPROTO_TCP: {
+ struct tcphdr *th = pd->hdr.tcp;
+
+ if (!pf_pull_hdr(m, *off, th, sizeof(*th),
+ action, reason, AF_INET6))
+ return (-1);
+ *hdrlen = sizeof(*th);
+ pd->p_len = pd->tot_len - *off - (th->th_off << 2);
+ pd->sport = &th->th_sport;
+ pd->dport = &th->th_dport;
+ break;
+ }
+ case IPPROTO_UDP: {
+ struct udphdr *uh = pd->hdr.udp;
+
+ if (!pf_pull_hdr(m, *off, uh, sizeof(*uh),
+ action, reason, AF_INET6))
+ return (-1);
+ *hdrlen = sizeof(*uh);
+ if (uh->uh_dport == 0 ||
+ ntohs(uh->uh_ulen) > m->m_pkthdr.len - *off ||
+ ntohs(uh->uh_ulen) < sizeof(struct udphdr)) {
+ *action = PF_DROP;
+ REASON_SET(reason, PFRES_SHORT);
+ return (-1);
+ }
+ pd->sport = &uh->uh_sport;
+ pd->dport = &uh->uh_dport;
+ break;
+ }
+ case IPPROTO_ICMPV6: {
+ size_t icmp_hlen = sizeof(struct icmp6_hdr);
+
+ if (!pf_pull_hdr(m, *off, pd->hdr.icmp6, icmp_hlen,
+ action, reason, AF_INET6))
+ return (-1);
+ /* ICMP headers we look further into to match state */
+ switch (pd->hdr.icmp6->icmp6_type) {
+ case MLD_LISTENER_QUERY:
+ case MLD_LISTENER_REPORT:
+ icmp_hlen = sizeof(struct mld_hdr);
+ break;
+ case ND_NEIGHBOR_SOLICIT:
+ case ND_NEIGHBOR_ADVERT:
+ icmp_hlen = sizeof(struct nd_neighbor_solicit);
+ break;
+ }
+ if (icmp_hlen > sizeof(struct icmp6_hdr) &&
+ !pf_pull_hdr(m, *off, pd->hdr.icmp6, icmp_hlen,
+ action, reason, AF_INET6))
+ return (-1);
+ *hdrlen = icmp_hlen;
+ break;
+ }
+ }
+ break;
+ }
+#endif
+ default:
+ panic("pf_setup_pdesc called with illegal af %u", af);
+
+ }
+ return (0);
+}
+
#ifdef INET
int
pf_test(int dir, struct ifnet *ifp, struct mbuf **m0,
@@ -5530,13 +5796,14 @@ pf_test(int dir, struct ifnet *ifp, stru
struct pf_state *s = NULL;
struct pf_ruleset *ruleset = NULL;
struct pf_pdesc pd;
- int off, dirndx, pqid = 0;
+ int off, hdrlen, dirndx, pqid = 0;
u_int16_t qid;
if (!pf_status.running)
return (PF_PASS);
memset(&pd, 0, sizeof(pd));
+ pd.hdr.any = &pf_hdrs;
if (ifp->if_type == IFT_CARP && ifp->if_carpdev)
kif = (struct pfi_kif *)ifp->if_carpdev->if_pf_kif;
else
@@ -5578,28 +5845,13 @@ pf_test(int dir, struct ifnet *ifp, stru
m = *m0; /* pf_normalize messes with m0 */
h = mtod(m, struct ip *);
- off = h->ip_hl << 2;
- if (off < (int)sizeof(*h)) {
- action = PF_DROP;
- REASON_SET(&reason, PFRES_SHORT);
- pflog |= PF_LOG_FORCE;
+ if (pf_setup_pdesc(AF_INET, dir, &pd, m, &action, &reason, kif, &a, &r,
+ &ruleset, &off, &hdrlen) == -1) {
+ if (action != PF_PASS)
+ pflog |= PF_LOG_FORCE;
goto done;
}
-
- pd.src = (struct pf_addr *)&h->ip_src;
- pd.dst = (struct pf_addr *)&h->ip_dst;
- pd.sport = pd.dport = NULL;
- pd.ip_sum = &h->ip_sum;
- pd.proto_sum = NULL;
- pd.proto = h->ip_p;
- pd.dir = dir;
- pd.sidx = (dir == PF_IN) ? 0 : 1;
- pd.didx = (dir == PF_IN) ? 1 : 0;
- pd.af = AF_INET;
- pd.tos = h->ip_tos;
- pd.tot_len = ntohs(h->ip_len);
pd.eh = eh;
- pd.rdomain = rtable_l2(m->m_pkthdr.rdomain);
/* handle fragments that didn't get reassembled by normalization */
if (h->ip_off & htons(IP_MF | IP_OFFMASK)) {
@@ -5611,20 +5863,8 @@ pf_test(int dir, struct ifnet *ifp, stru
switch (h->ip_p) {
case IPPROTO_TCP: {
- struct tcphdr th;
-
- pd.hdr.tcp = &th;
- if (!pf_pull_hdr(m, off, &th, sizeof(th),
- &action, &reason, AF_INET)) {
- if (action != PF_PASS)
- pflog |= PF_LOG_FORCE;
- goto done;
- }
- pd.p_len = pd.tot_len - off - (th.th_off << 2);
- if ((th.th_flags & TH_ACK) && pd.p_len == 0)
+ if ((pd.hdr.tcp->th_flags & TH_ACK) && pd.p_len == 0)
pqid = 1;
- pd.sport = &th.th_sport;
- pd.dport = &th.th_dport;
action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd);
if (action == PF_DROP)
goto done;
@@ -5639,7 +5879,7 @@ pf_test(int dir, struct ifnet *ifp, stru
pflog |= s->log;
} else if (s == NULL)
action = pf_test_rule(&r, &s, dir, kif,
- m, off, h, &pd, &a, &ruleset, &ipintrq);
+ m, off, h, &pd, &a, &ruleset, &ipintrq, hdrlen);
if (s) {
if (s->max_mss)
@@ -5651,24 +5891,6 @@ pf_test(int dir, struct ifnet *ifp, stru
}
case IPPROTO_UDP: {
- struct udphdr uh;
-
- pd.hdr.udp = &uh;
- if (!pf_pull_hdr(m, off, &uh, sizeof(uh),
- &action, &reason, AF_INET)) {
- if (action != PF_PASS)
- pflog |= PF_LOG_FORCE;
- goto done;
- }
- if (uh.uh_dport == 0 ||
- ntohs(uh.uh_ulen) > m->m_pkthdr.len - off ||
- ntohs(uh.uh_ulen) < sizeof(struct udphdr)) {
- action = PF_DROP;
- REASON_SET(&reason, PFRES_SHORT);
- goto done;
- }
- pd.sport = &uh.uh_sport;
- pd.dport = &uh.uh_dport;
action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd);
if (action == PF_PASS) {
#if NPFSYNC > 0
@@ -5679,20 +5901,11 @@ pf_test(int dir, struct ifnet *ifp, stru
pflog |= s->log;
} else if (s == NULL)
action = pf_test_rule(&r, &s, dir, kif,
- m, off, h, &pd, &a, &ruleset, &ipintrq);
+ m, off, h, &pd, &a, &ruleset, &ipintrq, hdrlen);
break;
}
case IPPROTO_ICMP: {
- struct icmp ih;
-
- pd.hdr.icmp = &ih;
- if (!pf_pull_hdr(m, off, &ih, ICMP_MINLEN,
- &action, &reason, AF_INET)) {
- if (action != PF_PASS)
- pflog |= PF_LOG_FORCE;
- goto done;
- }
action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd,
&reason);
if (action == PF_PASS) {
@@ -5704,7 +5917,7 @@ pf_test(int dir, struct ifnet *ifp, stru
pflog |= s->log;
} else if (s == NULL)
action = pf_test_rule(&r, &s, dir, kif,
- m, off, h, &pd, &a, &ruleset, &ipintrq);
+ m, off, h, &pd, &a, &ruleset, &ipintrq, hdrlen);
break;
}
@@ -5726,7 +5939,7 @@ pf_test(int dir, struct ifnet *ifp, stru
pflog |= s->log;
} else if (s == NULL)
action = pf_test_rule(&r, &s, dir, kif, m, off, h,
- &pd, &a, &ruleset, &ipintrq);
+ &pd, &a, &ruleset, &ipintrq, hdrlen);
break;
}
@@ -5891,12 +6104,13 @@ pf_test6(int dir, struct ifnet *ifp, str
struct pf_state *s = NULL;
struct pf_ruleset *ruleset = NULL;
struct pf_pdesc pd;
- int off, terminal = 0, dirndx, rh_cnt = 0;
+ int off, hdrlen, dirndx;
if (!pf_status.running)
return (PF_PASS);
memset(&pd, 0, sizeof(pd));
+ pd.hdr.any = &pf_hdrs;
if (ifp->if_type == IFT_CARP && ifp->if_carpdev)
kif = (struct pfi_kif *)ifp->if_carpdev->if_pf_kif;
else
@@ -5949,105 +6163,20 @@ pf_test6(int dir, struct ifnet *ifp, str
}
#endif
- pd.src = (struct pf_addr *)&h->ip6_src;
- pd.dst = (struct pf_addr *)&h->ip6_dst;
- pd.sport = pd.dport = NULL;
- pd.ip_sum = NULL;
- pd.proto_sum = NULL;
- pd.dir = dir;
- pd.sidx = (dir == PF_IN) ? 0 : 1;
- pd.didx = (dir == PF_IN) ? 1 : 0;
- pd.af = AF_INET6;
- pd.tos = 0;
- pd.tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr);
- pd.eh = eh;
-
- off = ((caddr_t)h - m->m_data) + sizeof(struct ip6_hdr);
- pd.proto = h->ip6_nxt;
- do {
- switch (pd.proto) {
- case IPPROTO_FRAGMENT:
- action = pf_test_fragment(&r, dir, kif, m, h,
- &pd, &a, &ruleset);
- if (action == PF_DROP)
- REASON_SET(&reason, PFRES_FRAG);
- goto done;
- case IPPROTO_ROUTING: {
- struct ip6_rthdr rthdr;
-
- if (rh_cnt++) {
- DPFPRINTF(LOG_NOTICE,
- "pf: IPv6 more than one rthdr");
- action = PF_DROP;
- REASON_SET(&reason, PFRES_IPOPTIONS);
- pflog |= PF_LOG_FORCE;
- goto done;
- }
- if (!pf_pull_hdr(m, off, &rthdr, sizeof(rthdr), NULL,
- &reason, pd.af)) {
- DPFPRINTF(LOG_NOTICE,
- "pf: IPv6 short rthdr");
- action = PF_DROP;
- REASON_SET(&reason, PFRES_SHORT);
- pflog |= PF_LOG_FORCE;
- goto done;
- }
- if (rthdr.ip6r_type == IPV6_RTHDR_TYPE_0) {
- DPFPRINTF(LOG_NOTICE,
- "pf: IPv6 rthdr0");
- action = PF_DROP;
- REASON_SET(&reason, PFRES_IPOPTIONS);
- pflog |= PF_LOG_FORCE;
- goto done;
- }
- /* FALLTHROUGH */
- }
- case IPPROTO_AH:
- case IPPROTO_HOPOPTS:
- case IPPROTO_DSTOPTS: {
- /* get next header and header length */
- struct ip6_ext opt6;
-
- if (!pf_pull_hdr(m, off, &opt6, sizeof(opt6),
- NULL, &reason, pd.af)) {
- DPFPRINTF(LOG_NOTICE,
- "pf: IPv6 short opt");
- action = PF_DROP;
- pflog |= PF_LOG_FORCE;
- goto done;
- }
- if (pd.proto == IPPROTO_AH)
- off += (opt6.ip6e_len + 2) * 4;
- else
- off += (opt6.ip6e_len + 1) * 8;
- pd.proto = opt6.ip6e_nxt;
- /* goto the next header */
- break;
- }
- default:
- terminal++;
- break;
- }
- } while (!terminal);
-
/* ptr to original, normalization can get us a new one */
n = m;
+ if (pf_setup_pdesc(AF_INET6, dir, &pd, m, &action, &reason, kif, &a, &r,
+ &ruleset, &off, &hdrlen) == -1) {
+ if (action != PF_PASS)
+ pflog |= PF_LOG_FORCE;
+ goto done;
+ }
+ pd.eh = eh;
+
switch (pd.proto) {
case IPPROTO_TCP: {
- struct tcphdr th;
-
- pd.hdr.tcp = &th;
- if (!pf_pull_hdr(m, off, &th, sizeof(th),
- &action, &reason, AF_INET6)) {
- if (action != PF_PASS)
- pflog |= PF_LOG_FORCE;
- goto done;
- }
- pd.p_len = pd.tot_len - off - (th.th_off << 2);
- pd.sport = &th.th_sport;
- pd.dport = &th.th_dport;
action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd);
if (action == PF_DROP)
goto done;
@@ -6062,7 +6191,7 @@ pf_test6(int dir, struct ifnet *ifp, str
pflog |= s->log;
} else if (s == NULL)
action = pf_test_rule(&r, &s, dir, kif,
- m, off, h, &pd, &a, &ruleset, &ip6intrq);
+ m, off, h, &pd, &a, &ruleset, &ip6intrq, hdrlen);
if (s) {
if (s->max_mss)
@@ -6074,24 +6203,6 @@ pf_test6(int dir, struct ifnet *ifp, str
}
case IPPROTO_UDP: {
- struct udphdr uh;
-
- pd.hdr.udp = &uh;
- if (!pf_pull_hdr(m, off, &uh, sizeof(uh),
- &action, &reason, AF_INET6)) {
- if (action != PF_PASS)
- pflog |= PF_LOG_FORCE;
- goto done;
- }
- if (uh.uh_dport == 0 ||
- ntohs(uh.uh_ulen) > m->m_pkthdr.len - off ||
- ntohs(uh.uh_ulen) < sizeof(struct udphdr)) {
- action = PF_DROP;
- REASON_SET(&reason, PFRES_SHORT);
- goto done;
- }
- pd.sport = &uh.uh_sport;
- pd.dport = &uh.uh_dport;
action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd);
if (action == PF_PASS) {
#if NPFSYNC > 0
@@ -6102,7 +6213,7 @@ pf_test6(int dir, struct ifnet *ifp, str
pflog |= s->log;
} else if (s == NULL)
action = pf_test_rule(&r, &s, dir, kif,
- m, off, h, &pd, &a, &ruleset, &ip6intrq);
+ m, off, h, &pd, &a, &ruleset, &ip6intrq, hdrlen);
break;
}
@@ -6114,38 +6225,6 @@ pf_test6(int dir, struct ifnet *ifp, str
}
case IPPROTO_ICMPV6: {
- union {
- struct icmp6_hdr icmp6;
- struct mld_hdr mld;
- struct nd_neighbor_solicit nd;
- } ih;
- size_t icmp_hlen = sizeof(struct icmp6_hdr);
-
- pd.hdr.icmp6 = &ih.icmp6;
- if (!pf_pull_hdr(m, off, &ih, icmp_hlen,
- &action, &reason, AF_INET6)) {
- if (action != PF_PASS)
- pflog |= PF_LOG_FORCE;
- goto done;
- }
- /* ICMP headers we look further into to match state */
- switch (ih.icmp6.icmp6_type) {
- case MLD_LISTENER_QUERY:
- case MLD_LISTENER_REPORT:
- icmp_hlen = sizeof(struct mld_hdr);
- break;
- case ND_NEIGHBOR_SOLICIT:
- case ND_NEIGHBOR_ADVERT:
- icmp_hlen = sizeof(struct nd_neighbor_solicit);
- break;
- }
- if (icmp_hlen > sizeof(struct icmp6_hdr) &&
- !pf_pull_hdr(m, off, &ih, icmp_hlen,
- &action, &reason, AF_INET6)) {
- if (action != PF_PASS)
- pflog |= PF_LOG_FORCE;
- goto done;
- }
action = pf_test_state_icmp(&s, dir, kif,
m, off, h, &pd, &reason);
if (action == PF_PASS) {
@@ -6157,7 +6236,7 @@ pf_test6(int dir, struct ifnet *ifp, str
pflog |= s->log;
} else if (s == NULL)
action = pf_test_rule(&r, &s, dir, kif,
- m, off, h, &pd, &a, &ruleset, &ip6intrq);
+ m, off, h, &pd, &a, &ruleset, &ip6intrq, hdrlen);
break;
}
@@ -6172,7 +6251,7 @@ pf_test6(int dir, struct ifnet *ifp, str
pflog |= s->log;
} else if (s == NULL)
action = pf_test_rule(&r, &s, dir, kif, m, off, h,
- &pd, &a, &ruleset, &ip6intrq);
+ &pd, &a, &ruleset, &ip6intrq, hdrlen);
break;
}
@@ -6184,7 +6263,7 @@ done:
}
/* handle dangerous IPv6 extension headers. */
- if (action == PF_PASS && rh_cnt &&
+ if (action == PF_PASS && pd.rh_cnt &&
!((s && s->state_flags & PFSTATE_ALLOWOPTS) || r->allow_opts)) {
action = PF_DROP;
REASON_SET(&reason, PFRES_IPOPTIONS);
Index: sys/net/pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pfvar.h,v
retrieving revision 1.311
diff -u -p -r1.311 pfvar.h
--- sys/net/pfvar.h 28 Jun 2010 23:21:41 -0000 1.311
+++ sys/net/pfvar.h 20 Sep 2010 08:54:41 -0000
@@ -1211,6 +1211,7 @@ struct pf_pdesc {
u_int16_t ndport; /* dst port after NAT */
u_int32_t p_len; /* total length of payload */
+ u_int32_t rh_cnt; /* # of routing headers */
u_int16_t *ip_sum;
u_int16_t *proto_sum;
@@ -1735,6 +1736,11 @@ extern void pf_addrcpy(struct
pf_addr
void pf_rm_rule(struct pf_rulequeue *,
struct pf_rule *);
struct pf_divert *pf_find_divert(struct mbuf *);
+int pf_setup_pdesc(sa_family_t, int,
+ struct pf_pdesc *, struct mbuf *,
+ u_short *, u_short *, struct pfi_kif *,
+ struct pf_rule **, struct pf_rule **,
+ struct pf_ruleset **, int *, int *);
#ifdef INET
int pf_test(int, struct ifnet *, struct mbuf **, struct ether_header *);
@@ -1790,6 +1796,8 @@ int pf_socket_lookup(int, struct pf_pdes
struct pf_state_key *pf_alloc_state_key(int);
void pf_pkt_addr_changed(struct mbuf *);
int pf_state_key_attach(struct pf_state_key *, struct pf_state *, int);
+int pf_translate(struct pf_pdesc *, struct pf_addr *, u_int16_t,
+ struct pf_addr *, u_int16_t, u_int16_t, int, struct mbuf *);
void pfr_initialize(void);
int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t);