aboutsummaryrefslogtreecommitdiff
path: root/net/pf.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/pf.c')
-rw-r--r--net/pf.c205
1 files changed, 160 insertions, 45 deletions
diff --git a/net/pf.c b/net/pf.c
index f4b242a74747..b99b767f933b 100644
--- a/net/pf.c
+++ b/net/pf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf.c,v 1.552 2007/08/21 15:57:27 dhartmei Exp $ */
+/* $OpenBSD: pf.c,v 1.567 2008/02/20 23:40:13 henning Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -51,6 +51,8 @@
#include <sys/proc.h>
#include <sys/rwlock.h>
+#include <crypto/md5.h>
+
#include <net/if.h>
#include <net/if_types.h>
#include <net/bpf.h>
@@ -110,6 +112,11 @@ u_int32_t ticket_altqs_inactive;
int altqs_inactive_open;
u_int32_t ticket_pabuf;
+MD5_CTX pf_tcp_secret_ctx;
+u_char pf_tcp_secret[16];
+int pf_tcp_secret_init;
+int pf_tcp_iss_off;
+
struct pf_anchor_stackframe {
struct pf_ruleset *rs;
struct pf_rule *r;
@@ -160,6 +167,7 @@ struct pf_rule *pf_get_translation(struct pf_pdesc *, struct mbuf *,
void pf_attach_state(struct pf_state_key *,
struct pf_state *, int);
void pf_detach_state(struct pf_state *, int);
+u_int32_t pf_tcp_iss(struct pf_pdesc *);
int pf_test_rule(struct pf_rule **, struct pf_state **,
int, struct pfi_kif *, struct mbuf *, int,
void *, struct pf_pdesc *, struct pf_rule **,
@@ -214,7 +222,7 @@ int pf_check_proto_cksum(struct mbuf *, int, int,
int pf_addr_wrap_neq(struct pf_addr_wrap *,
struct pf_addr_wrap *);
struct pf_state *pf_find_state(struct pfi_kif *,
- struct pf_state_key_cmp *, u_int8_t);
+ struct pf_state_key_cmp *, u_int);
int pf_src_connlimit(struct pf_state **);
void pf_stateins_err(const char *, struct pf_state *,
struct pfi_kif *);
@@ -233,10 +241,7 @@ struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = {
#define STATE_LOOKUP() \
do { \
- if (direction == PF_IN) \
- *state = pf_find_state(kif, &key, PF_EXT_GWY); \
- else \
- *state = pf_find_state(kif, &key, PF_LAN_EXT); \
+ *state = pf_find_state(kif, &key, direction); \
if (*state == NULL || (*state)->timeout == PFTM_PURGE) \
return (PF_DROP); \
if (direction == PF_OUT && \
@@ -526,19 +531,19 @@ pf_find_state_byid(struct pf_state_cmp *key)
}
struct pf_state *
-pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int8_t tree)
+pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int dir)
{
struct pf_state_key *sk;
struct pf_state *s;
pf_status.fcounters[FCNT_STATE_SEARCH]++;
- switch (tree) {
- case PF_LAN_EXT:
+ switch (dir) {
+ case PF_OUT:
sk = RB_FIND(pf_state_tree_lan_ext, &pf_statetbl_lan_ext,
(struct pf_state_key *)key);
break;
- case PF_EXT_GWY:
+ case PF_IN:
sk = RB_FIND(pf_state_tree_ext_gwy, &pf_statetbl_ext_gwy,
(struct pf_state_key *)key);
break;
@@ -556,19 +561,19 @@ pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int8_t tree)
}
struct pf_state *
-pf_find_state_all(struct pf_state_key_cmp *key, u_int8_t tree, int *more)
+pf_find_state_all(struct pf_state_key_cmp *key, u_int dir, int *more)
{
struct pf_state_key *sk;
struct pf_state *s, *ret = NULL;
pf_status.fcounters[FCNT_STATE_SEARCH]++;
- switch (tree) {
- case PF_LAN_EXT:
+ switch (dir) {
+ case PF_OUT:
sk = RB_FIND(pf_state_tree_lan_ext,
&pf_statetbl_lan_ext, (struct pf_state_key *)key);
break;
- case PF_EXT_GWY:
+ case PF_IN:
sk = RB_FIND(pf_state_tree_ext_gwy,
&pf_statetbl_ext_gwy, (struct pf_state_key *)key);
break;
@@ -819,6 +824,8 @@ pf_insert_state(struct pfi_kif *kif, struct pf_state *s)
TAILQ_FOREACH(sp, &cur->states, next)
if (sp->kif == kif) { /* collision! */
pf_stateins_err("tree_lan_ext", s, kif);
+ pf_detach_state(s,
+ PF_DT_SKIP_LANEXT|PF_DT_SKIP_EXTGWY);
return (-1);
}
pf_detach_state(s, PF_DT_SKIP_LANEXT|PF_DT_SKIP_EXTGWY);
@@ -961,10 +968,8 @@ pf_src_tree_remove_state(struct pf_state *s)
u_int32_t timeout;
if (s->src_node != NULL) {
- if (s->state_key->proto == IPPROTO_TCP) {
- if (s->src.tcp_est)
- --s->src_node->conn;
- }
+ if (s->src.tcp_est)
+ --s->src_node->conn;
if (--s->src_node->states <= 0) {
timeout = s->rule.ptr->timeout[PFTM_SRC_NODE];
if (!timeout)
@@ -1295,6 +1300,7 @@ pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2)
return (1);
switch (aw1->type) {
case PF_ADDR_ADDRMASK:
+ case PF_ADDR_RANGE:
if (PF_ANEQ(&aw1->v.a.addr, &aw2->v.a.addr, 0))
return (1);
if (PF_ANEQ(&aw1->v.a.mask, &aw2->v.a.mask, 0))
@@ -1598,7 +1604,7 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af,
m->m_pkthdr.pf.tag = rtag;
if (r != NULL && r->rtableid >= 0)
- m->m_pkthdr.pf.rtableid = m->m_pkthdr.pf.rtableid;
+ m->m_pkthdr.pf.rtableid = r->rtableid;
#ifdef ALTQ
if (r != NULL && r->qid) {
@@ -1790,6 +1796,44 @@ pf_match_addr(u_int8_t n, struct pf_addr *a, struct pf_addr *m,
}
}
+/*
+ * Return 1 if b <= a <= e, otherwise return 0.
+ */
+int
+pf_match_addr_range(struct pf_addr *b, struct pf_addr *e,
+ struct pf_addr *a, sa_family_t af)
+{
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ if ((a->addr32[0] < b->addr32[0]) ||
+ (a->addr32[0] > e->addr32[0]))
+ return (0);
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6: {
+ int i;
+
+ /* check a >= b */
+ for (i = 0; i < 4; ++i)
+ if (a->addr32[i] > b->addr32[i])
+ break;
+ else if (a->addr32[i] < b->addr32[i])
+ return (0);
+ /* check a <= e */
+ for (i = 0; i < 4; ++i)
+ if (a->addr32[i] < e->addr32[i])
+ break;
+ else if (a->addr32[i] > e->addr32[i])
+ return (0);
+ break;
+ }
+#endif /* INET6 */
+ }
+ return (1);
+}
+
int
pf_match(u_int8_t op, u_int32_t a1, u_int32_t a2, u_int32_t p)
{
@@ -2267,15 +2311,15 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r,
if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP ||
proto == IPPROTO_ICMP)) {
key.gwy.port = dport;
- if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL)
+ if (pf_find_state_all(&key, PF_IN, NULL) == NULL)
return (0);
} else if (low == 0 && high == 0) {
key.gwy.port = *nport;
- if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL)
+ if (pf_find_state_all(&key, PF_IN, NULL) == NULL)
return (0);
} else if (low == high) {
key.gwy.port = htons(low);
- if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) {
+ if (pf_find_state_all(&key, PF_IN, NULL) == NULL) {
*nport = htons(low);
return (0);
}
@@ -2292,7 +2336,7 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r,
/* low <= cut <= high */
for (tmp = cut; tmp <= high; ++(tmp)) {
key.gwy.port = htons(tmp);
- if (pf_find_state_all(&key, PF_EXT_GWY, NULL) ==
+ if (pf_find_state_all(&key, PF_IN, NULL) ==
NULL) {
*nport = htons(tmp);
return (0);
@@ -2300,7 +2344,7 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r,
}
for (tmp = cut - 1; tmp >= low; --(tmp)) {
key.gwy.port = htons(tmp);
- if (pf_find_state_all(&key, PF_EXT_GWY, NULL) ==
+ if (pf_find_state_all(&key, PF_IN, NULL) ==
NULL) {
*nport = htons(tmp);
return (0);
@@ -2836,6 +2880,34 @@ pf_alloc_state_key(struct pf_state *s)
return (sk);
}
+u_int32_t
+pf_tcp_iss(struct pf_pdesc *pd)
+{
+ MD5_CTX ctx;
+ u_int32_t digest[4];
+
+ if (pf_tcp_secret_init == 0) {
+ arc4random_bytes(pf_tcp_secret, sizeof(pf_tcp_secret));
+ MD5Init(&pf_tcp_secret_ctx);
+ MD5Update(&pf_tcp_secret_ctx, pf_tcp_secret, sizeof(pf_tcp_secret));
+ pf_tcp_secret_init = 1;
+ }
+ ctx = pf_tcp_secret_ctx;
+
+ MD5Update(&ctx, (char *)&pd->hdr.tcp->th_sport, sizeof(u_short));
+ MD5Update(&ctx, (char *)&pd->hdr.tcp->th_dport, sizeof(u_short));
+ if (pd->af == AF_INET6) {
+ MD5Update(&ctx, (char *)&pd->src->v6, sizeof(struct in6_addr));
+ MD5Update(&ctx, (char *)&pd->dst->v6, sizeof(struct in6_addr));
+ } else {
+ MD5Update(&ctx, (char *)&pd->src->v4, sizeof(struct in_addr));
+ MD5Update(&ctx, (char *)&pd->dst->v4, sizeof(struct in_addr));
+ }
+ MD5Final((u_char *)digest, &ctx);
+ pf_tcp_iss_off += 4096;
+ return (digest[0] + tcp_iss + pf_tcp_iss_off);
+}
+
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,
@@ -3077,7 +3149,8 @@ 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);
- else if (r->prob && r->prob <= arc4random())
+ else if (r->prob && r->prob <=
+ (arc4random() % (UINT_MAX - 1) + 1))
r = TAILQ_NEXT(r, entries);
else if (r->match_tag && !pf_match_tag(m, r, &tag))
r = TAILQ_NEXT(r, entries);
@@ -3203,10 +3276,22 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction,
(r->rule_flag & PFRULE_RETURN)) &&
!(th->th_flags & TH_RST)) {
u_int32_t ack = ntohl(th->th_seq) + pd->p_len;
- struct ip *h = mtod(m, struct ip *);
+ int len = 0;
+ struct ip *h4;
+ struct ip6_hdr *h6;
+
+ switch (af) {
+ case AF_INET:
+ h4 = mtod(m, struct ip *);
+ len = ntohs(h4->ip_len) - off;
+ break;
+ case AF_INET6:
+ h6 = mtod(m, struct ip6_hdr *);
+ len = ntohs(h6->ip6_plen) - (off - sizeof(*h6));
+ break;
+ }
- if (pf_check_proto_cksum(m, off,
- ntohs(h->ip_len) - off, IPPROTO_TCP, AF_INET))
+ if (pf_check_proto_cksum(m, off, len, IPPROTO_TCP, af))
REASON_SET(&reason, PFRES_PROTCKSUM);
else {
if (th->th_flags & TH_SYN)
@@ -3218,10 +3303,12 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction,
ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0,
r->return_ttl, 1, 0, pd->eh, kif->pfik_ifp);
}
- } else if ((af == AF_INET) && r->return_icmp)
+ } else if (pd->proto != IPPROTO_ICMP && af == AF_INET &&
+ r->return_icmp)
pf_send_icmp(m, r->return_icmp >> 8,
r->return_icmp & 255, af, r);
- else if ((af == AF_INET6) && r->return_icmp6)
+ else if (pd->proto != IPPROTO_ICMPV6 && af == AF_INET6 &&
+ r->return_icmp6)
pf_send_icmp(m, r->return_icmp6 >> 8,
r->return_icmp6 & 255, af, r);
}
@@ -3237,7 +3324,6 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction,
if (!state_icmp && (r->keep_state || nr != NULL ||
(pd->flags & PFDESC_TCP_NORM))) {
/* create new state */
- u_int16_t len;
struct pf_state *s = NULL;
struct pf_state_key *sk = NULL;
struct pf_src_node *sn = NULL;
@@ -3296,15 +3382,14 @@ cleanup:
s->log |= nr->log & PF_LOG_ALL;
switch (pd->proto) {
case IPPROTO_TCP:
- len = pd->tot_len - off - (th->th_off << 2);
s->src.seqlo = ntohl(th->th_seq);
- s->src.seqhi = s->src.seqlo + len + 1;
+ s->src.seqhi = s->src.seqlo + pd->p_len + 1;
if ((th->th_flags & (TH_SYN|TH_ACK)) ==
TH_SYN && r->keep_state == PF_STATE_MODULATE) {
/* Generate sequence number modulator */
- while ((s->src.seqdiff =
- tcp_rndiss_next() - s->src.seqlo) == 0)
- ;
+ if ((s->src.seqdiff = pf_tcp_iss(pd) -
+ s->src.seqlo) == 0)
+ s->src.seqdiff = 1;
pf_change_a(&th->th_seq, &th->th_sum,
htonl(s->src.seqlo + s->src.seqdiff), 0);
rewrite = 1;
@@ -3525,11 +3610,20 @@ 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->src.port_op || r->dst.port_op ||
- r->flagset || r->type || r->code ||
- r->os_fingerprint != PF_OSFP_ANY)
+ else if (r->os_fingerprint != PF_OSFP_ANY)
+ r = TAILQ_NEXT(r, entries);
+ else if (pd->proto == IPPROTO_UDP &&
+ (r->src.port_op || r->dst.port_op))
r = TAILQ_NEXT(r, entries);
- else if (r->prob && r->prob <= arc4random())
+ else if (pd->proto == IPPROTO_TCP &&
+ (r->src.port_op || r->dst.port_op || r->flagset))
+ r = TAILQ_NEXT(r, entries);
+ else if ((pd->proto == IPPROTO_ICMP ||
+ pd->proto == IPPROTO_ICMPV6) &&
+ (r->type || r->code))
+ r = TAILQ_NEXT(r, entries);
+ else if (r->prob && r->prob <=
+ (arc4random() % (UINT_MAX - 1) + 1))
r = TAILQ_NEXT(r, entries);
else if (r->match_tag && !pf_match_tag(m, r, &tag))
r = TAILQ_NEXT(r, entries);
@@ -3698,6 +3792,22 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
}
}
+ if (((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) &&
+ dst->state >= TCPS_FIN_WAIT_2 &&
+ src->state >= TCPS_FIN_WAIT_2) {
+ if (pf_status.debug >= PF_DEBUG_MISC) {
+ printf("pf: state reuse ");
+ pf_print_state(*state);
+ pf_print_flags(th->th_flags);
+ printf("\n");
+ }
+ /* XXX make sure it's the same direction ?? */
+ (*state)->src.state = (*state)->dst.state = TCPS_CLOSED;
+ pf_unlink_state(*state);
+ *state = NULL;
+ return (PF_DROP);
+ }
+
if (src->wscale && dst->wscale && !(th->th_flags & TH_SYN)) {
sws = src->wscale & PF_WSCALE_MASK;
dws = dst->wscale & PF_WSCALE_MASK;
@@ -3724,7 +3834,8 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
/* Deferred generation of sequence number modulator */
if (dst->seqdiff && !src->seqdiff) {
- while ((src->seqdiff = tcp_rndiss_next() - seq) == 0)
+ /* use random iss for the TCP server */
+ while ((src->seqdiff = arc4random() - seq) == 0)
;
ack = ntohl(th->th_ack) - dst->seqdiff;
pf_change_a(&th->th_seq, &th->th_sum, htonl(seq +
@@ -3842,7 +3953,8 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
(ackskew <= (MAXACKWINDOW << sws)) &&
/* Acking not more than one window forward */
((th->th_flags & TH_RST) == 0 || orig_seq == src->seqlo ||
- (orig_seq == src->seqlo + 1) || (pd->flags & PFDESC_IP_REAS) == 0)) {
+ (orig_seq == src->seqlo + 1) || (orig_seq + 1 == src->seqlo) ||
+ (pd->flags & PFDESC_IP_REAS) == 0)) {
/* Require an exact/+1 sequence match on resets when possible */
if (dst->scrub || src->scrub) {
@@ -3937,9 +4049,12 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
pf_print_state(*state);
pf_print_flags(th->th_flags);
printf(" seq=%u (%u) ack=%u len=%u ackskew=%d "
- "pkts=%llu:%llu\n", seq, orig_seq, ack, pd->p_len,
- ackskew, (*state)->packets[0],
- (*state)->packets[1]);
+ "pkts=%llu:%llu dir=%s,%s\n", seq, orig_seq, ack,
+ pd->p_len, ackskew, (*state)->packets[0],
+ (*state)->packets[1],
+ direction == PF_IN ? "in" : "out",
+ direction == (*state)->state_key->direction ?
+ "fwd" : "rev");
}
if (dst->scrub || src->scrub) {