Enable active defense of IP address / lease, as described in RFC5227.
This commit is contained in:
parent
74a79314d7
commit
8b27b41c0c
63
ndhc/arp.c
63
ndhc/arp.c
@ -1,5 +1,5 @@
|
|||||||
/* arp.c - arp ping checking
|
/* arp.c - arp ping checking
|
||||||
* Time-stamp: <2011-07-05 11:06:00 njk>
|
* Time-stamp: <2011-07-05 12:49:09 njk>
|
||||||
*
|
*
|
||||||
* Copyright 2010-2011 Nicholas J. Kain <njkain@gmail.com>
|
* Copyright 2010-2011 Nicholas J. Kain <njkain@gmail.com>
|
||||||
*
|
*
|
||||||
@ -43,6 +43,18 @@
|
|||||||
#define ARP_MSG_SIZE 0x2a
|
#define ARP_MSG_SIZE 0x2a
|
||||||
#define ARP_RETRANS_DELAY 5000 // ms
|
#define ARP_RETRANS_DELAY 5000 // ms
|
||||||
|
|
||||||
|
// From RFC5227
|
||||||
|
#define PROBE_WAIT 1000 // initial random delay
|
||||||
|
#define PROBE_NUM 3 // number of probe packets
|
||||||
|
#define PROBE_MIN 1000 // minimum delay until repeated probe
|
||||||
|
#define PROBE_MAX 2000 // maximum delay until repeated probe
|
||||||
|
#define ANNOUNCE_WAIT 2000 // delay before announcing
|
||||||
|
#define ANNOUNCE_NUM 2 // number of Announcement packets
|
||||||
|
#define ANNOUNCE_INTERVAL 2000 // time between Announcement packets
|
||||||
|
#define MAX_CONFLICTS 10 // max conflicts before rate-limiting
|
||||||
|
#define RATE_LIMIT_INTERVAL 60000 // delay between successive attempts
|
||||||
|
#define DEFEND_INTERVAL 10000 // minimum interval between defensive ARPs
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
AS_NONE = 0, // Nothing to react to wrt ARP
|
AS_NONE = 0, // Nothing to react to wrt ARP
|
||||||
AS_COLLISION_CHECK, // Checking to see if another host has our IP before
|
AS_COLLISION_CHECK, // Checking to see if another host has our IP before
|
||||||
@ -61,6 +73,11 @@ struct arp_stats {
|
|||||||
};
|
};
|
||||||
static struct arp_stats arp_stats[AS_MAX-1];
|
static struct arp_stats arp_stats[AS_MAX-1];
|
||||||
static int using_arp_bpf; // Is a BPF installed on the ARP socket?
|
static int using_arp_bpf; // Is a BPF installed on the ARP socket?
|
||||||
|
int arp_relentless_def; // Don't give up defense no matter what.
|
||||||
|
static long long last_conflict_ts; // TS of the last conflicting ARP seen.
|
||||||
|
static long long pending_def_ts; // TS of the upcoming defense ARP send.
|
||||||
|
static long long last_def_ts; // TS of the most recent defense ARP sent.
|
||||||
|
static int def_old_oldTimeout; // Old value of cs->oldTimeout.
|
||||||
|
|
||||||
static struct arpMsg arpreply;
|
static struct arpMsg arpreply;
|
||||||
static int arpreply_offset;
|
static int arpreply_offset;
|
||||||
@ -384,6 +401,10 @@ void arp_success(struct client_state_t *cs)
|
|||||||
cs->clientAddr = arp_dhcp_packet.yiaddr;
|
cs->clientAddr = arp_dhcp_packet.yiaddr;
|
||||||
cs->dhcpState = DS_BOUND;
|
cs->dhcpState = DS_BOUND;
|
||||||
cs->init = 0;
|
cs->init = 0;
|
||||||
|
last_conflict_ts = 0;
|
||||||
|
pending_def_ts = 0;
|
||||||
|
last_def_ts = 0;
|
||||||
|
def_old_oldTimeout = 0;
|
||||||
ifchange_bind(&arp_dhcp_packet);
|
ifchange_bind(&arp_dhcp_packet);
|
||||||
if (cs->arpPrevState == DS_RENEWING || cs->arpPrevState == DS_REBINDING) {
|
if (cs->arpPrevState == DS_RENEWING || cs->arpPrevState == DS_REBINDING) {
|
||||||
arp_switch_state(cs, AS_DEFENSE);
|
arp_switch_state(cs, AS_DEFENSE);
|
||||||
@ -468,6 +489,17 @@ static int arp_is_query_reply(struct arpMsg *am)
|
|||||||
// Perform retransmission if necessary.
|
// Perform retransmission if necessary.
|
||||||
void arp_retransmit(struct client_state_t *cs)
|
void arp_retransmit(struct client_state_t *cs)
|
||||||
{
|
{
|
||||||
|
if (pending_def_ts) {
|
||||||
|
log_line("arp: Defending our lease IP.");
|
||||||
|
pending_def_ts = 0;
|
||||||
|
last_def_ts = curms();
|
||||||
|
arp_announcement(cs);
|
||||||
|
if (def_old_oldTimeout) {
|
||||||
|
cs->timeout = cs->oldTimeout;
|
||||||
|
cs->oldTimeout = def_old_oldTimeout;
|
||||||
|
def_old_oldTimeout = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (curms() < arp_stats[arpState].ts + ARP_RETRANS_DELAY)
|
if (curms() < arp_stats[arpState].ts + ARP_RETRANS_DELAY)
|
||||||
return;
|
return;
|
||||||
log_line("DEBUG: retransmission timeout in arp state %u", arpState);
|
log_line("DEBUG: retransmission timeout in arp state %u", arpState);
|
||||||
@ -490,6 +522,31 @@ void arp_retransmit(struct client_state_t *cs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void arp_do_defense(struct client_state_t *cs)
|
||||||
|
{
|
||||||
|
log_line("arp: detected a peer attempting to use our IP!");
|
||||||
|
long long cur_conflict_ts = curms();
|
||||||
|
if (!last_conflict_ts ||
|
||||||
|
cur_conflict_ts - last_conflict_ts < DEFEND_INTERVAL) {
|
||||||
|
log_line("arp: Defending our lease IP.");
|
||||||
|
last_def_ts = cur_conflict_ts;
|
||||||
|
arp_announcement(cs);
|
||||||
|
} else if (!arp_relentless_def) {
|
||||||
|
log_line("arp: Conflicting peer is persistent. Requesting new lease.");
|
||||||
|
send_release(cs);
|
||||||
|
reinit_selecting(cs, 0);
|
||||||
|
} else {
|
||||||
|
pending_def_ts = last_def_ts + DEFEND_INTERVAL;
|
||||||
|
long long def_xmit_ts = pending_def_ts - cur_conflict_ts;
|
||||||
|
if (!cs->timeout || cs->timeout > def_xmit_ts) {
|
||||||
|
def_old_oldTimeout = cs->oldTimeout;
|
||||||
|
cs->oldTimeout = cs->timeout;
|
||||||
|
cs->timeout = def_xmit_ts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_conflict_ts = cur_conflict_ts;
|
||||||
|
}
|
||||||
|
|
||||||
void handle_arp_response(struct client_state_t *cs)
|
void handle_arp_response(struct client_state_t *cs)
|
||||||
{
|
{
|
||||||
int r = 0;
|
int r = 0;
|
||||||
@ -570,9 +627,7 @@ void handle_arp_response(struct client_state_t *cs)
|
|||||||
if (!arp_validate_bpf_defense(cs, &arpreply))
|
if (!arp_validate_bpf_defense(cs, &arpreply))
|
||||||
break;
|
break;
|
||||||
case AS_DEFENSE:
|
case AS_DEFENSE:
|
||||||
log_line("arp: detected a peer attempting to use our IP!");
|
arp_do_defense(cs);
|
||||||
// XXX: actually do work...
|
|
||||||
log_line("DEBUG: TODO actually do work!");
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
log_error("handle_arp_response: called in invalid state %u", arpState);
|
log_error("handle_arp_response: called in invalid state %u", arpState);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* arp.h - functions to call the interface change daemon
|
/* arp.h - functions to call the interface change daemon
|
||||||
* Time-stamp: <2011-07-05 10:49:41 njk>
|
* Time-stamp: <2011-07-05 12:54:21 njk>
|
||||||
*
|
*
|
||||||
* Copyright 2010-2011 Nicholas J. Kain <njkain@gmail.com>
|
* Copyright 2010-2011 Nicholas J. Kain <njkain@gmail.com>
|
||||||
*
|
*
|
||||||
@ -45,6 +45,8 @@ struct arpMsg {
|
|||||||
uint8_t pad[18]; // 2a pad for min. ethernet payload (60 bytes)
|
uint8_t pad[18]; // 2a pad for min. ethernet payload (60 bytes)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern int arp_relentless_def;
|
||||||
|
|
||||||
int arp_close_fd(struct client_state_t *cs);
|
int arp_close_fd(struct client_state_t *cs);
|
||||||
int arp_check(struct client_state_t *cs, struct dhcpmsg *packet);
|
int arp_check(struct client_state_t *cs, struct dhcpmsg *packet);
|
||||||
int arp_gw_check(struct client_state_t *cs);
|
int arp_gw_check(struct client_state_t *cs);
|
||||||
|
13
ndhc/ndhc.c
13
ndhc/ndhc.c
@ -1,5 +1,5 @@
|
|||||||
/* ndhc.c - DHCP client
|
/* ndhc.c - DHCP client
|
||||||
* Time-stamp: <2011-07-05 11:18:03 njk>
|
* Time-stamp: <2011-07-05 12:54:15 njk>
|
||||||
*
|
*
|
||||||
* (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
|
* (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
|
||||||
*
|
*
|
||||||
@ -94,6 +94,8 @@ static void show_usage(void)
|
|||||||
" -r, --request=IP IP address to request (default: none)\n"
|
" -r, --request=IP IP address to request (default: none)\n"
|
||||||
" -u, --user=USER Change privileges to this user\n"
|
" -u, --user=USER Change privileges to this user\n"
|
||||||
" -C, --chroot=DIR Chroot to this directory\n"
|
" -C, --chroot=DIR Chroot to this directory\n"
|
||||||
|
" -d, --relentless-defense Never back off in defending IP against\n"
|
||||||
|
" conflicting hosts (servers only)\n"
|
||||||
" -v, --version Display version\n"
|
" -v, --version Display version\n"
|
||||||
);
|
);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
@ -220,17 +222,18 @@ int main(int argc, char **argv)
|
|||||||
{"now", no_argument, 0, 'n'},
|
{"now", no_argument, 0, 'n'},
|
||||||
{"quit", no_argument, 0, 'q'},
|
{"quit", no_argument, 0, 'q'},
|
||||||
{"request", required_argument, 0, 'r'},
|
{"request", required_argument, 0, 'r'},
|
||||||
{"version", no_argument, 0, 'v'},
|
|
||||||
{"vendorid", required_argument, 0, 'V'},
|
{"vendorid", required_argument, 0, 'V'},
|
||||||
{"user", required_argument, 0, 'u'},
|
{"user", required_argument, 0, 'u'},
|
||||||
{"chroot", required_argument, 0, 'C'},
|
{"chroot", required_argument, 0, 'C'},
|
||||||
|
{"relentless-defense", no_argument, 0, 'd'},
|
||||||
|
{"version", no_argument, 0, 'v'},
|
||||||
{"help", no_argument, 0, '?'},
|
{"help", no_argument, 0, '?'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
c = getopt_long(argc, argv, "c:fbp:h:i:np:l:qr:u:C:vV:", arg_options,
|
c = getopt_long(argc, argv, "c:fbp:h:i:np:l:qr:V:u:C:dv", arg_options,
|
||||||
&option_index);
|
&option_index);
|
||||||
if (c == -1) break;
|
if (c == -1) break;
|
||||||
|
|
||||||
@ -285,6 +288,10 @@ int main(int argc, char **argv)
|
|||||||
case 'C':
|
case 'C':
|
||||||
strlcpy(chroot_dir, optarg, sizeof chroot_dir);
|
strlcpy(chroot_dir, optarg, sizeof chroot_dir);
|
||||||
break;
|
break;
|
||||||
|
case 'd':
|
||||||
|
log_line("DEBUG: relentless defense enabled");
|
||||||
|
arp_relentless_def = 1;
|
||||||
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
printf("ndhc, version " VERSION "\n\n");
|
printf("ndhc, version " VERSION "\n\n");
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
Loading…
Reference in New Issue
Block a user