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 |