Make sure our assigned address is unique on our segment by using arp.
This commit is contained in:
parent
5eb3c18b01
commit
9dce1a0378
23
ncmlib/io.c
23
ncmlib/io.c
@ -1,5 +1,5 @@
|
||||
/* 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>
|
||||
* All rights reserved.
|
||||
@ -28,6 +28,8 @@
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* 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>
|
||||
* All rights reserved.
|
||||
@ -32,5 +32,7 @@
|
||||
|
||||
int safe_read(int fd, 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_ */
|
||||
|
@ -8,6 +8,7 @@ set(NDHC_SRCS
|
||||
packet.c
|
||||
script.c
|
||||
clientpacket.c
|
||||
arpping.c
|
||||
ndhc.c
|
||||
)
|
||||
|
||||
|
160
ndhc/arpping.c
Normal file
160
ndhc/arpping.c
Normal 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
8
ndhc/arpping.h
Normal 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_ */
|
@ -175,6 +175,30 @@ int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
|
||||
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 */
|
||||
int send_release(uint32_t server, uint32_t ciaddr)
|
||||
{
|
||||
|
@ -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_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 get_raw_packet(struct dhcpMessage *payload, int fd);
|
||||
|
||||
|
16
ndhc/ndhc.c
16
ndhc/ndhc.c
@ -47,6 +47,7 @@
|
||||
#include "packet.h"
|
||||
#include "script.h"
|
||||
#include "socket.h"
|
||||
#include "arpping.h"
|
||||
#include "log.h"
|
||||
#include "chroot.h"
|
||||
#include "cap.h"
|
||||
@ -378,6 +379,21 @@ static void handle_packet(void)
|
||||
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 */
|
||||
t1 = lease >> 1;
|
||||
|
||||
|
@ -61,24 +61,6 @@ uint16_t checksum(void *addr, int count)
|
||||
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
|
||||
* hardware address */
|
||||
int raw_packet(struct dhcpMessage *payload, uint32_t source_ip,
|
||||
|
Loading…
Reference in New Issue
Block a user