From e66ff883f02721022cf398cc03fdd9e2bc661e94 Mon Sep 17 00:00:00 2001 From: henning Date: Tue, 6 Feb 2018 23:44:48 +0000 Subject: [PATCH] syncookies for pf. when syncookies are on, pf will blindly answer each and every SYN with a syncookie-SYNACK. Upon reception of the ACK completing the 3WHS, pf will reconstruct the original SYN, shove it through pf_test, where state will be created if the ruleset permits it. Then massage the freshly created state (we won't see the SYNACK), set up the sequence number modulator, and call into the existing synproxy code to start the 3WHS with the backend host. Add an - somewhat basic for now - adaptive mode where syncookies get enabled if a certain percentage of the state table is filled up with half-open tcp connections. This makes pf firewalls resilient against large synflood attacks. syncookies are off by default until we gained more experience, considered experimental for now. see http://bulabula.org/papers/2017/bsdcan/ for more details. joint work with sashan@, widely discussed and with lots of input by many --- sys/conf/files | 3 ++- sys/net/pf.c | 55 ++++++++++++++++++++++++++++++++++++++++++---- sys/net/pf_ioctl.c | 17 +++++++++++++- sys/net/pfvar.h | 24 +++++++++++++++++++- sys/sys/mbuf.h | 3 ++- 5 files changed, 94 insertions(+), 8 deletions(-) diff --git a/sys/conf/files b/sys/conf/files index e948086e270..f623c4101e3 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.656 2017/11/16 18:12:27 jcs Exp $ +# $OpenBSD: files,v 1.657 2018/02/06 23:44:48 henning Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -580,6 +580,7 @@ file net/pf_table.c pf file net/pf_osfp.c pf file net/pf_if.c pf file net/pf_lb.c pf +file net/pf_syncookies.c pf file net/hfsc.c pf file net/fq_codel.c pf diff --git a/sys/net/pf.c b/sys/net/pf.c index 1f8ac7d0a84..51a91114c74 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf.c,v 1.1059 2018/02/06 09:16:11 henning Exp $ */ +/* $OpenBSD: pf.c,v 1.1060 2018/02/06 23:44:48 henning Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -213,7 +213,7 @@ int pf_tcp_track_sloppy(struct pf_pdesc *, static __inline int pf_synproxy(struct pf_pdesc *, struct pf_state **, u_short *); int pf_test_state(struct pf_pdesc *, struct pf_state **, - u_short *); + u_short *, int); int pf_icmp_state_lookup(struct pf_pdesc *, struct pf_state_key_cmp *, struct pf_state **, u_int16_t, u_int16_t, int, int *, int, int); @@ -4718,7 +4718,8 @@ pf_synproxy(struct pf_pdesc *pd, struct pf_state **state, u_short *reason) } int -pf_test_state(struct pf_pdesc *pd, struct pf_state **state, u_short *reason) +pf_test_state(struct pf_pdesc *pd, struct pf_state **state, u_short *reason, + int syncookie) { struct pf_state_key_cmp key; int copyback = 0; @@ -4752,6 +4753,11 @@ pf_test_state(struct pf_pdesc *pd, struct pf_state **state, u_short *reason) switch (pd->virtual_proto) { case IPPROTO_TCP: + if (syncookie) { + pf_set_protostate(*state, PF_PEER_SRC, + PF_TCPS_PROXY_DST); + (*state)->dst.seqhi = ntohl(pd->hdr.tcp.th_ack) - 1; + } if ((action = pf_synproxy(pd, state, reason)) != PF_PASS) return (action); if ((pd->hdr.tcp.th_flags & (TH_SYN|TH_ACK)) == TH_SYN) { @@ -6904,13 +6910,54 @@ pf_test(sa_family_t af, int fwdir, struct ifnet *ifp, struct mbuf **m0) default: if (pd.virtual_proto == IPPROTO_TCP) { + if (pd.dir == PF_IN && (pd.hdr.tcp.th_flags & + (TH_SYN|TH_ACK)) == TH_SYN && + pf_synflood_check(&pd)) { + pf_syncookie_send(&pd); + action = PF_DROP; + goto done; + } if ((pd.hdr.tcp.th_flags & TH_ACK) && pd.p_len == 0) pqid = 1; action = pf_normalize_tcp(&pd); if (action == PF_DROP) goto unlock; } - action = pf_test_state(&pd, &s, &reason); + action = pf_test_state(&pd, &s, &reason, 0); + if (s == NULL && action != PF_PASS && action != PF_AFRT && + pd.dir == PF_IN && pd.virtual_proto == IPPROTO_TCP && + (pd.hdr.tcp.th_flags & (TH_SYN|TH_ACK|TH_RST)) == TH_ACK && + pf_syncookie_validate(&pd)) { + struct mbuf *msyn; + msyn = pf_syncookie_recreate_syn(&pd); + if (msyn) { + PF_UNLOCK(); + action = pf_test(af, fwdir, ifp, &msyn); + PF_LOCK(); + m_freem(msyn); + if (action == PF_PASS || action == PF_AFRT) { + pf_test_state(&pd, &s, &reason, 1); + if (s == NULL) { + PF_UNLOCK(); + return (PF_DROP); + } + s->src.seqhi = + ntohl(pd.hdr.tcp.th_ack) - 1; + s->src.seqlo = + ntohl(pd.hdr.tcp.th_seq) - 1; + pf_set_protostate(s, PF_PEER_SRC, + PF_TCPS_PROXY_DST); + action = pf_synproxy(&pd, &s, &reason); + if (action != PF_PASS) { + PF_UNLOCK(); + return (action); + } + } + } else + action = PF_DROP; + } + + if (action == PF_PASS || action == PF_AFRT) { #if NPFSYNC > 0 pfsync_update_state(s); diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c index a88a9466e86..231ff91b444 100644 --- a/sys/net/pf_ioctl.c +++ b/sys/net/pf_ioctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf_ioctl.c,v 1.328 2018/02/06 09:16:11 henning Exp $ */ +/* $OpenBSD: pf_ioctl.c,v 1.329 2018/02/06 23:44:48 henning Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -173,6 +173,7 @@ pfattach(int num) pfr_initialize(); pfi_initialize(); pf_osfp_initialize(); + pf_syncookies_init(); pool_sethardlimit(pf_pool_limits[PF_LIMIT_STATES].pp, pf_pool_limits[PF_LIMIT_STATES].limit, NULL, 0); @@ -2645,6 +2646,20 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } + case DIOCSETSYNFLWATS: { + struct pfioc_synflwats *io = (struct pfioc_synflwats *)addr; + + error = pf_syncookies_setwats(io->hiwat, io->lowat); + break; + } + + case DIOCSETSYNCOOKIES: { + u_int8_t *mode = (u_int8_t *)addr; + + error = pf_syncookies_setmode(*mode); + break; + } + default: error = ENODEV; break; diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 13f7fa9389e..8cade49569a 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pfvar.h,v 1.470 2017/12/29 17:05:25 bluhm Exp $ */ +/* $OpenBSD: pfvar.h,v 1.471 2018/02/06 23:44:48 henning Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -1296,6 +1296,7 @@ struct pf_status { u_int64_t pcounters[2][2][3]; u_int64_t bcounters[2][2]; u_int64_t stateid; + u_int64_t syncookies_inflight[2]; /* unACKed SYNcookies */ time_t since; u_int32_t running; u_int32_t states; @@ -1304,6 +1305,9 @@ struct pf_status { u_int32_t debug; u_int32_t hostid; u_int32_t reass; /* reassembly */ + u_int8_t syncookies_active; + u_int8_t syncookies_mode; /* never/always/adaptive */ + u_int8_t pad[2]; char ifname[IFNAMSIZ]; u_int8_t pf_chksum[PF_MD5_DIGEST_LENGTH]; }; @@ -1311,6 +1315,11 @@ struct pf_status { #define PF_REASS_ENABLED 0x01 #define PF_REASS_NODF 0x02 +#define PF_SYNCOOKIES_NEVER 0 +#define PF_SYNCOOKIES_ALWAYS 1 +#define PF_SYNCOOKIES_ADAPTIVE 2 +#define PF_SYNCOOKIES_MODE_MAX PF_SYNCOOKIES_ADAPTIVE + #define PF_PRIO_ZERO 0xff /* match "prio 0" packets */ struct pf_queue_bwspec { @@ -1564,6 +1573,10 @@ struct pfioc_iface { int pfiio_flags; }; +struct pfioc_synflwats { + u_int32_t hiwat; + u_int32_t lowat; +}; /* * ioctl operations @@ -1629,6 +1642,8 @@ struct pfioc_iface { #define DIOCGETQUEUES _IOWR('D', 94, struct pfioc_queue) #define DIOCGETQUEUE _IOWR('D', 95, struct pfioc_queue) #define DIOCGETQSTATS _IOWR('D', 96, struct pfioc_qstats) +#define DIOCSETSYNFLWATS _IOWR('D', 97, struct pfioc_synflwats) +#define DIOCSETSYNCOOKIES _IOWR('D', 98, u_int8_t) #ifdef _KERNEL @@ -1921,6 +1936,13 @@ void pf_send_tcp(const struct pf_rule *, sa_family_t, 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, u_int); +void pf_syncookies_init(void); +int pf_syncookies_setmode(u_int8_t); +int pf_syncookies_setwats(u_int32_t, u_int32_t); +int pf_synflood_check(struct pf_pdesc *); +void pf_syncookie_send(struct pf_pdesc *); +u_int8_t pf_syncookie_validate(struct pf_pdesc *); +struct mbuf * pf_syncookie_recreate_syn(struct pf_pdesc *); #endif /* _KERNEL */ #endif /* _NET_PFVAR_H_ */ diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index c60f0e26052..5a2dbbe8885 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: mbuf.h,v 1.232 2017/10/19 11:02:42 bluhm Exp $ */ +/* $OpenBSD: mbuf.h,v 1.233 2018/02/06 23:44:48 henning Exp $ */ /* $NetBSD: mbuf.h,v 1.19 1996/02/09 18:25:14 christos Exp $ */ /* @@ -108,6 +108,7 @@ struct pkthdr_pf { /* pkthdr_pf.flags */ #define PF_TAG_GENERATED 0x01 +#define PF_TAG_SYNCOOKIE_RECREATED 0x02 #define PF_TAG_TRANSLATE_LOCALHOST 0x04 #define PF_TAG_DIVERTED 0x08 #define PF_TAG_DIVERTED_PACKET 0x10 -- 2.20.1