Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F101968580
D31138.id92475.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
20 KB
Referenced Files
None
Subscribers
None
D31138.id92475.diff
View Options
diff --git a/sys/modules/pf/Makefile b/sys/modules/pf/Makefile
--- a/sys/modules/pf/Makefile
+++ b/sys/modules/pf/Makefile
@@ -4,7 +4,7 @@
KMOD= pf
SRCS= pf.c pf_if.c pf_lb.c pf_osfp.c pf_ioctl.c pf_norm.c pf_table.c \
- pf_ruleset.c pf_nv.c in4_cksum.c \
+ pf_ruleset.c pf_nv.c pf_syncookies.c in4_cksum.c \
bus_if.h device_if.h \
opt_pf.h opt_inet.h opt_inet6.h opt_bpf.h opt_sctp.h opt_global.h
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1132,6 +1132,12 @@
counter_u64_add(V_pf_status.counters[x], 1); \
} while (0)
+enum pf_syncookies_mode {
+ PF_SYNCOOKIES_NEVER = 0,
+ PF_SYNCOOKIES_ALWAYS = 1,
+ PF_SYNCOOKIES_MODE_MAX = PF_SYNCOOKIES_ALWAYS
+};
+
struct pf_kstatus {
counter_u64_t counters[PFRES_MAX]; /* reason for passing/dropping */
counter_u64_t lcounters[LCNT_MAX]; /* limit counters */
@@ -1146,6 +1152,8 @@
char ifname[IFNAMSIZ];
uint8_t pf_chksum[PF_MD5_DIGEST_LENGTH];
bool keep_counters;
+ enum pf_syncookies_mode syncookies_mode;
+ bool syncookies_active;
};
struct pf_divert {
@@ -1486,6 +1494,8 @@
#define DIOCKILLSRCNODES _IOWR('D', 91, struct pfioc_src_node_kill)
#define DIOCKEEPCOUNTERS _IOWR('D', 92, struct pfioc_nv)
#define DIOCGETSTATESV2 _IOWR('D', 93, struct pfioc_states_v2)
+#define DIOCGETSYNCOOKIES _IOWR('D', 94, struct pfioc_nv)
+#define DIOCSETSYNCOOKIES _IOWR('D', 95, struct pfioc_nv)
struct pf_ifspeed_v0 {
char ifname[IFNAMSIZ];
@@ -1816,6 +1826,30 @@
sa_family_t);
void pf_qid2qname(u_int32_t, char *);
+u_int16_t pf_get_mss(struct mbuf *, int, u_int16_t, sa_family_t);
+u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t, sa_family_t);
+struct mbuf *pf_build_tcp(const struct pf_krule *, sa_family_t,
+ const struct pf_addr *, const struct pf_addr *,
+ u_int16_t, u_int16_t, u_int32_t, u_int32_t,
+ u_int8_t, u_int16_t, u_int16_t, u_int8_t, int,
+ u_int16_t);
+void pf_send_tcp(const struct pf_krule *, sa_family_t,
+ const struct pf_addr *, const struct pf_addr *,
+ u_int16_t, u_int16_t, u_int32_t, u_int32_t,
+ u_int8_t, u_int16_t, u_int16_t, u_int8_t, int,
+ u_int16_t);
+
+void pf_syncookies_init(void);
+int pf_syncookies_setmode(u_int8_t);
+int pf_get_syncookies(struct pfioc_nv *);
+int pf_set_syncookies(struct pfioc_nv *);
+int pf_synflood_check(struct pf_pdesc *);
+void pf_syncookie_send(struct mbuf *m, int off,
+ struct pf_pdesc *);
+u_int8_t pf_syncookie_validate(struct pf_pdesc *);
+struct mbuf * pf_syncookie_recreate_syn(uint8_t, int,
+ struct pf_pdesc *);
+
VNET_DECLARE(struct pf_kstatus, pf_status);
#define V_pf_status VNET(pf_status)
diff --git a/sys/netinet/tcp.h b/sys/netinet/tcp.h
--- a/sys/netinet/tcp.h
+++ b/sys/netinet/tcp.h
@@ -105,6 +105,8 @@
#define TCPOPT_FAST_OPEN 34
#define TCPOLEN_FAST_OPEN_EMPTY 2
+#define MAX_TCPOPTLEN 40 /* Absolute maximum TCP options len */
+
/* Miscellaneous constants */
#define MAX_SACK_BLKS 6 /* Max # SACK blocks stored at receiver side */
#define TCP_MAX_SACK 4 /* MAX # SACKs sent in any segment */
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -160,7 +160,8 @@
#define LCNT_SRCCONNRATE 4 /* max-src-conn-rate */
#define LCNT_OVERLOAD_TABLE 5 /* entry added to overload table */
#define LCNT_OVERLOAD_FLUSH 6 /* state entries flushed */
-#define LCNT_MAX 7 /* total+1 */
+#define LCNT_SYNCOOKIES_VALID 7 /* syncookies validated */ /* XXX TODO: Ensure no API breakage! */
+#define LCNT_MAX 8 /* total+1 */
#define LCNT_NAMES { \
"max states per rule", \
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -243,11 +243,6 @@
struct pf_addr *, struct pf_addr *, u_int16_t,
u_int16_t *, u_int16_t *, u_int16_t *,
u_int16_t *, u_int8_t, sa_family_t);
-static void pf_send_tcp(const struct pf_krule *, sa_family_t,
- const struct pf_addr *, const struct pf_addr *,
- u_int16_t, u_int16_t, u_int32_t, u_int32_t,
- u_int8_t, u_int16_t, u_int16_t, u_int8_t, int,
- u_int16_t);
static void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t,
sa_family_t, struct pf_krule *);
static void pf_detach_state(struct pf_kstate *);
@@ -291,10 +286,6 @@
void *, struct pf_pdesc *, u_short *);
static int pf_test_state_other(struct pf_kstate **, int,
struct pfi_kkif *, struct mbuf *, 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,
- sa_family_t);
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,
@@ -2453,14 +2444,13 @@
return (copyback);
}
-static void
-pf_send_tcp(const struct pf_krule *r, sa_family_t af,
+struct mbuf *
+pf_build_tcp(const struct pf_krule *r, sa_family_t af,
const struct pf_addr *saddr, const struct pf_addr *daddr,
u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack,
u_int8_t flags, u_int16_t win, u_int16_t mss, u_int8_t ttl, int tag,
u_int16_t rtag)
{
- struct pf_send_entry *pfse;
struct mbuf *m;
int len, tlen;
#ifdef INET
@@ -2496,22 +2486,16 @@
panic("%s: unsupported af %d", __func__, af);
}
- /* Allocate outgoing queue entry, mbuf and mbuf tag. */
- pfse = malloc(sizeof(*pfse), M_PFTEMP, M_NOWAIT);
- if (pfse == NULL)
- return;
m = m_gethdr(M_NOWAIT, MT_DATA);
- if (m == NULL) {
- free(pfse, M_PFTEMP);
- return;
- }
+ if (m == NULL)
+ return (NULL);
+
#ifdef MAC
mac_netinet_firewall_send(m);
#endif
if ((pf_mtag = pf_get_mtag(m)) == NULL) {
- free(pfse, M_PFTEMP);
m_freem(m);
- return;
+ return (NULL);
}
if (tag)
m->m_flags |= M_SKIP_FIREWALL;
@@ -2592,8 +2576,6 @@
h->ip_len = htons(len);
h->ip_ttl = ttl ? ttl : V_ip_defttl;
h->ip_sum = 0;
-
- pfse->pfse_type = PFSE_IP;
break;
#endif /* INET */
#ifdef INET6
@@ -2604,11 +2586,48 @@
h6->ip6_vfc |= IPV6_VERSION;
h6->ip6_hlim = IPV6_DEFHLIM;
+ break;
+#endif /* INET6 */
+ }
+
+ return (m);
+}
+
+void
+pf_send_tcp(const struct pf_krule *r, sa_family_t af,
+ const struct pf_addr *saddr, const struct pf_addr *daddr,
+ u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack,
+ u_int8_t flags, u_int16_t win, u_int16_t mss, u_int8_t ttl, int tag,
+ u_int16_t rtag)
+{
+ struct pf_send_entry *pfse;
+ struct mbuf *m;
+
+ m = pf_build_tcp(r, af, saddr, daddr, sport, dport, seq, ack, flags,
+ win, mss, ttl, tag, rtag);
+ if (m == NULL)
+ return;
+
+ /* Allocate outgoing queue entry, mbuf and mbuf tag. */
+ pfse = malloc(sizeof(*pfse), M_PFTEMP, M_NOWAIT);
+ if (pfse == NULL) {
+ m_freem(m);
+ return;
+ }
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ pfse->pfse_type = PFSE_IP;
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
pfse->pfse_type = PFSE_IP6;
break;
#endif /* INET6 */
}
+
pfse->pfse_m = m;
pf_send(pfse);
}
@@ -3192,7 +3211,7 @@
return (1);
}
-static u_int8_t
+u_int8_t
pf_get_wscale(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af)
{
int hlen;
@@ -3232,7 +3251,7 @@
return (wscale);
}
-static u_int16_t
+u_int16_t
pf_get_mss(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af)
{
int hlen;
@@ -6077,6 +6096,18 @@
goto done;
}
pd.p_len = pd.tot_len - off - (pd.hdr.tcp.th_off << 2);
+
+ pd.sport = &pd.hdr.tcp.th_sport;
+ pd.dport = &pd.hdr.tcp.th_dport;
+
+ /* Respond to SYN with a syncookie. */
+ if ((pd.hdr.tcp.th_flags & (TH_SYN|TH_ACK|TH_RST)) == TH_SYN &&
+ pd.dir == PF_IN && pf_synflood_check(&pd)) {
+ pf_syncookie_send(m, off, &pd);
+ action = PF_DROP;
+ break;
+ }
+
if ((pd.hdr.tcp.th_flags & TH_ACK) && pd.p_len == 0)
pqid = 1;
action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd);
@@ -6090,9 +6121,49 @@
r = s->rule.ptr;
a = s->anchor.ptr;
log = s->log;
- } else if (s == NULL)
- action = pf_test_rule(&r, &s, dir, kif, m, off, &pd,
- &a, &ruleset, inp);
+ } else if (s == NULL) {
+ /* Validate remote SYN|ACK, re-create original SYN if
+ * valid. */
+ if ((pd.hdr.tcp.th_flags & (TH_SYN|TH_ACK|TH_RST)) ==
+ TH_ACK && pf_syncookie_validate(&pd) &&
+ pd.dir == PF_IN) {
+ struct mbuf *msyn;
+
+ msyn = pf_syncookie_recreate_syn(h->ip_ttl,
+ off,&pd);
+ if (msyn == NULL) {
+ action = PF_DROP;
+ break;
+ }
+
+ action = pf_test(dir, pflags, ifp, &msyn, inp);
+ m_freem(msyn);
+
+ if (action == PF_PASS) {
+ action = pf_test_state_tcp(&s, dir,
+ kif, m, off, h, &pd, &reason);
+ if (action != PF_PASS || s == NULL) {
+ action = PF_DROP;
+ break;
+ }
+
+ s->src.seqhi = ntohl(pd.hdr.tcp.th_ack)
+ - 1;
+ s->src.seqlo = ntohl(pd.hdr.tcp.th_seq)
+ - 1;
+ s->src.state = PF_TCPS_PROXY_DST;
+
+ action = pf_synproxy(&pd, &s, &reason);
+ if (action != PF_PASS)
+ break;
+ }
+ break;
+ }
+ else {
+ action = pf_test_rule(&r, &s, dir, kif, m, off,
+ &pd, &a, &ruleset, inp);
+ }
+ }
break;
}
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -284,6 +284,7 @@
pfr_initialize();
pfi_initialize_vnet();
pf_normalize_init();
+ pf_syncookies_init();
V_pf_limits[PF_LIMIT_STATES].limit = PFSTATE_HIWAT;
V_pf_limits[PF_LIMIT_SRC_NODES].limit = PFSNODE_HIWAT;
@@ -5524,7 +5525,7 @@
{
int error;
- rm_init(&pf_rules_lock, "pf rulesets");
+ rm_init_flags(&pf_rules_lock, "pf rulesets", RM_RECURSE);
sx_init(&pf_ioctl_lock, "pf ioctl");
sx_init(&pf_end_lock, "pf end thread");
diff --git a/sys/netpfil/pf/pf_mtag.h b/sys/netpfil/pf/pf_mtag.h
--- a/sys/netpfil/pf/pf_mtag.h
+++ b/sys/netpfil/pf/pf_mtag.h
@@ -43,6 +43,7 @@
#define PF_FASTFWD_OURS_PRESENT 0x10
#define PF_REASSEMBLED 0x20
#define PF_DUPLICATED 0x40
+#define PF_TAG_SYNCOOKIE_RECREATED 0x80
struct pf_mtag {
void *hdr; /* saved hdr pos in mbuf, for ECN */
diff --git a/sys/netpfil/pf/pf_syncookies.c b/sys/netpfil/pf/pf_syncookies.c
new file mode 100644
--- /dev/null
+++ b/sys/netpfil/pf/pf_syncookies.c
@@ -0,0 +1,350 @@
+/* $OpenBSD: pf_syncookies.c,v 1.7 2018/09/10 15:54:28 henning Exp $ */
+
+/* Copyright (c) 2016,2017 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2016 Alexandr Nedvedicky <sashan@openbsd.org>
+ *
+ * syncookie parts based on FreeBSD sys/netinet/tcp_syncache.c
+ *
+ * Copyright (c) 2001 McAfee, Inc.
+ * Copyright (c) 2006,2013 Andre Oppermann, Internet Business Solutions AG
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Jonathan Lemon
+ * and McAfee Research, the Security Research Division of McAfee, Inc. under
+ * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
+ * DARPA CHATS research program. [2001 McAfee, Inc.]
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * when we're under synflood, we use syncookies to prevent state table
+ * exhaustion. Trigger for the synflood mode is the number of half-open
+ * connections in the state table.
+ * We leave synflood mode when the number of half-open states - including
+ * in-flight syncookies - drops far enough again
+ */
+
+/*
+ * syncookie enabled Initial Sequence Number:
+ * 24 bit MAC
+ * 3 bit WSCALE index
+ * 3 bit MSS index
+ * 1 bit SACK permitted
+ * 1 bit odd/even secret
+ *
+ * References:
+ * RFC4987 TCP SYN Flooding Attacks and Common Mitigations
+ * http://cr.yp.to/syncookies.html (overview)
+ * http://cr.yp.to/syncookies/archive (details)
+ */
+
+//#include "pflog.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/filio.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/kernel.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/rwlock.h>
+#include <sys/syslog.h>
+
+#include <crypto/siphash/siphash.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_var.h>
+
+#include <net/pfvar.h>
+
+#define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x
+
+union pf_syncookie {
+ uint8_t cookie;
+ struct {
+ uint8_t oddeven:1,
+ sack_ok:1,
+ wscale_idx:3,
+ mss_idx:3;
+ } flags;
+};
+
+#define PF_SYNCOOKIE_SECRET_SIZE SIPHASH_KEY_LENGTH
+#define PF_SYNCOOKIE_SECRET_LIFETIME 15 /* seconds */
+
+/* Protected by PF_RULES_xLOCK. */
+struct pf_syncookie_status {
+ struct callout keytimeout;
+ uint8_t oddeven;
+ uint8_t key[2][SIPHASH_KEY_LENGTH];
+};
+VNET_DEFINE_STATIC(struct pf_syncookie_status, pf_syncookie_status);
+#define V_pf_syncookie_status VNET(pf_syncookie_status)
+
+void pf_syncookie_rotate(void *);
+void pf_syncookie_newkey(void);
+uint32_t pf_syncookie_mac(struct pf_pdesc *, union pf_syncookie,
+ uint32_t);
+uint32_t pf_syncookie_generate(struct mbuf *m, int off, struct pf_pdesc *,
+ uint16_t);
+
+void
+pf_syncookies_init(void)
+{
+ callout_init(&V_pf_syncookie_status.keytimeout, 1);
+ PF_RULES_WLOCK();
+ pf_syncookies_setmode(PF_SYNCOOKIES_NEVER);
+ PF_RULES_WUNLOCK();
+}
+
+int
+pf_syncookies_setmode(u_int8_t mode)
+{
+ if (mode > PF_SYNCOOKIES_MODE_MAX)
+ return (EINVAL);
+
+ if (V_pf_status.syncookies_mode == mode)
+ return (0);
+
+ V_pf_status.syncookies_mode = mode;
+ if (V_pf_status.syncookies_mode == PF_SYNCOOKIES_ALWAYS) {
+ pf_syncookie_newkey();
+ V_pf_status.syncookies_active = true;
+ }
+ return (0);
+}
+
+int
+pf_synflood_check(struct pf_pdesc *pd)
+{
+ MPASS(pd->proto == IPPROTO_TCP);
+ PF_RULES_RASSERT();
+
+ if (pd->pf_mtag && (pd->pf_mtag->tag & PF_TAG_SYNCOOKIE_RECREATED))
+ return (0);
+
+ return (V_pf_status.syncookies_mode);
+}
+
+void
+pf_syncookie_send(struct mbuf *m, int off, struct pf_pdesc *pd)
+{
+ uint16_t mss;
+ uint32_t iss;
+
+ mss = max(V_tcp_mssdflt, pf_get_mss(m, off, pd->hdr.tcp.th_off, pd->af));
+ iss = pf_syncookie_generate(m, off, pd, mss);
+ pf_send_tcp(NULL, pd->af, pd->dst, pd->src, *pd->dport, *pd->sport,
+ iss, ntohl(pd->hdr.tcp.th_seq) + 1, TH_SYN|TH_ACK, 0, mss,
+ 0, 1, 0);
+}
+
+uint8_t
+pf_syncookie_validate(struct pf_pdesc *pd)
+{
+ uint32_t hash, ack, seq;
+ union pf_syncookie cookie;
+
+ MPASS(pd->proto == IPPROTO_TCP);
+ PF_RULES_RASSERT();
+
+ seq = ntohl(pd->hdr.tcp.th_seq) - 1;
+ ack = ntohl(pd->hdr.tcp.th_ack) - 1;
+ cookie.cookie = (ack & 0xff) ^ (ack >> 24);
+
+ hash = pf_syncookie_mac(pd, cookie, seq);
+ if ((ack & ~0xff) != (hash & ~0xff))
+ return (0);
+
+ counter_u64_add(V_pf_status.lcounters[LCNT_SYNCOOKIES_VALID], 1);
+ return (1);
+}
+
+/*
+ * all following functions private
+ */
+void
+pf_syncookie_rotate(void *arg)
+{
+ CURVNET_SET((struct vnet *)arg);
+
+ /* do we want to disable syncookies? */
+ if (V_pf_status.syncookies_active) {
+ V_pf_status.syncookies_active = false;
+ DPFPRINTF(PF_DEBUG_MISC, ("syncookies disabled"));
+ }
+
+ /* nothing in flight any more? delete keys and return */
+ if (!V_pf_status.syncookies_active) {
+ memset(V_pf_syncookie_status.key[0], 0,
+ PF_SYNCOOKIE_SECRET_SIZE);
+ memset(V_pf_syncookie_status.key[1], 0,
+ PF_SYNCOOKIE_SECRET_SIZE);
+ CURVNET_RESTORE();
+ return;
+ }
+
+ /* new key, including timeout */
+ pf_syncookie_newkey();
+
+ CURVNET_RESTORE();
+ printf("KP: %s() return\n", __func__);
+}
+
+void
+pf_syncookie_newkey(void)
+{
+ PF_RULES_WASSERT();
+
+ V_pf_syncookie_status.oddeven = (V_pf_syncookie_status.oddeven + 1) & 0x1;
+ arc4random_buf(V_pf_syncookie_status.key[V_pf_syncookie_status.oddeven],
+ PF_SYNCOOKIE_SECRET_SIZE);
+ callout_reset(&V_pf_syncookie_status.keytimeout,
+ PF_SYNCOOKIE_SECRET_LIFETIME, pf_syncookie_rotate, curvnet);
+}
+
+/*
+ * Distribution and probability of certain MSS values. Those in between are
+ * rounded down to the next lower one.
+ * [An Analysis of TCP Maximum Segment Sizes, S. Alcock and R. Nelson, 2011]
+ * .2% .3% 5% 7% 7% 20% 15% 45%
+ */
+static int pf_syncookie_msstab[] =
+ { 216, 536, 1200, 1360, 1400, 1440, 1452, 1460 };
+
+/*
+ * Distribution and probability of certain WSCALE values.
+ * The absence of the WSCALE option is encoded with index zero.
+ * [WSCALE values histograms, Allman, 2012]
+ * X 10 10 35 5 6 14 10% by host
+ * X 11 4 5 5 18 49 3% by connections
+ */
+static int pf_syncookie_wstab[] = { 0, 0, 1, 2, 4, 6, 7, 8 };
+
+uint32_t
+pf_syncookie_mac(struct pf_pdesc *pd, union pf_syncookie cookie, uint32_t seq)
+{
+ SIPHASH_CTX ctx;
+ uint32_t siphash[2];
+
+ PF_RULES_RASSERT();
+ MPASS(pd->proto == IPPROTO_TCP);
+
+ SipHash24_Init(&ctx);
+ SipHash_SetKey(&ctx, V_pf_syncookie_status.key[cookie.flags.oddeven]);
+
+ switch (pd->af) {
+ case AF_INET:
+ SipHash_Update(&ctx, pd->src, sizeof(pd->src->v4));
+ SipHash_Update(&ctx, pd->dst, sizeof(pd->dst->v4));
+ break;
+ case AF_INET6:
+ SipHash_Update(&ctx, pd->src, sizeof(pd->src->v6));
+ SipHash_Update(&ctx, pd->dst, sizeof(pd->dst->v6));
+ break;
+ default:
+ panic("unknown address family");
+ }
+
+ SipHash_Update(&ctx, pd->sport, sizeof(*pd->sport));
+ SipHash_Update(&ctx, pd->dport, sizeof(*pd->dport));
+ SipHash_Update(&ctx, &seq, sizeof(seq));
+ SipHash_Update(&ctx, &cookie, sizeof(cookie));
+ SipHash_Final((uint8_t *)&siphash, &ctx);
+
+ return (siphash[0] ^ siphash[1]);
+}
+
+uint32_t
+pf_syncookie_generate(struct mbuf *m, int off, struct pf_pdesc *pd,
+ uint16_t mss)
+{
+ uint8_t i, wscale;
+ uint32_t iss, hash;
+ union pf_syncookie cookie;
+
+ PF_RULES_RASSERT();
+
+ cookie.cookie = 0;
+
+ /* map MSS */
+ for (i = nitems(pf_syncookie_msstab) - 1;
+ pf_syncookie_msstab[i] > mss && i > 0; i--)
+ /* nada */;
+ cookie.flags.mss_idx = i;
+
+ /* map WSCALE */
+ wscale = pf_get_wscale(m, off, pd->hdr.tcp.th_off, pd->af);
+ for (i = nitems(pf_syncookie_wstab) - 1;
+ pf_syncookie_wstab[i] > wscale && i > 0; i--)
+ /* nada */;
+ cookie.flags.wscale_idx = i;
+ cookie.flags.sack_ok = 0; /* XXX */
+
+ cookie.flags.oddeven = V_pf_syncookie_status.oddeven;
+ hash = pf_syncookie_mac(pd, cookie, ntohl(pd->hdr.tcp.th_seq));
+
+ /*
+ * Put the flags into the hash and XOR them to get better ISS number
+ * variance. This doesn't enhance the cryptographic strength and is
+ * done to prevent the 8 cookie bits from showing up directly on the
+ * wire.
+ */
+ iss = hash & ~0xff;
+ iss |= cookie.cookie ^ (hash >> 24);
+
+ return (iss);
+}
+
+struct mbuf *
+pf_syncookie_recreate_syn(uint8_t ttl, int off, struct pf_pdesc *pd)
+{
+ uint8_t wscale;
+ uint16_t mss;
+ uint32_t ack, seq;
+ union pf_syncookie cookie;
+
+ seq = ntohl(pd->hdr.tcp.th_seq) - 1;
+ ack = ntohl(pd->hdr.tcp.th_ack) - 1;
+ cookie.cookie = (ack & 0xff) ^ (ack >> 24);
+
+ if (cookie.flags.mss_idx >= nitems(pf_syncookie_msstab) ||
+ cookie.flags.wscale_idx >= nitems(pf_syncookie_wstab))
+ return (NULL);
+
+ mss = pf_syncookie_msstab[cookie.flags.mss_idx];
+ wscale = pf_syncookie_wstab[cookie.flags.wscale_idx];
+
+ return (pf_build_tcp(NULL, pd->af, pd->src, pd->dst, *pd->sport,
+ *pd->dport, seq, 0, TH_SYN, wscale, mss, ttl, 0,
+ PF_TAG_SYNCOOKIE_RECREATED));
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Nov 6, 10:49 PM (18 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14501303
Default Alt Text
D31138.id92475.diff (20 KB)
Attached To
Mode
D31138: pf: syncookie support
Attached
Detach File
Event Timeline
Log In to Comment