summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frantzen <frantzen@cvs.openbsd.org>2006-01-31 03:40:32 +0000
committerMike Frantzen <frantzen@cvs.openbsd.org>2006-01-31 03:40:32 +0000
commit61ff07ed23e103b238a75639cbc7a39b6407bc76 (patch)
tree9ba43fba9682de69f5a60612ca0d38b8a4af0bfd
parent0ca1e3ba1a5dfd0d5ccc6b2f608115f414bcac4f (diff)
the TCP SACK option needs sequence number modulation
ok dhartmei@ and beck@. help + testing from kpfaff AT palloys.com.pl
-rw-r--r--sys/net/pf.c80
1 files changed, 79 insertions, 1 deletions
diff --git a/sys/net/pf.c b/sys/net/pf.c
index d47e8283987..91125fceaf4 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf.c,v 1.508 2005/11/14 09:18:55 pascoe Exp $ */
+/* $OpenBSD: pf.c,v 1.509 2006/01/31 03:40:31 frantzen Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -127,6 +127,8 @@ int pf_check_threshold(struct pf_threshold *);
void pf_change_ap(struct pf_addr *, u_int16_t *,
u_int16_t *, u_int16_t *, struct pf_addr *,
u_int16_t, u_int8_t, sa_family_t);
+int pf_modulate_sack(struct mbuf *, int, struct pf_pdesc *,
+ struct tcphdr *, struct pf_state_peer *);
#ifdef INET6
void pf_change_a6(struct pf_addr *, u_int16_t *,
struct pf_addr *, u_int8_t);
@@ -1492,6 +1494,63 @@ pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa,
}
}
+
+/*
+ * Need to modulate the sequence numbers in the TCP SACK option
+ */
+int
+pf_modulate_sack(struct mbuf *m, int off, struct pf_pdesc *pd,
+ struct tcphdr *th, struct pf_state_peer *dst)
+{
+ int hlen = (th->th_off << 2) - sizeof(*th), thoptlen = hlen;
+ u_int8_t opts[MAX_TCPOPTLEN], *opt = opts;
+ int copyback = 0, i, olen;
+ struct sackblk sack;
+
+#define TCPOLEN_SACKLEN (TCPOLEN_SACK + 2)
+ if (hlen < TCPOLEN_SACKLEN ||
+ !pf_pull_hdr(m, off + sizeof(*th), opts, hlen, NULL, NULL, pd->af))
+ return 0;
+
+ while (hlen >= TCPOLEN_SACKLEN) {
+ olen = opt[1];
+ switch (*opt) {
+ case TCPOPT_EOL: /* FALLTHROUGH */
+ case TCPOPT_NOP:
+ opt++;
+ hlen--;
+ break;
+ case TCPOPT_SACK:
+ if (olen > hlen)
+ olen = hlen;
+ if (olen >= TCPOLEN_SACKLEN) {
+ for (i = 2; i + TCPOLEN_SACK <= olen;
+ i += TCPOLEN_SACK) {
+ memcpy(&sack, &opt[i], sizeof(sack));
+ pf_change_a(&sack.start, &th->th_sum,
+ htonl(ntohl(sack.start) -
+ dst->seqdiff), 0);
+ pf_change_a(&sack.end, &th->th_sum,
+ htonl(ntohl(sack.end) -
+ dst->seqdiff), 0);
+ memcpy(&opt[i], &sack, sizeof(sack));
+ }
+ copyback = 1;
+ }
+ /* FALLTHROUGH */
+ default:
+ if (olen < 2)
+ olen = 2;
+ hlen -= olen;
+ opt += olen;
+ }
+ }
+
+ if (copyback)
+ m_copyback(m, off + sizeof(*th), thoptlen, opts);
+ return (copyback);
+}
+
void
pf_send_tcp(const struct pf_rule *r, sa_family_t af,
const struct pf_addr *saddr, const struct pf_addr *daddr,
@@ -4280,6 +4339,25 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
ackskew = dst->seqlo - ack;
+
+ /*
+ * Need to demodulate the sequence numbers in any TCP SACK options
+ * (Selective ACK). We could optionally validate the SACK values
+ * against the current ACK window, either forwards or backwards, but
+ * I'm not confident that SACK has been implemented properly
+ * everywhere. It wouldn't surprise me if several stacks accidently
+ * SACK too far backwards of previously ACKed data. There really aren't
+ * any security implications of bad SACKing unless the target stack
+ * doesn't validate the option length correctly. Someone trying to
+ * spoof into a TCP connection won't bother blindly sending SACK
+ * options anyway.
+ */
+ if (dst->seqdiff && (th->th_off << 2) > sizeof(struct tcphdr)) {
+ if (pf_modulate_sack(m, off, pd, th, dst))
+ copyback = 1;
+ }
+
+
#define MAXACKWINDOW (0xffff + 1500) /* 1500 is an arbitrary fudge factor */
if (SEQ_GEQ(src->seqhi, end) &&
/* Last octet inside other's window space */