Make sure our assigned address is unique on our segment by using arp.

This commit is contained in:
Nicholas J. Kain 2010-11-15 20:06:50 -05:00
parent 5eb3c18b01
commit 9dce1a0378
9 changed files with 235 additions and 20 deletions

View File

@ -1,5 +1,5 @@
/* io.c - light wrappers for POSIX i/o functions /* io.c - light wrappers for POSIX i/o functions
* Time-stamp: <2010-11-13 08:22:53 njk> * Time-stamp: <2010-11-15 19:45:39 njk>
* *
* (c) 2010 Nicholas J. Kain <njkain at gmail dot com> * (c) 2010 Nicholas J. Kain <njkain at gmail dot com>
* All rights reserved. * All rights reserved.
@ -28,6 +28,8 @@
*/ */
#include <unistd.h> #include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h> #include <errno.h>
/* returns -1 on error, >= 0 and equal to # chars read on success */ /* returns -1 on error, >= 0 and equal to # chars read on success */
@ -65,3 +67,22 @@ int safe_write(int fd, const char *buf, int len)
} }
return s; return s;
} }
/* returns -1 on error, >= 0 and equal to # chars written on success */
int safe_sendto(int fd, const char *buf, int len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen)
{
int r, s = 0;
while (s < len) {
r = sendto(fd, buf + s, len - s, flags, dest_addr, addrlen);
if (r == -1) {
if (errno == EINTR)
continue;
else
return -1;
}
s += r;
}
return s;
}

View File

@ -1,5 +1,5 @@
/* io.h - light wrappers for POSIX i/o functions /* io.h - light wrappers for POSIX i/o functions
* Time-stamp: <2010-11-13 08:22:48 njk> * Time-stamp: <2010-11-15 19:45:32 njk>
* *
* (c) 2010 Nicholas J. Kain <njkain at gmail dot com> * (c) 2010 Nicholas J. Kain <njkain at gmail dot com>
* All rights reserved. * All rights reserved.
@ -32,5 +32,7 @@
int safe_read(int fd, char *buf, int len); int safe_read(int fd, char *buf, int len);
int safe_write(int fd, const char *buf, int len); int safe_write(int fd, const char *buf, int len);
int safe_sendto(int fd, const char *buf, int len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
#endif /* NCM_IO_H_ */ #endif /* NCM_IO_H_ */

View File

@ -8,6 +8,7 @@ set(NDHC_SRCS
packet.c packet.c
script.c script.c
clientpacket.c clientpacket.c
arpping.c
ndhc.c ndhc.c
) )

160
ndhc/arpping.c Normal file
View File

@ -0,0 +1,160 @@
/*
* Shamelessly ripped off from busybox's udhcpc variant, which in turn was...
* Mostly stolen from: dhcpcd - DHCP client daemon
* by Yoichi Hariguchi <yoichi@fore.com>
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/if_ether.h>
#include <net/if_arp.h>
#include <sys/time.h>
#include <errno.h>
#include <poll.h>
#include "dhcpd.h"
#include "log.h"
#include "strl.h"
#include "io.h"
struct arpMsg {
/* Ethernet header */
uint8_t h_dest[6]; /* 00 destination ether addr */
uint8_t h_source[6]; /* 06 source ether addr */
uint16_t h_proto; /* 0c packet type ID field */
/* ARP packet */
uint16_t htype; /* 0e hardware type (must be ARPHRD_ETHER) */
uint16_t ptype; /* 10 protocol type (must be ETH_P_IP) */
uint8_t hlen; /* 12 hardware address length (must be 6) */
uint8_t plen; /* 13 protocol address length (must be 4) */
uint16_t operation; /* 14 ARP opcode */
uint8_t sHaddr[6]; /* 16 sender's hardware address */
uint8_t sInaddr[4]; /* 1c sender's IP address */
uint8_t tHaddr[6]; /* 20 target's hardware address */
uint8_t tInaddr[4]; /* 26 target's IP address */
uint8_t pad[18]; /* 2a pad for min. ethernet payload (60 bytes) */
};
enum {
ARP_MSG_SIZE = 0x2a
};
static int safe_poll(struct pollfd *ufds, nfds_t nfds, int timeout)
{
while (1) {
int n = poll(ufds, nfds, timeout);
if (n >= 0)
return n;
/* Make sure we inch towards completion */
if (timeout > 0)
timeout--;
/* E.g. strace causes poll to return this */
if (errno == EINTR)
continue;
/* Kernel is very low on memory. Retry. */
/* I doubt many callers would handle this correctly! */
if (errno == ENOMEM)
continue;
log_warning("poll error: %s", strerror(errno));
return n;
}
}
static unsigned long long curms()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
}
/* Returns 1 if no reply received */
int arpping(uint32_t test_nip, const uint8_t *safe_mac, uint32_t from_ip,
uint8_t *from_mac, const char *interface)
{
int timeout_ms;
struct pollfd pfd[1];
int rv = 1; /* "no reply received" yet */
int opt = 1;
struct sockaddr addr; /* for interface name */
struct arpMsg arp;
pfd[0].fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
if (pfd[0].fd == -1) {
log_warning("arpping: failed to create socket: %s", strerror(errno));
return -1;
}
if (setsockopt(pfd[0].fd, SOL_SOCKET, SO_BROADCAST,
&opt, sizeof opt) == -1) {
log_warning("arpping: failed to set broadcast: %s", strerror(errno));
goto ret;
}
/* send arp request */
memset(&arp, 0, sizeof arp);
memset(arp.h_dest, 0xff, 6); /* MAC DA */
memcpy(arp.h_source, from_mac, 6); /* MAC SA */
arp.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */
arp.htype = htons(ARPHRD_ETHER); /* hardware type */
arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */
arp.hlen = 6; /* hardware address length */
arp.plen = 4; /* protocol address length */
arp.operation = htons(ARPOP_REQUEST); /* ARP op code */
memcpy(arp.sHaddr, from_mac, 6); /* source hardware address */
memcpy(arp.sInaddr, &from_ip, sizeof from_ip); /* source IP address */
/* tHaddr is zero-filled */ /* target hardware address */
memcpy(arp.tInaddr, &test_nip, sizeof test_nip);/* target IP address */
memset(&addr, 0, sizeof addr);
strlcpy(addr.sa_data, interface, sizeof addr.sa_data);
if (safe_sendto(pfd[0].fd, (const char *)&arp, sizeof arp,
0, &addr, sizeof addr) < 0) {
log_error("arpping: sendto failed: %s", strerror(errno));
goto ret;
}
/* wait for arp reply, and check it */
timeout_ms = 2000;
do {
typedef uint32_t aliased_uint32_t __attribute__((__may_alias__));
int r;
unsigned long long prevTime = curms();
pfd[0].events = POLLIN;
r = safe_poll(pfd, 1, timeout_ms);
if (r < 0)
break;
if (r) {
r = safe_read(pfd[0].fd, (char *)&arp, sizeof arp);
if (r < 0)
break;
//log3("sHaddr %02x:%02x:%02x:%02x:%02x:%02x",
//arp.sHaddr[0], arp.sHaddr[1], arp.sHaddr[2],
//arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]);
if (r >= ARP_MSG_SIZE
&& arp.operation == htons(ARPOP_REPLY)
/* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */
/* && memcmp(arp.tHaddr, from_mac, 6) == 0 */
&& *(aliased_uint32_t*)arp.sInaddr == test_nip
) {
/* if ARP source MAC matches safe_mac
* (which is client's MAC), then it's not a conflict
* (client simply already has this IP and replies to ARPs!)
*/
if (!safe_mac || memcmp(safe_mac, arp.sHaddr, 6) != 0)
rv = 0;
break;
}
}
timeout_ms -= (int)(curms() - prevTime);
} while (timeout_ms > 0);
ret:
close(pfd[0].fd);
log_line("%srp reply received for this address", rv ? "No a" : "A");
return rv;
}

8
ndhc/arpping.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef ARPPING_H_
#define ARPPING_H_
int arpping(uint32_t test_nip, const uint8_t *safe_mac, uint32_t from_ip,
uint8_t *from_mac, const char *interface);
#endif /* ARPPING_H_ */

View File

@ -175,6 +175,30 @@ int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
return bcast_raw_packet(&packet); return bcast_raw_packet(&packet);
} }
/* Broadcast a DHCP decline message */
int send_decline(uint32_t xid, uint32_t server, uint32_t requested)
{
struct dhcpMessage packet;
/* Fill in: op, htype, hlen, cookie, chaddr, random xid fields,
* client-id option (unless -C), message type option:
*/
init_packet(&packet, DHCPDECLINE);
/* RFC 2131 says DHCPDECLINE's xid is randomly selected by client,
* but in case the server is buggy and wants DHCPDECLINE's xid
* to match the xid which started entire handshake,
* we use the same xid we used in initial DHCPDISCOVER:
*/
packet.xid = xid;
/* DHCPDECLINE uses "requested ip", not ciaddr, to store offered IP */
add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
add_simple_option(packet.options, DHCP_SERVER_ID, server);
log_line("Sending decline...");
return bcast_raw_packet(&packet);
}
/* Unicasts a DHCP release message */ /* Unicasts a DHCP release message */
int send_release(uint32_t server, uint32_t ciaddr) int send_release(uint32_t server, uint32_t ciaddr)
{ {

View File

@ -8,6 +8,7 @@ int send_discover(uint32_t xid, uint32_t requested);
int send_selecting(uint32_t xid, uint32_t server, uint32_t requested); int send_selecting(uint32_t xid, uint32_t server, uint32_t requested);
int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr); int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr);
int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr); int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr);
int send_decline(uint32_t xid, uint32_t server, uint32_t requested);
int send_release(uint32_t server, uint32_t ciaddr); int send_release(uint32_t server, uint32_t ciaddr);
int get_raw_packet(struct dhcpMessage *payload, int fd); int get_raw_packet(struct dhcpMessage *payload, int fd);

View File

@ -47,6 +47,7 @@
#include "packet.h" #include "packet.h"
#include "script.h" #include "script.h"
#include "socket.h" #include "socket.h"
#include "arpping.h"
#include "log.h" #include "log.h"
#include "chroot.h" #include "chroot.h"
#include "cap.h" #include "cap.h"
@ -378,6 +379,21 @@ static void handle_packet(void)
lease = RETRY_DELAY; lease = RETRY_DELAY;
} }
if (!arpping(packet.yiaddr, NULL, 0, client_config.arp,
client_config.interface)) {
log_line("Offered address is in use: declining.");
send_decline(xid, server_addr, packet.yiaddr);
if (state != REQUESTING)
run_script(NULL, SCRIPT_DECONFIG);
state = INIT_SELECTING;
requested_ip = 0;
timeout = now;
packet_num = 0;
change_listen_mode(LISTEN_RAW);
break;
}
/* enter bound state */ /* enter bound state */
t1 = lease >> 1; t1 = lease >> 1;

View File

@ -61,24 +61,6 @@ uint16_t checksum(void *addr, int count)
return ~sum; return ~sum;
} }
/* returns -1 on error, >= 0 and equal to # chars written on success */
static int safe_sendto(int fd, const char *buf, int len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen)
{
int r, s = 0;
while (s < len) {
r = sendto(fd, buf + s, len - s, flags, dest_addr, addrlen);
if (r == -1) {
if (errno == EINTR)
continue;
else
return -1;
}
s += r;
}
return s;
}
/* Constuct a ip/udp header for a packet, and specify the source and dest /* Constuct a ip/udp header for a packet, and specify the source and dest
* hardware address */ * hardware address */
int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int raw_packet(struct dhcpMessage *payload, uint32_t source_ip,