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
|
||||
* 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>
|
||||
*
|
||||
@ -43,6 +43,18 @@
|
||||
#define ARP_MSG_SIZE 0x2a
|
||||
#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 {
|
||||
AS_NONE = 0, // Nothing to react to wrt ARP
|
||||
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 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 int arpreply_offset;
|
||||
@ -384,6 +401,10 @@ void arp_success(struct client_state_t *cs)
|
||||
cs->clientAddr = arp_dhcp_packet.yiaddr;
|
||||
cs->dhcpState = DS_BOUND;
|
||||
cs->init = 0;
|
||||
last_conflict_ts = 0;
|
||||
pending_def_ts = 0;
|
||||
last_def_ts = 0;
|
||||
def_old_oldTimeout = 0;
|
||||
ifchange_bind(&arp_dhcp_packet);
|
||||
if (cs->arpPrevState == DS_RENEWING || cs->arpPrevState == DS_REBINDING) {
|
||||
arp_switch_state(cs, AS_DEFENSE);
|
||||
@ -468,6 +489,17 @@ static int arp_is_query_reply(struct arpMsg *am)
|
||||
// Perform retransmission if necessary.
|
||||
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)
|
||||
return;
|
||||
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)
|
||||
{
|
||||
int r = 0;
|
||||
@ -570,9 +627,7 @@ void handle_arp_response(struct client_state_t *cs)
|
||||
if (!arp_validate_bpf_defense(cs, &arpreply))
|
||||
break;
|
||||
case AS_DEFENSE:
|
||||
log_line("arp: detected a peer attempting to use our IP!");
|
||||
// XXX: actually do work...
|
||||
log_line("DEBUG: TODO actually do work!");
|
||||
arp_do_defense(cs);
|
||||
break;
|
||||
default:
|
||||
log_error("handle_arp_response: called in invalid state %u", arpState);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* 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>
|
||||
*
|
||||
@ -45,6 +45,8 @@ struct arpMsg {
|
||||
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_check(struct client_state_t *cs, struct dhcpmsg *packet);
|
||||
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
|
||||
* 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>
|
||||
*
|
||||
@ -94,6 +94,8 @@ static void show_usage(void)
|
||||
" -r, --request=IP IP address to request (default: none)\n"
|
||||
" -u, --user=USER Change privileges to this user\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"
|
||||
);
|
||||
exit(EXIT_SUCCESS);
|
||||
@ -220,17 +222,18 @@ int main(int argc, char **argv)
|
||||
{"now", no_argument, 0, 'n'},
|
||||
{"quit", no_argument, 0, 'q'},
|
||||
{"request", required_argument, 0, 'r'},
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{"vendorid", required_argument, 0, 'V'},
|
||||
{"user", required_argument, 0, 'u'},
|
||||
{"chroot", required_argument, 0, 'C'},
|
||||
{"relentless-defense", no_argument, 0, 'd'},
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, '?'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while (1) {
|
||||
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);
|
||||
if (c == -1) break;
|
||||
|
||||
@ -285,6 +288,10 @@ int main(int argc, char **argv)
|
||||
case 'C':
|
||||
strlcpy(chroot_dir, optarg, sizeof chroot_dir);
|
||||
break;
|
||||
case 'd':
|
||||
log_line("DEBUG: relentless defense enabled");
|
||||
arp_relentless_def = 1;
|
||||
break;
|
||||
case 'v':
|
||||
printf("ndhc, version " VERSION "\n\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
|
Loading…
Reference in New Issue
Block a user