Changeset View
Changeset View
Standalone View
Standalone View
sys/netpfil/pf/pf_syncookies.c
| Show First 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | |||||
| #define PF_SYNCOOKIE_SECRET_SIZE SIPHASH_KEY_LENGTH | #define PF_SYNCOOKIE_SECRET_SIZE SIPHASH_KEY_LENGTH | ||||
| #define PF_SYNCOOKIE_SECRET_LIFETIME 15 /* seconds */ | #define PF_SYNCOOKIE_SECRET_LIFETIME 15 /* seconds */ | ||||
| /* Protected by PF_RULES_xLOCK. */ | /* Protected by PF_RULES_xLOCK. */ | ||||
| struct pf_syncookie_status { | struct pf_syncookie_status { | ||||
| struct callout keytimeout; | struct callout keytimeout; | ||||
| uint8_t oddeven; | uint8_t oddeven; | ||||
| uint8_t key[2][SIPHASH_KEY_LENGTH]; | uint8_t key[2][SIPHASH_KEY_LENGTH]; | ||||
| uint32_t hiwat; /* absolute; # of states */ | |||||
| uint32_t lowat; | |||||
| }; | }; | ||||
| VNET_DEFINE_STATIC(struct pf_syncookie_status, pf_syncookie_status); | VNET_DEFINE_STATIC(struct pf_syncookie_status, pf_syncookie_status); | ||||
| #define V_pf_syncookie_status VNET(pf_syncookie_status) | #define V_pf_syncookie_status VNET(pf_syncookie_status) | ||||
| static int pf_syncookies_setmode(u_int8_t); | static int pf_syncookies_setmode(u_int8_t); | ||||
| void pf_syncookie_rotate(void *); | void pf_syncookie_rotate(void *); | ||||
| void pf_syncookie_newkey(void); | void pf_syncookie_newkey(void); | ||||
| uint32_t pf_syncookie_mac(struct pf_pdesc *, union pf_syncookie, | uint32_t pf_syncookie_mac(struct pf_pdesc *, union pf_syncookie, | ||||
| ▲ Show 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | |||||
| pf_synflood_check(struct pf_pdesc *pd) | pf_synflood_check(struct pf_pdesc *pd) | ||||
| { | { | ||||
| MPASS(pd->proto == IPPROTO_TCP); | MPASS(pd->proto == IPPROTO_TCP); | ||||
| PF_RULES_RASSERT(); | PF_RULES_RASSERT(); | ||||
| if (pd->pf_mtag && (pd->pf_mtag->tag & PF_TAG_SYNCOOKIE_RECREATED)) | if (pd->pf_mtag && (pd->pf_mtag->tag & PF_TAG_SYNCOOKIE_RECREATED)) | ||||
| return (0); | return (0); | ||||
| if (V_pf_status.syncookies_mode != PF_SYNCOOKIES_ADAPTIVE) | |||||
| return (V_pf_status.syncookies_mode); | return (V_pf_status.syncookies_mode); | ||||
| if (!V_pf_status.syncookies_active && | |||||
| atomic_load_32(&V_pf_status.states_halfopen) > | |||||
| V_pf_syncookie_status.hiwat) { | |||||
| /* We'd want to 'pf_syncookie_newkey()' here, but that requires | |||||
| * the rules write lock, which we can't get with the read lock | |||||
| * held. */ | |||||
| callout_reset(&V_pf_syncookie_status.keytimeout, 0, | |||||
| pf_syncookie_rotate, curvnet); | |||||
| V_pf_status.syncookies_active = true; | |||||
| DPFPRINTF(LOG_WARNING, | |||||
| ("synflood detected, enabling syncookies\n")); | |||||
| // XXXTODO V_pf_status.lcounters[LCNT_SYNFLOODS]++; | |||||
| } | } | ||||
| return (V_pf_status.syncookies_active); | |||||
| } | |||||
| void | void | ||||
| pf_syncookie_send(struct mbuf *m, int off, struct pf_pdesc *pd) | pf_syncookie_send(struct mbuf *m, int off, struct pf_pdesc *pd) | ||||
| { | { | ||||
| uint16_t mss; | uint16_t mss; | ||||
| uint32_t iss; | uint32_t iss; | ||||
| mss = max(V_tcp_mssdflt, pf_get_mss(m, off, pd->hdr.tcp.th_off, pd->af)); | 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); | iss = pf_syncookie_generate(m, off, pd, mss); | ||||
| pf_send_tcp(NULL, pd->af, pd->dst, pd->src, *pd->dport, *pd->sport, | 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, | iss, ntohl(pd->hdr.tcp.th_seq) + 1, TH_SYN|TH_ACK, 0, mss, | ||||
| 0, 1, 0); | 0, 1, 0); | ||||
| counter_u64_add(V_pf_status.lcounters[KLCNT_SYNCOOKIES_SENT], 1); | counter_u64_add(V_pf_status.lcounters[KLCNT_SYNCOOKIES_SENT], 1); | ||||
| /* XXX Maybe only in adaptive mode? */ | |||||
| atomic_add_64(&V_pf_status.syncookies_inflight[V_pf_syncookie_status.oddeven], | |||||
| 1); | |||||
| } | } | ||||
| uint8_t | uint8_t | ||||
| pf_syncookie_validate(struct pf_pdesc *pd) | pf_syncookie_validate(struct pf_pdesc *pd) | ||||
| { | { | ||||
| uint32_t hash, ack, seq; | uint32_t hash, ack, seq; | ||||
| union pf_syncookie cookie; | union pf_syncookie cookie; | ||||
| MPASS(pd->proto == IPPROTO_TCP); | MPASS(pd->proto == IPPROTO_TCP); | ||||
| PF_RULES_RASSERT(); | PF_RULES_RASSERT(); | ||||
| seq = ntohl(pd->hdr.tcp.th_seq) - 1; | seq = ntohl(pd->hdr.tcp.th_seq) - 1; | ||||
| ack = ntohl(pd->hdr.tcp.th_ack) - 1; | ack = ntohl(pd->hdr.tcp.th_ack) - 1; | ||||
| cookie.cookie = (ack & 0xff) ^ (ack >> 24); | cookie.cookie = (ack & 0xff) ^ (ack >> 24); | ||||
| /* we don't know oddeven before setting the cookie (union) */ | |||||
| if (atomic_load_64(&V_pf_status.syncookies_inflight[cookie.flags.oddeven]) | |||||
| == 0) | |||||
| return (0); | |||||
| hash = pf_syncookie_mac(pd, cookie, seq); | hash = pf_syncookie_mac(pd, cookie, seq); | ||||
| if ((ack & ~0xff) != (hash & ~0xff)) | if ((ack & ~0xff) != (hash & ~0xff)) | ||||
| return (0); | return (0); | ||||
| counter_u64_add(V_pf_status.lcounters[KLCNT_SYNCOOKIES_VALID], 1); | counter_u64_add(V_pf_status.lcounters[KLCNT_SYNCOOKIES_VALID], 1); | ||||
| atomic_add_64(&V_pf_status.syncookies_inflight[cookie.flags.oddeven], -1); | |||||
| return (1); | return (1); | ||||
| } | } | ||||
| /* | /* | ||||
| * all following functions private | * all following functions private | ||||
| */ | */ | ||||
| void | void | ||||
| pf_syncookie_rotate(void *arg) | pf_syncookie_rotate(void *arg) | ||||
| { | { | ||||
| CURVNET_SET((struct vnet *)arg); | CURVNET_SET((struct vnet *)arg); | ||||
| /* do we want to disable syncookies? */ | /* do we want to disable syncookies? */ | ||||
| if (V_pf_status.syncookies_active) { | if (V_pf_status.syncookies_active && | ||||
| ((V_pf_status.syncookies_mode == PF_SYNCOOKIES_ADAPTIVE && | |||||
| (atomic_load_32(&V_pf_status.states_halfopen) + | |||||
| atomic_load_64(&V_pf_status.syncookies_inflight[0]) + | |||||
| atomic_load_64(&V_pf_status.syncookies_inflight[1])) < | |||||
| V_pf_syncookie_status.lowat) || | |||||
| V_pf_status.syncookies_mode == PF_SYNCOOKIES_NEVER) | |||||
| ) { | |||||
| V_pf_status.syncookies_active = false; | V_pf_status.syncookies_active = false; | ||||
| DPFPRINTF(PF_DEBUG_MISC, ("syncookies disabled")); | DPFPRINTF(PF_DEBUG_MISC, ("syncookies disabled\n")); | ||||
| } | } | ||||
| /* nothing in flight any more? delete keys and return */ | /* nothing in flight any more? delete keys and return */ | ||||
| if (!V_pf_status.syncookies_active) { | if (!V_pf_status.syncookies_active && | ||||
| atomic_load_64(&V_pf_status.syncookies_inflight[0]) == 0 && | |||||
| atomic_load_64(&V_pf_status.syncookies_inflight[1]) == 0) { | |||||
| memset(V_pf_syncookie_status.key[0], 0, | memset(V_pf_syncookie_status.key[0], 0, | ||||
| PF_SYNCOOKIE_SECRET_SIZE); | PF_SYNCOOKIE_SECRET_SIZE); | ||||
| memset(V_pf_syncookie_status.key[1], 0, | memset(V_pf_syncookie_status.key[1], 0, | ||||
| PF_SYNCOOKIE_SECRET_SIZE); | PF_SYNCOOKIE_SECRET_SIZE); | ||||
| CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
| return; | return; | ||||
| } | } | ||||
| PF_RULES_WLOCK(); | |||||
| /* new key, including timeout */ | /* new key, including timeout */ | ||||
| pf_syncookie_newkey(); | pf_syncookie_newkey(); | ||||
| PF_RULES_WUNLOCK(); | |||||
| CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
| } | } | ||||
| void | void | ||||
| pf_syncookie_newkey(void) | pf_syncookie_newkey(void) | ||||
| { | { | ||||
| PF_RULES_WASSERT(); | PF_RULES_WASSERT(); | ||||
| MPASS(V_pf_syncookie_status.oddeven < 2); | |||||
| V_pf_syncookie_status.oddeven = (V_pf_syncookie_status.oddeven + 1) & 0x1; | V_pf_syncookie_status.oddeven = (V_pf_syncookie_status.oddeven + 1) & 0x1; | ||||
| atomic_store_64(&V_pf_status.syncookies_inflight[V_pf_syncookie_status.oddeven], 0); | |||||
| arc4random_buf(V_pf_syncookie_status.key[V_pf_syncookie_status.oddeven], | arc4random_buf(V_pf_syncookie_status.key[V_pf_syncookie_status.oddeven], | ||||
| PF_SYNCOOKIE_SECRET_SIZE); | PF_SYNCOOKIE_SECRET_SIZE); | ||||
| callout_reset(&V_pf_syncookie_status.keytimeout, | callout_reset(&V_pf_syncookie_status.keytimeout, | ||||
| PF_SYNCOOKIE_SECRET_LIFETIME, pf_syncookie_rotate, curvnet); | PF_SYNCOOKIE_SECRET_LIFETIME * hz, pf_syncookie_rotate, curvnet); | ||||
| } | } | ||||
| /* | /* | ||||
| * Distribution and probability of certain MSS values. Those in between are | * Distribution and probability of certain MSS values. Those in between are | ||||
| * rounded down to the next lower one. | * rounded down to the next lower one. | ||||
| * [An Analysis of TCP Maximum Segment Sizes, S. Alcock and R. Nelson, 2011] | * [An Analysis of TCP Maximum Segment Sizes, S. Alcock and R. Nelson, 2011] | ||||
| * .2% .3% 5% 7% 7% 20% 15% 45% | * .2% .3% 5% 7% 7% 20% 15% 45% | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 110 Lines • Show Last 20 Lines | |||||