From 8b27b41c0c20b7d410d8151e892b4be35d9590c2 Mon Sep 17 00:00:00 2001 From: "Nicholas J. Kain" Date: Tue, 5 Jul 2011 13:03:55 -0400 Subject: [PATCH] Enable active defense of IP address / lease, as described in RFC5227. --- ndhc/arp.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++---- ndhc/arp.h | 4 +++- ndhc/ndhc.c | 13 ++++++++--- 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/ndhc/arp.c b/ndhc/arp.c index ee8ca4c..e733589 100644 --- a/ndhc/arp.c +++ b/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 * @@ -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); diff --git a/ndhc/arp.h b/ndhc/arp.h index e6cdc6b..6e157a2 100644 --- a/ndhc/arp.h +++ b/ndhc/arp.h @@ -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 * @@ -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); diff --git a/ndhc/ndhc.c b/ndhc/ndhc.c index 6f77eae..0508c9d 100644 --- a/ndhc/ndhc.c +++ b/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 * @@ -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);