Enable active defense of IP address / lease, as described in RFC5227.

This commit is contained in:
Nicholas J. Kain 2011-07-05 13:03:55 -04:00
parent 74a79314d7
commit 8b27b41c0c
3 changed files with 72 additions and 8 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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);