The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netpfil/pf/pf_syncookies.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $OpenBSD: pf_syncookies.c,v 1.7 2018/09/10 15:54:28 henning Exp $ */
    2 
    3 /* Copyright (c) 2016,2017 Henning Brauer <henning@openbsd.org>
    4  * Copyright (c) 2016 Alexandr Nedvedicky <sashan@openbsd.org>
    5  *
    6  * syncookie parts based on FreeBSD sys/netinet/tcp_syncache.c
    7  *
    8  * Copyright (c) 2001 McAfee, Inc.
    9  * Copyright (c) 2006,2013 Andre Oppermann, Internet Business Solutions AG
   10  * All rights reserved.
   11  *
   12  * This software was developed for the FreeBSD Project by Jonathan Lemon
   13  * and McAfee Research, the Security Research Division of McAfee, Inc. under
   14  * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
   15  * DARPA CHATS research program. [2001 McAfee, Inc.]
   16  *
   17  * Redistribution and use in source and binary forms, with or without
   18  * modification, are permitted provided that the following conditions
   19  * are met:
   20  * 1. Redistributions of source code must retain the above copyright
   21  *    notice, this list of conditions and the following disclaimer.
   22  * 2. Redistributions in binary form must reproduce the above copyright
   23  *    notice, this list of conditions and the following disclaimer in the
   24  *    documentation and/or other materials provided with the distribution.
   25  *
   26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   36  * SUCH DAMAGE.
   37  */
   38 
   39 /*
   40  * when we're under synflood, we use syncookies to prevent state table
   41  * exhaustion. Trigger for the synflood mode is the number of half-open
   42  * connections in the state table.
   43  * We leave synflood mode when the number of half-open states - including
   44  * in-flight syncookies - drops far enough again
   45  */
   46  
   47 /*
   48  * syncookie enabled Initial Sequence Number:
   49  *  24 bit MAC
   50  *   3 bit WSCALE index
   51  *   3 bit MSS index
   52  *   1 bit SACK permitted
   53  *   1 bit odd/even secret
   54  *
   55  * References:
   56  *  RFC4987 TCP SYN Flooding Attacks and Common Mitigations
   57  *  http://cr.yp.to/syncookies.html    (overview)
   58  *  http://cr.yp.to/syncookies/archive (details)
   59  */
   60 
   61 //#include "pflog.h"
   62 
   63 #include <sys/param.h>
   64 #include <sys/systm.h>
   65 #include <sys/mbuf.h>
   66 #include <sys/filio.h>
   67 #include <sys/socket.h>
   68 #include <sys/socketvar.h>
   69 #include <sys/kernel.h>
   70 #include <sys/time.h>
   71 #include <sys/proc.h>
   72 #include <sys/rwlock.h>
   73 #include <sys/syslog.h>
   74 
   75 #include <crypto/siphash/siphash.h>
   76 
   77 #include <net/if.h>
   78 #include <net/if_var.h>
   79 #include <net/if_types.h>
   80 #include <net/route.h>
   81 
   82 #include <netinet/in.h>
   83 #include <netinet/ip.h>
   84 #include <netinet/tcp.h>
   85 #include <netinet/tcp_var.h>
   86 
   87 #include <net/pfvar.h>
   88 #include <netpfil/pf/pf_nv.h>
   89 
   90 #define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x
   91 
   92 union pf_syncookie {
   93         uint8_t         cookie;
   94         struct {
   95                 uint8_t oddeven:1,
   96                         sack_ok:1,
   97                         wscale_idx:3,
   98                         mss_idx:3;
   99         } flags;
  100 };
  101 
  102 #define PF_SYNCOOKIE_SECRET_SIZE        SIPHASH_KEY_LENGTH
  103 #define PF_SYNCOOKIE_SECRET_LIFETIME    15 /* seconds */
  104 
  105 /* Protected by PF_RULES_xLOCK. */
  106 struct pf_syncookie_status {
  107         struct callout  keytimeout;
  108         uint8_t         oddeven;
  109         uint8_t         key[2][SIPHASH_KEY_LENGTH];
  110         uint32_t        hiwat;  /* absolute; # of states */
  111         uint32_t        lowat;
  112 };
  113 VNET_DEFINE_STATIC(struct pf_syncookie_status, pf_syncookie_status);
  114 #define V_pf_syncookie_status   VNET(pf_syncookie_status)
  115 
  116 static int      pf_syncookies_setmode(u_int8_t);
  117 void            pf_syncookie_rotate(void *);
  118 void            pf_syncookie_newkey(void);
  119 uint32_t        pf_syncookie_mac(struct pf_pdesc *, union pf_syncookie,
  120                     uint32_t);
  121 uint32_t        pf_syncookie_generate(struct mbuf *m, int off, struct pf_pdesc *,
  122                     uint16_t);
  123 
  124 void
  125 pf_syncookies_init(void)
  126 {
  127         callout_init(&V_pf_syncookie_status.keytimeout, 1);
  128         PF_RULES_WLOCK();
  129         pf_syncookies_setmode(PF_SYNCOOKIES_NEVER);
  130         PF_RULES_WUNLOCK();
  131 }
  132 
  133 void
  134 pf_syncookies_cleanup(void)
  135 {
  136         callout_stop(&V_pf_syncookie_status.keytimeout);
  137 }
  138 
  139 int
  140 pf_get_syncookies(struct pfioc_nv *nv)
  141 {
  142         nvlist_t        *nvl = NULL;
  143         void            *nvlpacked = NULL;
  144         int              error;
  145 
  146 #define ERROUT(x)       ERROUT_FUNCTION(errout, x)
  147 
  148         nvl = nvlist_create(0);
  149         if (nvl == NULL)
  150                 ERROUT(ENOMEM);
  151 
  152         nvlist_add_bool(nvl, "enabled",
  153             V_pf_status.syncookies_mode != PF_SYNCOOKIES_NEVER);
  154         nvlist_add_bool(nvl, "adaptive",
  155             V_pf_status.syncookies_mode == PF_SYNCOOKIES_ADAPTIVE);
  156         nvlist_add_number(nvl, "highwater", V_pf_syncookie_status.hiwat);
  157         nvlist_add_number(nvl, "lowwater", V_pf_syncookie_status.lowat);
  158 
  159         nvlpacked = nvlist_pack(nvl, &nv->len);
  160         if (nvlpacked == NULL)
  161                 ERROUT(ENOMEM);
  162 
  163         if (nv->size == 0) {
  164                 ERROUT(0);
  165         } else if (nv->size < nv->len) {
  166                 ERROUT(ENOSPC);
  167         }
  168 
  169         error = copyout(nvlpacked, nv->data, nv->len);
  170 
  171 #undef ERROUT
  172 errout:
  173         nvlist_destroy(nvl);
  174         free(nvlpacked, M_NVLIST);
  175 
  176         return (error);
  177 }
  178 
  179 int
  180 pf_set_syncookies(struct pfioc_nv *nv)
  181 {
  182         nvlist_t        *nvl = NULL;
  183         void            *nvlpacked = NULL;
  184         int              error;
  185         bool             enabled, adaptive;
  186         uint32_t         hiwat, lowat;
  187         uint8_t          newmode;
  188 
  189 #define ERROUT(x)       ERROUT_FUNCTION(errout, x)
  190 
  191         if (nv->len > pf_ioctl_maxcount)
  192                 return (ENOMEM);
  193 
  194         nvlpacked = malloc(nv->len, M_NVLIST, M_WAITOK);
  195         if (nvlpacked == NULL)
  196                 return (ENOMEM);
  197 
  198         error = copyin(nv->data, nvlpacked, nv->len);
  199         if (error)
  200                 ERROUT(error);
  201 
  202         nvl = nvlist_unpack(nvlpacked, nv->len, 0);
  203         if (nvl == NULL)
  204                 ERROUT(EBADMSG);
  205 
  206         if (! nvlist_exists_bool(nvl, "enabled")
  207             || ! nvlist_exists_bool(nvl, "adaptive"))
  208                 ERROUT(EBADMSG);
  209 
  210         enabled = nvlist_get_bool(nvl, "enabled");
  211         adaptive = nvlist_get_bool(nvl, "adaptive");
  212         PFNV_CHK(pf_nvuint32_opt(nvl, "highwater", &hiwat,
  213             V_pf_syncookie_status.hiwat));
  214         PFNV_CHK(pf_nvuint32_opt(nvl, "lowwater", &lowat,
  215             V_pf_syncookie_status.lowat));
  216 
  217         if (lowat >= hiwat)
  218                 ERROUT(EINVAL);
  219 
  220         newmode = PF_SYNCOOKIES_NEVER;
  221         if (enabled)
  222                 newmode = adaptive ? PF_SYNCOOKIES_ADAPTIVE : PF_SYNCOOKIES_ALWAYS;
  223 
  224         PF_RULES_WLOCK();
  225         error = pf_syncookies_setmode(newmode);
  226 
  227         V_pf_syncookie_status.lowat = lowat;
  228         V_pf_syncookie_status.hiwat = hiwat;
  229 
  230         PF_RULES_WUNLOCK();
  231 
  232 #undef ERROUT
  233 errout:
  234         nvlist_destroy(nvl);
  235         free(nvlpacked, M_NVLIST);
  236 
  237         return (error);
  238 }
  239 
  240 static int
  241 pf_syncookies_setmode(u_int8_t mode)
  242 {
  243         if (mode > PF_SYNCOOKIES_MODE_MAX)
  244                 return (EINVAL);
  245 
  246         if (V_pf_status.syncookies_mode == mode)
  247                 return (0);
  248 
  249         V_pf_status.syncookies_mode = mode;
  250         if (V_pf_status.syncookies_mode == PF_SYNCOOKIES_ALWAYS) {
  251                 pf_syncookie_newkey();
  252                 V_pf_status.syncookies_active = true;
  253         }
  254         return (0);
  255 }
  256 
  257 int
  258 pf_synflood_check(struct pf_pdesc *pd)
  259 {
  260         MPASS(pd->proto == IPPROTO_TCP);
  261         PF_RULES_RASSERT();
  262 
  263         if (pd->pf_mtag && (pd->pf_mtag->tag & PF_TAG_SYNCOOKIE_RECREATED))
  264                 return (0);
  265 
  266         if (V_pf_status.syncookies_mode != PF_SYNCOOKIES_ADAPTIVE)
  267                 return (V_pf_status.syncookies_mode);
  268 
  269         if (!V_pf_status.syncookies_active &&
  270             atomic_load_32(&V_pf_status.states_halfopen) >
  271             V_pf_syncookie_status.hiwat) {
  272                 /* We'd want to 'pf_syncookie_newkey()' here, but that requires
  273                  * the rules write lock, which we can't get with the read lock
  274                  * held. */
  275                 callout_reset(&V_pf_syncookie_status.keytimeout, 0,
  276                     pf_syncookie_rotate, curvnet);
  277                 V_pf_status.syncookies_active = true;
  278                 DPFPRINTF(LOG_WARNING,
  279                     ("synflood detected, enabling syncookies\n"));
  280                 // XXXTODO V_pf_status.lcounters[LCNT_SYNFLOODS]++;
  281         }
  282 
  283         return (V_pf_status.syncookies_active);
  284 }
  285 
  286 void
  287 pf_syncookie_send(struct mbuf *m, int off, struct pf_pdesc *pd)
  288 {
  289         uint16_t        mss;
  290         uint32_t        iss;
  291 
  292         mss = max(V_tcp_mssdflt, pf_get_mss(m, off, pd->hdr.tcp.th_off, pd->af));
  293         iss = pf_syncookie_generate(m, off, pd, mss);
  294         pf_send_tcp(NULL, pd->af, pd->dst, pd->src, *pd->dport, *pd->sport,
  295             iss, ntohl(pd->hdr.tcp.th_seq) + 1, TH_SYN|TH_ACK, 0, mss,
  296             0, 1, 0);
  297         counter_u64_add(V_pf_status.lcounters[KLCNT_SYNCOOKIES_SENT], 1);
  298         /* XXX Maybe only in adaptive mode? */
  299         atomic_add_64(&V_pf_status.syncookies_inflight[V_pf_syncookie_status.oddeven],
  300             1);
  301 }
  302 
  303 bool
  304 pf_syncookie_check(struct pf_pdesc *pd)
  305 {
  306         uint32_t                 hash, ack, seq;
  307         union pf_syncookie       cookie;
  308 
  309         MPASS(pd->proto == IPPROTO_TCP);
  310         PF_RULES_RASSERT();
  311 
  312         seq = ntohl(pd->hdr.tcp.th_seq) - 1;
  313         ack = ntohl(pd->hdr.tcp.th_ack) - 1;
  314         cookie.cookie = (ack & 0xff) ^ (ack >> 24);
  315 
  316         /* we don't know oddeven before setting the cookie (union) */
  317         if (atomic_load_64(&V_pf_status.syncookies_inflight[cookie.flags.oddeven])
  318             == 0)
  319                 return (0);
  320 
  321         hash = pf_syncookie_mac(pd, cookie, seq);
  322         if ((ack & ~0xff) != (hash & ~0xff))
  323                 return (false);
  324 
  325         return (true);
  326 }
  327 
  328 uint8_t
  329 pf_syncookie_validate(struct pf_pdesc *pd)
  330 {
  331         uint32_t                 ack;
  332         union pf_syncookie       cookie;
  333 
  334         if (! pf_syncookie_check(pd))
  335                 return (0);
  336 
  337         ack = ntohl(pd->hdr.tcp.th_ack) - 1;
  338         cookie.cookie = (ack & 0xff) ^ (ack >> 24);
  339 
  340         counter_u64_add(V_pf_status.lcounters[KLCNT_SYNCOOKIES_VALID], 1);
  341         atomic_add_64(&V_pf_status.syncookies_inflight[cookie.flags.oddeven], -1);
  342 
  343         return (1);
  344 }
  345 
  346 /*
  347  * all following functions private
  348  */
  349 void
  350 pf_syncookie_rotate(void *arg)
  351 {
  352         CURVNET_SET((struct vnet *)arg);
  353 
  354         /* do we want to disable syncookies? */
  355         if (V_pf_status.syncookies_active &&
  356             ((V_pf_status.syncookies_mode == PF_SYNCOOKIES_ADAPTIVE &&
  357             (atomic_load_32(&V_pf_status.states_halfopen) +
  358             atomic_load_64(&V_pf_status.syncookies_inflight[0]) +
  359             atomic_load_64(&V_pf_status.syncookies_inflight[1])) <
  360             V_pf_syncookie_status.lowat) ||
  361             V_pf_status.syncookies_mode == PF_SYNCOOKIES_NEVER)
  362                         ) {
  363                 V_pf_status.syncookies_active = false;
  364                 DPFPRINTF(PF_DEBUG_MISC, ("syncookies disabled\n"));
  365         }
  366 
  367         /* nothing in flight any more? delete keys and return */
  368         if (!V_pf_status.syncookies_active &&
  369             atomic_load_64(&V_pf_status.syncookies_inflight[0]) == 0 &&
  370             atomic_load_64(&V_pf_status.syncookies_inflight[1]) == 0) {
  371                 memset(V_pf_syncookie_status.key[0], 0,
  372                     PF_SYNCOOKIE_SECRET_SIZE);
  373                 memset(V_pf_syncookie_status.key[1], 0,
  374                     PF_SYNCOOKIE_SECRET_SIZE);
  375                 CURVNET_RESTORE();
  376                 return;
  377         }
  378 
  379         PF_RULES_WLOCK();
  380         /* new key, including timeout */
  381         pf_syncookie_newkey();
  382         PF_RULES_WUNLOCK();
  383 
  384         CURVNET_RESTORE();
  385 }
  386 
  387 void
  388 pf_syncookie_newkey(void)
  389 {
  390         PF_RULES_WASSERT();
  391 
  392         MPASS(V_pf_syncookie_status.oddeven < 2);
  393         V_pf_syncookie_status.oddeven = (V_pf_syncookie_status.oddeven + 1) & 0x1;
  394         atomic_store_64(&V_pf_status.syncookies_inflight[V_pf_syncookie_status.oddeven], 0);
  395         arc4random_buf(V_pf_syncookie_status.key[V_pf_syncookie_status.oddeven],
  396             PF_SYNCOOKIE_SECRET_SIZE);
  397         callout_reset(&V_pf_syncookie_status.keytimeout,
  398             PF_SYNCOOKIE_SECRET_LIFETIME * hz, pf_syncookie_rotate, curvnet);
  399 }
  400 
  401 /*
  402  * Distribution and probability of certain MSS values.  Those in between are
  403  * rounded down to the next lower one.
  404  * [An Analysis of TCP Maximum Segment Sizes, S. Alcock and R. Nelson, 2011]
  405  *   .2%  .3%   5%    7%    7%    20%   15%   45%
  406  */
  407 static int pf_syncookie_msstab[] =
  408     { 216, 536, 1200, 1360, 1400, 1440, 1452, 1460 };
  409 
  410 /*
  411  * Distribution and probability of certain WSCALE values.
  412  * The absence of the WSCALE option is encoded with index zero.
  413  * [WSCALE values histograms, Allman, 2012]
  414  *                                  X 10 10 35  5  6 14 10%   by host
  415  *                                  X 11  4  5  5 18 49  3%   by connections
  416  */
  417 static int pf_syncookie_wstab[] = { 0, 0, 1, 2, 4, 6, 7, 8 };
  418 
  419 uint32_t
  420 pf_syncookie_mac(struct pf_pdesc *pd, union pf_syncookie cookie, uint32_t seq)
  421 {
  422         SIPHASH_CTX     ctx;
  423         uint32_t        siphash[2];
  424 
  425         PF_RULES_RASSERT();
  426         MPASS(pd->proto == IPPROTO_TCP);
  427 
  428         SipHash24_Init(&ctx);
  429         SipHash_SetKey(&ctx, V_pf_syncookie_status.key[cookie.flags.oddeven]);
  430 
  431         switch (pd->af) {
  432         case AF_INET:
  433                 SipHash_Update(&ctx, pd->src, sizeof(pd->src->v4));
  434                 SipHash_Update(&ctx, pd->dst, sizeof(pd->dst->v4));
  435                 break;
  436         case AF_INET6:
  437                 SipHash_Update(&ctx, pd->src, sizeof(pd->src->v6));
  438                 SipHash_Update(&ctx, pd->dst, sizeof(pd->dst->v6));
  439                 break;
  440         default:
  441                 panic("unknown address family");
  442         }
  443 
  444         SipHash_Update(&ctx, pd->sport, sizeof(*pd->sport));
  445         SipHash_Update(&ctx, pd->dport, sizeof(*pd->dport));
  446         SipHash_Update(&ctx, &seq, sizeof(seq));
  447         SipHash_Update(&ctx, &cookie, sizeof(cookie));
  448         SipHash_Final((uint8_t *)&siphash, &ctx);
  449 
  450         return (siphash[0] ^ siphash[1]);
  451 }
  452 
  453 uint32_t
  454 pf_syncookie_generate(struct mbuf *m, int off, struct pf_pdesc *pd,
  455     uint16_t mss)
  456 {
  457         uint8_t                  i, wscale;
  458         uint32_t                 iss, hash;
  459         union pf_syncookie       cookie;
  460 
  461         PF_RULES_RASSERT();
  462 
  463         cookie.cookie = 0;
  464 
  465         /* map MSS */
  466         for (i = nitems(pf_syncookie_msstab) - 1;
  467             pf_syncookie_msstab[i] > mss && i > 0; i--)
  468                 /* nada */;
  469         cookie.flags.mss_idx = i;
  470 
  471         /* map WSCALE */
  472         wscale = pf_get_wscale(m, off, pd->hdr.tcp.th_off, pd->af);
  473         for (i = nitems(pf_syncookie_wstab) - 1;
  474             pf_syncookie_wstab[i] > wscale && i > 0; i--)
  475                 /* nada */;
  476         cookie.flags.wscale_idx = i;
  477         cookie.flags.sack_ok = 0;       /* XXX */
  478 
  479         cookie.flags.oddeven = V_pf_syncookie_status.oddeven;
  480         hash = pf_syncookie_mac(pd, cookie, ntohl(pd->hdr.tcp.th_seq));
  481 
  482         /*
  483          * Put the flags into the hash and XOR them to get better ISS number
  484          * variance.  This doesn't enhance the cryptographic strength and is
  485          * done to prevent the 8 cookie bits from showing up directly on the
  486          * wire.
  487          */
  488         iss = hash & ~0xff;
  489         iss |= cookie.cookie ^ (hash >> 24);
  490 
  491         return (iss);
  492 }
  493 
  494 struct mbuf *
  495 pf_syncookie_recreate_syn(uint8_t ttl, int off, struct pf_pdesc *pd)
  496 {
  497         uint8_t                  wscale;
  498         uint16_t                 mss;
  499         uint32_t                 ack, seq;
  500         union pf_syncookie       cookie;
  501 
  502         seq = ntohl(pd->hdr.tcp.th_seq) - 1;
  503         ack = ntohl(pd->hdr.tcp.th_ack) - 1;
  504         cookie.cookie = (ack & 0xff) ^ (ack >> 24);
  505 
  506         if (cookie.flags.mss_idx >= nitems(pf_syncookie_msstab) ||
  507             cookie.flags.wscale_idx >= nitems(pf_syncookie_wstab))
  508                 return (NULL);
  509 
  510         mss = pf_syncookie_msstab[cookie.flags.mss_idx];
  511         wscale = pf_syncookie_wstab[cookie.flags.wscale_idx];
  512 
  513         return (pf_build_tcp(NULL, pd->af, pd->src, pd->dst, *pd->sport,
  514             *pd->dport, seq, 0, TH_SYN, wscale, mss, ttl, 0,
  515             PF_TAG_SYNCOOKIE_RECREATED));
  516 }

Cache object: df00c00bf0f65223460038ee37b9393d


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.