Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/ip_id.c
Show First 20 Lines • Show All 118 Lines • ▼ Show 20 Lines | |||||
#define V_array_size VNET(array_size) | #define V_array_size VNET(array_size) | ||||
#define V_random_id_collisions VNET(random_id_collisions) | #define V_random_id_collisions VNET(random_id_collisions) | ||||
#define V_random_id_total VNET(random_id_total) | #define V_random_id_total VNET(random_id_total) | ||||
#define V_ip_id_mtx VNET(ip_id_mtx) | #define V_ip_id_mtx VNET(ip_id_mtx) | ||||
/* | /* | ||||
* Non-random ID state engine is simply a per-cpu counter. | * Non-random ID state engine is simply a per-cpu counter. | ||||
*/ | */ | ||||
static VNET_DEFINE(counter_u64_t, ip_id); | static VNET_DEFINE(unsigned, ip_id); | ||||
#define V_ip_id VNET(ip_id) | #define V_ip_id VNET(ip_id) | ||||
static int sysctl_ip_randomid(SYSCTL_HANDLER_ARGS); | static int sysctl_ip_randomid(SYSCTL_HANDLER_ARGS); | ||||
static int sysctl_ip_id_change(SYSCTL_HANDLER_ARGS); | static int sysctl_ip_id_change(SYSCTL_HANDLER_ARGS); | ||||
static void ip_initid(int); | static void ip_initid_array(int); | ||||
static uint16_t ip_randomid(void); | static uint16_t ip_randomid(void); | ||||
static void ipid_sysinit(void); | static void ipid_sysinit(void); | ||||
static void ipid_sysuninit(void); | static void ipid_sysuninit(void); | ||||
SYSCTL_DECL(_net_inet_ip); | SYSCTL_DECL(_net_inet_ip); | ||||
SYSCTL_PROC(_net_inet_ip, OID_AUTO, random_id, | SYSCTL_PROC(_net_inet_ip, OID_AUTO, random_id, | ||||
CTLTYPE_INT | CTLFLAG_VNET | CTLFLAG_RW, | CTLTYPE_INT | CTLFLAG_VNET | CTLFLAG_RW, | ||||
&VNET_NAME(ip_do_randomid), 0, sysctl_ip_randomid, "IU", | &VNET_NAME(ip_do_randomid), 0, sysctl_ip_randomid, "IU", | ||||
Show All 19 Lines | sysctl_ip_randomid(SYSCTL_HANDLER_ARGS) | ||||
error = sysctl_handle_int(oidp, &new, 0, req); | error = sysctl_handle_int(oidp, &new, 0, req); | ||||
if (error || req->newptr == NULL) | if (error || req->newptr == NULL) | ||||
return (error); | return (error); | ||||
if (new != 0 && new != 1) | if (new != 0 && new != 1) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (new == V_ip_do_randomid) | if (new == V_ip_do_randomid) | ||||
return (0); | return (0); | ||||
if (new == 1 && V_ip_do_randomid == 0) | if (new == 1 && V_ip_do_randomid == 0) | ||||
ip_initid(8192); | ip_initid_array(8192); | ||||
/* We don't free memory when turning random ID off, due to race. */ | /* We don't free memory when turning random ID off, due to race. */ | ||||
V_ip_do_randomid = new; | V_ip_do_randomid = new; | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
sysctl_ip_id_change(SYSCTL_HANDLER_ARGS) | sysctl_ip_id_change(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int error, new; | int error, new; | ||||
new = V_array_size; | new = V_array_size; | ||||
error = sysctl_handle_int(oidp, &new, 0, req); | error = sysctl_handle_int(oidp, &new, 0, req); | ||||
if (error == 0 && req->newptr) { | if (error == 0 && req->newptr) { | ||||
if (new >= 512 && new <= 32768) | if (new >= 512 && new <= 32768) | ||||
ip_initid(new); | ip_initid_array(new); | ||||
else | else | ||||
error = EINVAL; | error = EINVAL; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
ip_initid(int new_size) | ip_initid_array(int new_size) | ||||
{ | { | ||||
uint16_t *new_array; | uint16_t *new_array; | ||||
bitstr_t *new_bits; | bitstr_t *new_bits; | ||||
new_array = malloc(new_size * sizeof(uint16_t), M_IPID, | new_array = malloc(new_size * sizeof(uint16_t), M_IPID, | ||||
M_WAITOK | M_ZERO); | M_WAITOK | M_ZERO); | ||||
new_bits = malloc(bitstr_size(65536), M_IPID, M_WAITOK | M_ZERO); | new_bits = malloc(bitstr_size(65536), M_IPID, M_WAITOK | M_ZERO); | ||||
Show All 33 Lines | ip_randomid(void) | ||||
V_array_ptr++; | V_array_ptr++; | ||||
if (V_array_ptr == V_array_size) | if (V_array_ptr == V_array_size) | ||||
V_array_ptr = 0; | V_array_ptr = 0; | ||||
V_random_id_total++; | V_random_id_total++; | ||||
mtx_unlock(&V_ip_id_mtx); | mtx_unlock(&V_ip_id_mtx); | ||||
return (new_id); | return (new_id); | ||||
} | } | ||||
unsigned | |||||
ip_initid(void) | |||||
{ | |||||
unsigned temp = 0; | |||||
arc4rand(&temp, sizeof(temp), 0); | |||||
return (temp); | |||||
} | |||||
void | void | ||||
ip_fillid(struct ip *ip) | ip_fillid(struct ip *ip, unsigned *pid) | ||||
{ | { | ||||
/* | /* | ||||
* Per RFC6864 Section 4 | * Per RFC6864 Section 4 | ||||
* | * | ||||
* o Atomic datagrams: (DF==1) && (MF==0) && (frag_offset==0) | * o Atomic datagrams: (DF==1) && (MF==0) && (frag_offset==0) | ||||
* o Non-atomic datagrams: (DF==0) || (MF==1) || (frag_offset>0) | * o Non-atomic datagrams: (DF==0) || (MF==1) || (frag_offset>0) | ||||
*/ | */ | ||||
if (V_ip_rfc6864 && (ip->ip_off & htons(IP_DF)) == htons(IP_DF)) | if (V_ip_rfc6864 && (ip->ip_off & htons(IP_DF)) == htons(IP_DF)) | ||||
ip->ip_id = 0; | ip->ip_id = 0; | ||||
else if (V_ip_do_randomid) | else if (V_ip_do_randomid) | ||||
ip->ip_id = ip_randomid(); | ip->ip_id = ip_randomid(); | ||||
else { | else if (pid != NULL) { | ||||
counter_u64_add(V_ip_id, 1); | /* no need to byteswap IP ID field */ | ||||
/* | ip->ip_id = atomic_fetchadd_int(pid, 1) & 0xFFFF; | ||||
* There are two issues about this trick, to be kept in mind. | } else { /* fallback to global IP ID counter */ | ||||
* 1) We can migrate between counter_u64_add() and next | /* no need to byteswap IP ID field */ | ||||
* line, and grab counter from other CPU, resulting in too | ip->ip_id = atomic_fetchadd_int(&V_ip_id, 1) & 0xFFFF; | ||||
* quick ID reuse. This is tolerable in our particular case, | |||||
* since probability of such event is much lower then reuse | |||||
* of ID due to legitimate overflow, that at modern Internet | |||||
* speeds happens all the time. | |||||
* 2) We are relying on the fact that counter(9) is based on | |||||
* UMA_ZONE_PCPU uma(9) zone. We also take only last | |||||
* sixteen bits of a counter, so we don't care about the | |||||
* fact that machines with 32-bit word update their counters | |||||
* not atomically. | |||||
*/ | |||||
ip->ip_id = htons((*(uint64_t *)zpcpu_get(V_ip_id)) & 0xffff); | |||||
} | } | ||||
} | } | ||||
static void | static void | ||||
ipid_sysinit(void) | ipid_sysinit(void) | ||||
{ | { | ||||
mtx_init(&V_ip_id_mtx, "ip_id_mtx", NULL, MTX_DEF); | mtx_init(&V_ip_id_mtx, "ip_id_mtx", NULL, MTX_DEF); | ||||
V_ip_id = counter_u64_alloc(M_WAITOK); | V_ip_id = ip_initid(); | ||||
for (int i = 0; i < mp_ncpus; i++) | |||||
arc4rand(zpcpu_get_cpu(V_ip_id, i), sizeof(uint64_t), 0); | |||||
} | } | ||||
VNET_SYSINIT(ip_id, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, ipid_sysinit, NULL); | VNET_SYSINIT(ip_id, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, ipid_sysinit, NULL); | ||||
static void | static void | ||||
ipid_sysuninit(void) | ipid_sysuninit(void) | ||||
{ | { | ||||
mtx_destroy(&V_ip_id_mtx); | mtx_destroy(&V_ip_id_mtx); | ||||
if (V_id_array != NULL) { | if (V_id_array != NULL) { | ||||
free(V_id_array, M_IPID); | free(V_id_array, M_IPID); | ||||
free(V_id_bits, M_IPID); | free(V_id_bits, M_IPID); | ||||
} | } | ||||
counter_u64_free(V_ip_id); | |||||
} | } | ||||
VNET_SYSUNINIT(ip_id, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, ipid_sysuninit, NULL); | VNET_SYSUNINIT(ip_id, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, ipid_sysuninit, NULL); |