18e1864edSKristof Provost /* $OpenBSD: pf_syncookies.c,v 1.7 2018/09/10 15:54:28 henning Exp $ */ 28e1864edSKristof Provost 38e1864edSKristof Provost /* Copyright (c) 2016,2017 Henning Brauer <henning@openbsd.org> 48e1864edSKristof Provost * Copyright (c) 2016 Alexandr Nedvedicky <sashan@openbsd.org> 58e1864edSKristof Provost * 68e1864edSKristof Provost * syncookie parts based on FreeBSD sys/netinet/tcp_syncache.c 78e1864edSKristof Provost * 88e1864edSKristof Provost * Copyright (c) 2001 McAfee, Inc. 98e1864edSKristof Provost * Copyright (c) 2006,2013 Andre Oppermann, Internet Business Solutions AG 108e1864edSKristof Provost * All rights reserved. 118e1864edSKristof Provost * 128e1864edSKristof Provost * This software was developed for the FreeBSD Project by Jonathan Lemon 138e1864edSKristof Provost * and McAfee Research, the Security Research Division of McAfee, Inc. under 148e1864edSKristof Provost * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 158e1864edSKristof Provost * DARPA CHATS research program. [2001 McAfee, Inc.] 168e1864edSKristof Provost * 178e1864edSKristof Provost * Redistribution and use in source and binary forms, with or without 188e1864edSKristof Provost * modification, are permitted provided that the following conditions 198e1864edSKristof Provost * are met: 208e1864edSKristof Provost * 1. Redistributions of source code must retain the above copyright 218e1864edSKristof Provost * notice, this list of conditions and the following disclaimer. 228e1864edSKristof Provost * 2. Redistributions in binary form must reproduce the above copyright 238e1864edSKristof Provost * notice, this list of conditions and the following disclaimer in the 248e1864edSKristof Provost * documentation and/or other materials provided with the distribution. 258e1864edSKristof Provost * 268e1864edSKristof Provost * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 278e1864edSKristof Provost * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 288e1864edSKristof Provost * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 298e1864edSKristof Provost * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 308e1864edSKristof Provost * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 318e1864edSKristof Provost * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 328e1864edSKristof Provost * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 338e1864edSKristof Provost * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 348e1864edSKristof Provost * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 358e1864edSKristof Provost * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 368e1864edSKristof Provost * SUCH DAMAGE. 378e1864edSKristof Provost */ 388e1864edSKristof Provost 398e1864edSKristof Provost /* 408e1864edSKristof Provost * when we're under synflood, we use syncookies to prevent state table 418e1864edSKristof Provost * exhaustion. Trigger for the synflood mode is the number of half-open 428e1864edSKristof Provost * connections in the state table. 438e1864edSKristof Provost * We leave synflood mode when the number of half-open states - including 448e1864edSKristof Provost * in-flight syncookies - drops far enough again 458e1864edSKristof Provost */ 468e1864edSKristof Provost 478e1864edSKristof Provost /* 488e1864edSKristof Provost * syncookie enabled Initial Sequence Number: 498e1864edSKristof Provost * 24 bit MAC 508e1864edSKristof Provost * 3 bit WSCALE index 518e1864edSKristof Provost * 3 bit MSS index 528e1864edSKristof Provost * 1 bit SACK permitted 538e1864edSKristof Provost * 1 bit odd/even secret 548e1864edSKristof Provost * 558e1864edSKristof Provost * References: 568e1864edSKristof Provost * RFC4987 TCP SYN Flooding Attacks and Common Mitigations 578e1864edSKristof Provost * http://cr.yp.to/syncookies.html (overview) 588e1864edSKristof Provost * http://cr.yp.to/syncookies/archive (details) 598e1864edSKristof Provost */ 608e1864edSKristof Provost 618e1864edSKristof Provost //#include "pflog.h" 628e1864edSKristof Provost 638e1864edSKristof Provost #include <sys/param.h> 648e1864edSKristof Provost #include <sys/systm.h> 658e1864edSKristof Provost #include <sys/mbuf.h> 668e1864edSKristof Provost #include <sys/filio.h> 678e1864edSKristof Provost #include <sys/socket.h> 688e1864edSKristof Provost #include <sys/socketvar.h> 698e1864edSKristof Provost #include <sys/kernel.h> 708e1864edSKristof Provost #include <sys/time.h> 718e1864edSKristof Provost #include <sys/proc.h> 728e1864edSKristof Provost #include <sys/rwlock.h> 738e1864edSKristof Provost #include <sys/syslog.h> 748e1864edSKristof Provost 758e1864edSKristof Provost #include <crypto/siphash/siphash.h> 768e1864edSKristof Provost 778e1864edSKristof Provost #include <net/if.h> 788e1864edSKristof Provost #include <net/if_var.h> 798e1864edSKristof Provost #include <net/if_types.h> 808e1864edSKristof Provost #include <net/route.h> 818e1864edSKristof Provost 828e1864edSKristof Provost #include <netinet/in.h> 83e68b3792SGleb Smirnoff #include <netinet/in_pcb.h> 848e1864edSKristof Provost #include <netinet/ip.h> 858e1864edSKristof Provost #include <netinet/tcp.h> 868e1864edSKristof Provost #include <netinet/tcp_var.h> 878e1864edSKristof Provost 888e1864edSKristof Provost #include <net/pfvar.h> 89955460d4SKristof Provost #include <netpfil/pf/pf_nv.h> 908e1864edSKristof Provost 918e1864edSKristof Provost #define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x 928e1864edSKristof Provost 938e1864edSKristof Provost union pf_syncookie { 948e1864edSKristof Provost uint8_t cookie; 958e1864edSKristof Provost struct { 968e1864edSKristof Provost uint8_t oddeven:1, 978e1864edSKristof Provost sack_ok:1, 988e1864edSKristof Provost wscale_idx:3, 998e1864edSKristof Provost mss_idx:3; 1008e1864edSKristof Provost } flags; 1018e1864edSKristof Provost }; 1028e1864edSKristof Provost 1038e1864edSKristof Provost #define PF_SYNCOOKIE_SECRET_SIZE SIPHASH_KEY_LENGTH 1048e1864edSKristof Provost #define PF_SYNCOOKIE_SECRET_LIFETIME 15 /* seconds */ 1058e1864edSKristof Provost 1068e1864edSKristof Provost /* Protected by PF_RULES_xLOCK. */ 1078e1864edSKristof Provost struct pf_syncookie_status { 1088e1864edSKristof Provost struct callout keytimeout; 1098e1864edSKristof Provost uint8_t oddeven; 1108e1864edSKristof Provost uint8_t key[2][SIPHASH_KEY_LENGTH]; 111bf863718SKristof Provost uint32_t hiwat; /* absolute; # of states */ 112bf863718SKristof Provost uint32_t lowat; 1138e1864edSKristof Provost }; 1148e1864edSKristof Provost VNET_DEFINE_STATIC(struct pf_syncookie_status, pf_syncookie_status); 1158e1864edSKristof Provost #define V_pf_syncookie_status VNET(pf_syncookie_status) 1168e1864edSKristof Provost 117231e83d3SKristof Provost static int pf_syncookies_setmode(u_int8_t); 1188e1864edSKristof Provost void pf_syncookie_rotate(void *); 1198e1864edSKristof Provost void pf_syncookie_newkey(void); 1208e1864edSKristof Provost uint32_t pf_syncookie_mac(struct pf_pdesc *, union pf_syncookie, 1218e1864edSKristof Provost uint32_t); 1229a405864SKristof Provost uint32_t pf_syncookie_generate(struct pf_pdesc *, uint16_t); 1238e1864edSKristof Provost 1248e1864edSKristof Provost void 1258e1864edSKristof Provost pf_syncookies_init(void) 1268e1864edSKristof Provost { 1278e1864edSKristof Provost callout_init(&V_pf_syncookie_status.keytimeout, 1); 1288e1864edSKristof Provost PF_RULES_WLOCK(); 129933be8d7SKristof Provost 130933be8d7SKristof Provost V_pf_syncookie_status.hiwat = PF_SYNCOOKIES_HIWATPCT * 131933be8d7SKristof Provost V_pf_limits[PF_LIMIT_STATES].limit / 100; 132933be8d7SKristof Provost V_pf_syncookie_status.lowat = PF_SYNCOOKIES_LOWATPCT * 133933be8d7SKristof Provost V_pf_limits[PF_LIMIT_STATES].limit / 100; 134933be8d7SKristof Provost pf_syncookies_setmode(PF_SYNCOOKIES_ADAPTIVE); 135933be8d7SKristof Provost 1368e1864edSKristof Provost PF_RULES_WUNLOCK(); 1378e1864edSKristof Provost } 1388e1864edSKristof Provost 13932271c4dSKristof Provost void 14032271c4dSKristof Provost pf_syncookies_cleanup(void) 14132271c4dSKristof Provost { 14232271c4dSKristof Provost callout_stop(&V_pf_syncookie_status.keytimeout); 14332271c4dSKristof Provost } 14432271c4dSKristof Provost 1458e1864edSKristof Provost int 146231e83d3SKristof Provost pf_get_syncookies(struct pfioc_nv *nv) 147231e83d3SKristof Provost { 148231e83d3SKristof Provost nvlist_t *nvl = NULL; 149231e83d3SKristof Provost void *nvlpacked = NULL; 150be461cdfSKristof Provost int error; 151be461cdfSKristof Provost 152be461cdfSKristof Provost #define ERROUT(x) ERROUT_FUNCTION(errout, x) 153231e83d3SKristof Provost 154231e83d3SKristof Provost nvl = nvlist_create(0); 155231e83d3SKristof Provost if (nvl == NULL) 156be461cdfSKristof Provost ERROUT(ENOMEM); 157231e83d3SKristof Provost 158231e83d3SKristof Provost nvlist_add_bool(nvl, "enabled", 159231e83d3SKristof Provost V_pf_status.syncookies_mode != PF_SYNCOOKIES_NEVER); 160955460d4SKristof Provost nvlist_add_bool(nvl, "adaptive", 161955460d4SKristof Provost V_pf_status.syncookies_mode == PF_SYNCOOKIES_ADAPTIVE); 162955460d4SKristof Provost nvlist_add_number(nvl, "highwater", V_pf_syncookie_status.hiwat); 163955460d4SKristof Provost nvlist_add_number(nvl, "lowwater", V_pf_syncookie_status.lowat); 164a6173e94SKristof Provost nvlist_add_number(nvl, "halfopen_states", 165a6173e94SKristof Provost atomic_load_32(&V_pf_status.states_halfopen)); 166231e83d3SKristof Provost 167231e83d3SKristof Provost nvlpacked = nvlist_pack(nvl, &nv->len); 168be461cdfSKristof Provost if (nvlpacked == NULL) 169be461cdfSKristof Provost ERROUT(ENOMEM); 170be461cdfSKristof Provost 171231e83d3SKristof Provost if (nv->size == 0) { 172be461cdfSKristof Provost ERROUT(0); 173231e83d3SKristof Provost } else if (nv->size < nv->len) { 174be461cdfSKristof Provost ERROUT(ENOSPC); 175231e83d3SKristof Provost } 176231e83d3SKristof Provost 177be461cdfSKristof Provost error = copyout(nvlpacked, nv->data, nv->len); 178be461cdfSKristof Provost 179be461cdfSKristof Provost #undef ERROUT 180be461cdfSKristof Provost errout: 181be461cdfSKristof Provost nvlist_destroy(nvl); 182a37e0e6dSFranco Fichtner free(nvlpacked, M_NVLIST); 183be461cdfSKristof Provost 184be461cdfSKristof Provost return (error); 185231e83d3SKristof Provost } 186231e83d3SKristof Provost 187231e83d3SKristof Provost int 188231e83d3SKristof Provost pf_set_syncookies(struct pfioc_nv *nv) 189231e83d3SKristof Provost { 190231e83d3SKristof Provost nvlist_t *nvl = NULL; 191231e83d3SKristof Provost void *nvlpacked = NULL; 192231e83d3SKristof Provost int error; 193231e83d3SKristof Provost bool enabled, adaptive; 194955460d4SKristof Provost uint32_t hiwat, lowat; 195955460d4SKristof Provost uint8_t newmode; 196955460d4SKristof Provost 197955460d4SKristof Provost #define ERROUT(x) ERROUT_FUNCTION(errout, x) 198231e83d3SKristof Provost 199231e83d3SKristof Provost if (nv->len > pf_ioctl_maxcount) 200231e83d3SKristof Provost return (ENOMEM); 201231e83d3SKristof Provost 202a37e0e6dSFranco Fichtner nvlpacked = malloc(nv->len, M_NVLIST, M_WAITOK); 203231e83d3SKristof Provost error = copyin(nv->data, nvlpacked, nv->len); 204955460d4SKristof Provost if (error) 205955460d4SKristof Provost ERROUT(error); 206231e83d3SKristof Provost 207231e83d3SKristof Provost nvl = nvlist_unpack(nvlpacked, nv->len, 0); 208955460d4SKristof Provost if (nvl == NULL) 209955460d4SKristof Provost ERROUT(EBADMSG); 210231e83d3SKristof Provost 211231e83d3SKristof Provost if (! nvlist_exists_bool(nvl, "enabled") 212955460d4SKristof Provost || ! nvlist_exists_bool(nvl, "adaptive")) 213955460d4SKristof Provost ERROUT(EBADMSG); 214231e83d3SKristof Provost 215231e83d3SKristof Provost enabled = nvlist_get_bool(nvl, "enabled"); 216231e83d3SKristof Provost adaptive = nvlist_get_bool(nvl, "adaptive"); 217955460d4SKristof Provost PFNV_CHK(pf_nvuint32_opt(nvl, "highwater", &hiwat, 218955460d4SKristof Provost V_pf_syncookie_status.hiwat)); 219955460d4SKristof Provost PFNV_CHK(pf_nvuint32_opt(nvl, "lowwater", &lowat, 220955460d4SKristof Provost V_pf_syncookie_status.lowat)); 221231e83d3SKristof Provost 222955460d4SKristof Provost if (lowat >= hiwat) 223955460d4SKristof Provost ERROUT(EINVAL); 224955460d4SKristof Provost 225955460d4SKristof Provost newmode = PF_SYNCOOKIES_NEVER; 226955460d4SKristof Provost if (enabled) 227955460d4SKristof Provost newmode = adaptive ? PF_SYNCOOKIES_ADAPTIVE : PF_SYNCOOKIES_ALWAYS; 228231e83d3SKristof Provost 229231e83d3SKristof Provost PF_RULES_WLOCK(); 230955460d4SKristof Provost error = pf_syncookies_setmode(newmode); 231955460d4SKristof Provost 232955460d4SKristof Provost V_pf_syncookie_status.lowat = lowat; 233955460d4SKristof Provost V_pf_syncookie_status.hiwat = hiwat; 234955460d4SKristof Provost 235231e83d3SKristof Provost PF_RULES_WUNLOCK(); 236231e83d3SKristof Provost 237955460d4SKristof Provost #undef ERROUT 238955460d4SKristof Provost errout: 239955460d4SKristof Provost nvlist_destroy(nvl); 240a37e0e6dSFranco Fichtner free(nvlpacked, M_NVLIST); 241955460d4SKristof Provost 242231e83d3SKristof Provost return (error); 243231e83d3SKristof Provost } 244231e83d3SKristof Provost 245231e83d3SKristof Provost static int 2468e1864edSKristof Provost pf_syncookies_setmode(u_int8_t mode) 2478e1864edSKristof Provost { 2488e1864edSKristof Provost if (mode > PF_SYNCOOKIES_MODE_MAX) 2498e1864edSKristof Provost return (EINVAL); 2508e1864edSKristof Provost 2518e1864edSKristof Provost if (V_pf_status.syncookies_mode == mode) 2528e1864edSKristof Provost return (0); 2538e1864edSKristof Provost 2548e1864edSKristof Provost V_pf_status.syncookies_mode = mode; 2558e1864edSKristof Provost if (V_pf_status.syncookies_mode == PF_SYNCOOKIES_ALWAYS) { 2568e1864edSKristof Provost pf_syncookie_newkey(); 2578e1864edSKristof Provost V_pf_status.syncookies_active = true; 2588e1864edSKristof Provost } 2598e1864edSKristof Provost return (0); 2608e1864edSKristof Provost } 2618e1864edSKristof Provost 2628e1864edSKristof Provost int 2638e1864edSKristof Provost pf_synflood_check(struct pf_pdesc *pd) 2648e1864edSKristof Provost { 2658e1864edSKristof Provost MPASS(pd->proto == IPPROTO_TCP); 2668e1864edSKristof Provost PF_RULES_RASSERT(); 2678e1864edSKristof Provost 2687dc3be36SKajetan Staszkiewicz if (pd->pf_mtag && (pd->pf_mtag->flags & PF_MTAG_FLAG_SYNCOOKIE_RECREATED)) 2698e1864edSKristof Provost return (0); 2708e1864edSKristof Provost 271bf863718SKristof Provost if (V_pf_status.syncookies_mode != PF_SYNCOOKIES_ADAPTIVE) 2728e1864edSKristof Provost return (V_pf_status.syncookies_mode); 273bf863718SKristof Provost 274bf863718SKristof Provost if (!V_pf_status.syncookies_active && 275bf863718SKristof Provost atomic_load_32(&V_pf_status.states_halfopen) > 276bf863718SKristof Provost V_pf_syncookie_status.hiwat) { 277bf863718SKristof Provost /* We'd want to 'pf_syncookie_newkey()' here, but that requires 278bf863718SKristof Provost * the rules write lock, which we can't get with the read lock 279bf863718SKristof Provost * held. */ 280bf863718SKristof Provost callout_reset(&V_pf_syncookie_status.keytimeout, 0, 281bf863718SKristof Provost pf_syncookie_rotate, curvnet); 282bf863718SKristof Provost V_pf_status.syncookies_active = true; 283bf863718SKristof Provost DPFPRINTF(LOG_WARNING, 284bf863718SKristof Provost ("synflood detected, enabling syncookies\n")); 285bf863718SKristof Provost // XXXTODO V_pf_status.lcounters[LCNT_SYNFLOODS]++; 286bf863718SKristof Provost } 287bf863718SKristof Provost 288bf863718SKristof Provost return (V_pf_status.syncookies_active); 2898e1864edSKristof Provost } 2908e1864edSKristof Provost 2918e1864edSKristof Provost void 2929a405864SKristof Provost pf_syncookie_send(struct pf_pdesc *pd) 2938e1864edSKristof Provost { 2948e1864edSKristof Provost uint16_t mss; 2958e1864edSKristof Provost uint32_t iss; 2968e1864edSKristof Provost 2979a405864SKristof Provost mss = max(V_tcp_mssdflt, pf_get_mss(pd)); 2989a405864SKristof Provost iss = pf_syncookie_generate(pd, mss); 2998e1864edSKristof Provost pf_send_tcp(NULL, pd->af, pd->dst, pd->src, *pd->dport, *pd->sport, 3008e1864edSKristof Provost iss, ntohl(pd->hdr.tcp.th_seq) + 1, TH_SYN|TH_ACK, 0, mss, 301*4a7c6d62SMark Johnston 0, M_SKIP_FIREWALL | (pd->m->m_flags & M_LOOP), 0, 0, 302*4a7c6d62SMark Johnston pd->act.rtableid); 3034cab80a8SKristof Provost counter_u64_add(V_pf_status.lcounters[KLCNT_SYNCOOKIES_SENT], 1); 304bf863718SKristof Provost /* XXX Maybe only in adaptive mode? */ 305bf863718SKristof Provost atomic_add_64(&V_pf_status.syncookies_inflight[V_pf_syncookie_status.oddeven], 306bf863718SKristof Provost 1); 3078e1864edSKristof Provost } 3088e1864edSKristof Provost 3099c041b45SKristof Provost bool 3109c041b45SKristof Provost pf_syncookie_check(struct pf_pdesc *pd) 3118e1864edSKristof Provost { 3128e1864edSKristof Provost uint32_t hash, ack, seq; 3138e1864edSKristof Provost union pf_syncookie cookie; 3148e1864edSKristof Provost 3158e1864edSKristof Provost MPASS(pd->proto == IPPROTO_TCP); 3168e1864edSKristof Provost PF_RULES_RASSERT(); 3178e1864edSKristof Provost 3188e1864edSKristof Provost seq = ntohl(pd->hdr.tcp.th_seq) - 1; 3198e1864edSKristof Provost ack = ntohl(pd->hdr.tcp.th_ack) - 1; 3208e1864edSKristof Provost cookie.cookie = (ack & 0xff) ^ (ack >> 24); 3218e1864edSKristof Provost 322bf863718SKristof Provost /* we don't know oddeven before setting the cookie (union) */ 323bf863718SKristof Provost if (atomic_load_64(&V_pf_status.syncookies_inflight[cookie.flags.oddeven]) 324bf863718SKristof Provost == 0) 325bf863718SKristof Provost return (0); 326bf863718SKristof Provost 3278e1864edSKristof Provost hash = pf_syncookie_mac(pd, cookie, seq); 3288e1864edSKristof Provost if ((ack & ~0xff) != (hash & ~0xff)) 3299c041b45SKristof Provost return (false); 3309c041b45SKristof Provost 3319c041b45SKristof Provost return (true); 3329c041b45SKristof Provost } 3339c041b45SKristof Provost 3349c041b45SKristof Provost uint8_t 3359c041b45SKristof Provost pf_syncookie_validate(struct pf_pdesc *pd) 3369c041b45SKristof Provost { 3379c041b45SKristof Provost uint32_t ack; 3389c041b45SKristof Provost union pf_syncookie cookie; 3399c041b45SKristof Provost 3409c041b45SKristof Provost if (! pf_syncookie_check(pd)) 3418e1864edSKristof Provost return (0); 3428e1864edSKristof Provost 3439c041b45SKristof Provost ack = ntohl(pd->hdr.tcp.th_ack) - 1; 3449c041b45SKristof Provost cookie.cookie = (ack & 0xff) ^ (ack >> 24); 3459c041b45SKristof Provost 3464cab80a8SKristof Provost counter_u64_add(V_pf_status.lcounters[KLCNT_SYNCOOKIES_VALID], 1); 347bf863718SKristof Provost atomic_add_64(&V_pf_status.syncookies_inflight[cookie.flags.oddeven], -1); 3484cab80a8SKristof Provost 3498e1864edSKristof Provost return (1); 3508e1864edSKristof Provost } 3518e1864edSKristof Provost 3528e1864edSKristof Provost /* 3538e1864edSKristof Provost * all following functions private 3548e1864edSKristof Provost */ 3558e1864edSKristof Provost void 3568e1864edSKristof Provost pf_syncookie_rotate(void *arg) 3578e1864edSKristof Provost { 3588e1864edSKristof Provost CURVNET_SET((struct vnet *)arg); 3598e1864edSKristof Provost 3608e1864edSKristof Provost /* do we want to disable syncookies? */ 361bf863718SKristof Provost if (V_pf_status.syncookies_active && 362bf863718SKristof Provost ((V_pf_status.syncookies_mode == PF_SYNCOOKIES_ADAPTIVE && 363bf863718SKristof Provost (atomic_load_32(&V_pf_status.states_halfopen) + 364bf863718SKristof Provost atomic_load_64(&V_pf_status.syncookies_inflight[0]) + 365bf863718SKristof Provost atomic_load_64(&V_pf_status.syncookies_inflight[1])) < 366bf863718SKristof Provost V_pf_syncookie_status.lowat) || 367bf863718SKristof Provost V_pf_status.syncookies_mode == PF_SYNCOOKIES_NEVER) 368bf863718SKristof Provost ) { 3698e1864edSKristof Provost V_pf_status.syncookies_active = false; 370bf863718SKristof Provost DPFPRINTF(PF_DEBUG_MISC, ("syncookies disabled\n")); 3718e1864edSKristof Provost } 3728e1864edSKristof Provost 3738e1864edSKristof Provost /* nothing in flight any more? delete keys and return */ 374bf863718SKristof Provost if (!V_pf_status.syncookies_active && 375bf863718SKristof Provost atomic_load_64(&V_pf_status.syncookies_inflight[0]) == 0 && 376bf863718SKristof Provost atomic_load_64(&V_pf_status.syncookies_inflight[1]) == 0) { 3778e1864edSKristof Provost memset(V_pf_syncookie_status.key[0], 0, 3788e1864edSKristof Provost PF_SYNCOOKIE_SECRET_SIZE); 3798e1864edSKristof Provost memset(V_pf_syncookie_status.key[1], 0, 3808e1864edSKristof Provost PF_SYNCOOKIE_SECRET_SIZE); 3818e1864edSKristof Provost CURVNET_RESTORE(); 3828e1864edSKristof Provost return; 3838e1864edSKristof Provost } 3848e1864edSKristof Provost 385bf863718SKristof Provost PF_RULES_WLOCK(); 3868e1864edSKristof Provost /* new key, including timeout */ 3878e1864edSKristof Provost pf_syncookie_newkey(); 388bf863718SKristof Provost PF_RULES_WUNLOCK(); 3898e1864edSKristof Provost 3908e1864edSKristof Provost CURVNET_RESTORE(); 3918e1864edSKristof Provost } 3928e1864edSKristof Provost 3938e1864edSKristof Provost void 3948e1864edSKristof Provost pf_syncookie_newkey(void) 3958e1864edSKristof Provost { 3968e1864edSKristof Provost PF_RULES_WASSERT(); 3978e1864edSKristof Provost 398bf863718SKristof Provost MPASS(V_pf_syncookie_status.oddeven < 2); 3998e1864edSKristof Provost V_pf_syncookie_status.oddeven = (V_pf_syncookie_status.oddeven + 1) & 0x1; 400bf863718SKristof Provost atomic_store_64(&V_pf_status.syncookies_inflight[V_pf_syncookie_status.oddeven], 0); 4018e1864edSKristof Provost arc4random_buf(V_pf_syncookie_status.key[V_pf_syncookie_status.oddeven], 4028e1864edSKristof Provost PF_SYNCOOKIE_SECRET_SIZE); 4038e1864edSKristof Provost callout_reset(&V_pf_syncookie_status.keytimeout, 404bf863718SKristof Provost PF_SYNCOOKIE_SECRET_LIFETIME * hz, pf_syncookie_rotate, curvnet); 4058e1864edSKristof Provost } 4068e1864edSKristof Provost 4078e1864edSKristof Provost /* 4088e1864edSKristof Provost * Distribution and probability of certain MSS values. Those in between are 4098e1864edSKristof Provost * rounded down to the next lower one. 4108e1864edSKristof Provost * [An Analysis of TCP Maximum Segment Sizes, S. Alcock and R. Nelson, 2011] 4118e1864edSKristof Provost * .2% .3% 5% 7% 7% 20% 15% 45% 4128e1864edSKristof Provost */ 4138e1864edSKristof Provost static int pf_syncookie_msstab[] = 4148e1864edSKristof Provost { 216, 536, 1200, 1360, 1400, 1440, 1452, 1460 }; 4158e1864edSKristof Provost 4168e1864edSKristof Provost /* 4178e1864edSKristof Provost * Distribution and probability of certain WSCALE values. 4188e1864edSKristof Provost * The absence of the WSCALE option is encoded with index zero. 4198e1864edSKristof Provost * [WSCALE values histograms, Allman, 2012] 4208e1864edSKristof Provost * X 10 10 35 5 6 14 10% by host 4218e1864edSKristof Provost * X 11 4 5 5 18 49 3% by connections 4228e1864edSKristof Provost */ 4238e1864edSKristof Provost static int pf_syncookie_wstab[] = { 0, 0, 1, 2, 4, 6, 7, 8 }; 4248e1864edSKristof Provost 4258e1864edSKristof Provost uint32_t 4268e1864edSKristof Provost pf_syncookie_mac(struct pf_pdesc *pd, union pf_syncookie cookie, uint32_t seq) 4278e1864edSKristof Provost { 4288e1864edSKristof Provost SIPHASH_CTX ctx; 4298e1864edSKristof Provost uint32_t siphash[2]; 4308e1864edSKristof Provost 4318e1864edSKristof Provost PF_RULES_RASSERT(); 4328e1864edSKristof Provost MPASS(pd->proto == IPPROTO_TCP); 4338e1864edSKristof Provost 4348e1864edSKristof Provost SipHash24_Init(&ctx); 4358e1864edSKristof Provost SipHash_SetKey(&ctx, V_pf_syncookie_status.key[cookie.flags.oddeven]); 4368e1864edSKristof Provost 4378e1864edSKristof Provost switch (pd->af) { 4388e1864edSKristof Provost case AF_INET: 4398e1864edSKristof Provost SipHash_Update(&ctx, pd->src, sizeof(pd->src->v4)); 4408e1864edSKristof Provost SipHash_Update(&ctx, pd->dst, sizeof(pd->dst->v4)); 4418e1864edSKristof Provost break; 4428e1864edSKristof Provost case AF_INET6: 4438e1864edSKristof Provost SipHash_Update(&ctx, pd->src, sizeof(pd->src->v6)); 4448e1864edSKristof Provost SipHash_Update(&ctx, pd->dst, sizeof(pd->dst->v6)); 4458e1864edSKristof Provost break; 4468e1864edSKristof Provost default: 4478e1864edSKristof Provost panic("unknown address family"); 4488e1864edSKristof Provost } 4498e1864edSKristof Provost 4508e1864edSKristof Provost SipHash_Update(&ctx, pd->sport, sizeof(*pd->sport)); 4518e1864edSKristof Provost SipHash_Update(&ctx, pd->dport, sizeof(*pd->dport)); 4528e1864edSKristof Provost SipHash_Update(&ctx, &seq, sizeof(seq)); 4538e1864edSKristof Provost SipHash_Update(&ctx, &cookie, sizeof(cookie)); 4548e1864edSKristof Provost SipHash_Final((uint8_t *)&siphash, &ctx); 4558e1864edSKristof Provost 4568e1864edSKristof Provost return (siphash[0] ^ siphash[1]); 4578e1864edSKristof Provost } 4588e1864edSKristof Provost 4598e1864edSKristof Provost uint32_t 4609a405864SKristof Provost pf_syncookie_generate(struct pf_pdesc *pd, uint16_t mss) 4618e1864edSKristof Provost { 4628e1864edSKristof Provost uint8_t i, wscale; 4638e1864edSKristof Provost uint32_t iss, hash; 4648e1864edSKristof Provost union pf_syncookie cookie; 4658e1864edSKristof Provost 4668e1864edSKristof Provost PF_RULES_RASSERT(); 4678e1864edSKristof Provost 4688e1864edSKristof Provost cookie.cookie = 0; 4698e1864edSKristof Provost 4708e1864edSKristof Provost /* map MSS */ 4718e1864edSKristof Provost for (i = nitems(pf_syncookie_msstab) - 1; 4728e1864edSKristof Provost pf_syncookie_msstab[i] > mss && i > 0; i--) 4738e1864edSKristof Provost /* nada */; 4748e1864edSKristof Provost cookie.flags.mss_idx = i; 4758e1864edSKristof Provost 4768e1864edSKristof Provost /* map WSCALE */ 4779a405864SKristof Provost wscale = pf_get_wscale(pd); 4788e1864edSKristof Provost for (i = nitems(pf_syncookie_wstab) - 1; 4798e1864edSKristof Provost pf_syncookie_wstab[i] > wscale && i > 0; i--) 4808e1864edSKristof Provost /* nada */; 4818e1864edSKristof Provost cookie.flags.wscale_idx = i; 4828e1864edSKristof Provost cookie.flags.sack_ok = 0; /* XXX */ 4838e1864edSKristof Provost 4848e1864edSKristof Provost cookie.flags.oddeven = V_pf_syncookie_status.oddeven; 4858e1864edSKristof Provost hash = pf_syncookie_mac(pd, cookie, ntohl(pd->hdr.tcp.th_seq)); 4868e1864edSKristof Provost 4878e1864edSKristof Provost /* 4888e1864edSKristof Provost * Put the flags into the hash and XOR them to get better ISS number 4898e1864edSKristof Provost * variance. This doesn't enhance the cryptographic strength and is 4908e1864edSKristof Provost * done to prevent the 8 cookie bits from showing up directly on the 4918e1864edSKristof Provost * wire. 4928e1864edSKristof Provost */ 4938e1864edSKristof Provost iss = hash & ~0xff; 4948e1864edSKristof Provost iss |= cookie.cookie ^ (hash >> 24); 4958e1864edSKristof Provost 4968e1864edSKristof Provost return (iss); 4978e1864edSKristof Provost } 4988e1864edSKristof Provost 4998e1864edSKristof Provost struct mbuf * 50005896f1eSKristof Provost pf_syncookie_recreate_syn(struct pf_pdesc *pd) 5018e1864edSKristof Provost { 5028e1864edSKristof Provost uint8_t wscale; 5038e1864edSKristof Provost uint16_t mss; 5048e1864edSKristof Provost uint32_t ack, seq; 5058e1864edSKristof Provost union pf_syncookie cookie; 5068e1864edSKristof Provost 5078e1864edSKristof Provost seq = ntohl(pd->hdr.tcp.th_seq) - 1; 5088e1864edSKristof Provost ack = ntohl(pd->hdr.tcp.th_ack) - 1; 5098e1864edSKristof Provost cookie.cookie = (ack & 0xff) ^ (ack >> 24); 5108e1864edSKristof Provost 5118e1864edSKristof Provost if (cookie.flags.mss_idx >= nitems(pf_syncookie_msstab) || 5128e1864edSKristof Provost cookie.flags.wscale_idx >= nitems(pf_syncookie_wstab)) 5138e1864edSKristof Provost return (NULL); 5148e1864edSKristof Provost 5158e1864edSKristof Provost mss = pf_syncookie_msstab[cookie.flags.mss_idx]; 5168e1864edSKristof Provost wscale = pf_syncookie_wstab[cookie.flags.wscale_idx]; 5178e1864edSKristof Provost 5188e1864edSKristof Provost return (pf_build_tcp(NULL, pd->af, pd->src, pd->dst, *pd->sport, 519*4a7c6d62SMark Johnston *pd->dport, seq, 0, TH_SYN, wscale, mss, pd->ttl, 520*4a7c6d62SMark Johnston (pd->m->m_flags & M_LOOP), 0, PF_MTAG_FLAG_SYNCOOKIE_RECREATED, 521*4a7c6d62SMark Johnston pd->act.rtableid)); 5228e1864edSKristof Provost } 523