Clean up the DHCP packet sending functions and make them more RFC-compliant.

Rename cs->requestedIP to cs->clientAddr.
Move the IFS_* defines into netlink.c.
Cosmetic cleanups.
This commit is contained in:
Nicholas J. Kain 2011-07-01 11:37:13 -04:00
parent a68c8cb64c
commit 38ad2399ed
9 changed files with 90 additions and 79 deletions

View File

@ -204,12 +204,12 @@ static void arp_failed(struct client_state_t *cs)
{
log_line("arp: Offered address is in use -- declining");
arp_close_fd(cs);
send_decline(cs->xid, cs->serverAddr, arp_dhcp_packet.yiaddr);
send_decline(cs, arp_dhcp_packet.yiaddr);
if (cs->arpPrevState != DS_REQUESTING)
ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_SELECTING;
cs->requestedIP = 0;
cs->clientAddr = 0;
cs->timeout = 0;
cs->packetNum = 0;
set_listen_raw(cs);
@ -224,7 +224,7 @@ void arp_gw_failed(struct client_state_t *cs)
cs->dhcpState = DS_SELECTING;
cs->oldTimeout = 0;
cs->timeout = 0;
cs->requestedIP = 0;
cs->clientAddr = 0;
cs->packetNum = 0;
set_listen_raw(cs);
}
@ -237,7 +237,7 @@ void arp_success(struct client_state_t *cs)
struct in_addr temp_addr = {.s_addr = arp_dhcp_packet.yiaddr};
log_line("arp: Lease of %s obtained, lease time %ld",
inet_ntoa(temp_addr), cs->lease);
cs->requestedIP = arp_dhcp_packet.yiaddr;
cs->clientAddr = arp_dhcp_packet.yiaddr;
cs->dhcpState = DS_BOUND;
cs->init = 0;
ifchange(&arp_dhcp_packet,

View File

@ -23,14 +23,6 @@
#include <stdint.h>
enum {
IFS_NONE = 0,
IFS_UP,
IFS_DOWN,
IFS_SHUT,
IFS_REMOVED
};
struct client_state_t {
unsigned long long leaseStartTime;
int dhcpState;
@ -40,7 +32,7 @@ struct client_state_t {
int packetNum;
int epollFd, signalFd, listenFd, arpFd, nlFd;
int timeout, oldTimeout;
uint32_t requestedIP, serverAddr, routerAddr;
uint32_t clientAddr, serverAddr, routerAddr;
uint32_t lease, renewTime, rebindTime, xid;
int using_dhcp_bpf;
uint8_t routerArp[6];

View File

@ -60,9 +60,6 @@
#define VERSION "1.0"
struct client_state_t cs = {
.dhcpState = DS_SELECTING,
.arpPrevState = DS_SELECTING,
.ifsPrevState = IFS_NONE,
.init = 1,
.epollFd = -1,
.signalFd = -1,
@ -75,8 +72,6 @@ struct client_state_t cs = {
struct client_config_t client_config = {
/* Default options. */
.interface = "eth0",
.clientid = NULL,
.hostname = NULL,
.arp = "\0\0\0\0\0\0",
};
@ -252,7 +247,7 @@ int main(int argc, char **argv)
client_config.quit_after_lease = 1;
break;
case 'r':
cs.requestedIP = inet_addr(optarg);
cs.clientAddr = inet_addr(optarg);
break;
case 'u':
pwd = getpwnam(optarg);

View File

@ -42,6 +42,14 @@
#define NLMSG_RECVSIZE 8192
enum {
IFS_NONE = 0,
IFS_UP,
IFS_DOWN,
IFS_SHUT,
IFS_REMOVED
};
static unsigned int nl_seq;
/* internal callback handling */
@ -115,7 +123,7 @@ static void takedown_if(struct client_state_t *cs)
ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_SELECTING;
cs->timeout = 0;
cs->requestedIP = 0;
cs->clientAddr = 0;
cs->packetNum = 0;
set_listen_raw(cs);
}

View File

@ -308,3 +308,14 @@ void add_option_request_list(struct dhcpmsg *packet)
reqdata[1] = j - 2;
add_option_string(packet, reqdata);
}
void add_option_vendor_string(struct dhcpmsg *packet)
{
struct vendor {
char vendor;
char length;
char str[sizeof "ndhc"];
} vendor_id = { DHCP_VENDOR, sizeof "ndhc" - 1, "ndhc"};
add_option_string(packet, (uint8_t *)&vendor_id);
}

View File

@ -92,5 +92,6 @@ ssize_t get_end_option_idx(struct dhcpmsg *packet);
size_t add_option_string(struct dhcpmsg *packet, uint8_t *optstr);
size_t add_u32_option(struct dhcpmsg *packet, uint8_t code, uint32_t data);
void add_option_request_list(struct dhcpmsg *packet);
void add_option_vendor_string(struct dhcpmsg *packet);
#endif

View File

@ -2,7 +2,6 @@
* Time-stamp: <2011-06-11 11:15:09 njk>
*
* (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
* (c) 2001 Russ Dill <Russ.Dill@asu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -32,7 +31,6 @@
#include <features.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <linux/filter.h>
#include <time.h>
#include <errno.h>
@ -353,7 +351,8 @@ static int create_raw_listen_socket(struct client_state_t *cs, int ifindex)
// Verify that the UDP client and server ports match that of the
// IANA-assigned DHCP ports.
BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (67 << 16) + 68, 1, 0),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,
(DHCP_SERVER_PORT << 16) + DHCP_CLIENT_PORT, 1, 0),
BPF_STMT(BPF_RET + BPF_K, 0),
// Get the UDP length field and store it in X.
BPF_STMT(BPF_LD + BPF_H + BPF_IND, 4),
@ -489,7 +488,7 @@ static int validate_dhcp_packet(struct client_state_t *cs, int len,
{
ssize_t optlen;
if (len < sizeof *packet - sizeof packet->options) {
log_line("Packet is too short to contain magic cookie. Ignoring.");
log_line("Packet is too short to contain magic cookie. Ignoring.");
return 0;
}
if (ntohl(packet->cookie) != DHCP_MAGIC) {
@ -502,7 +501,7 @@ static int validate_dhcp_packet(struct client_state_t *cs, int len,
return 0;
}
if (!(*msg = get_option_data(packet, DHCP_MESSAGE_TYPE, &optlen))) {
log_line("Packet does not specify a DHCP message type. Ignoring.");
log_line("Packet does not specify a DHCP message type. Ignoring.");
return 0;
}
return 1;
@ -550,73 +549,77 @@ static struct dhcpmsg init_packet(char type, uint32_t xid)
add_option_string(&packet, client_config.clientid);
if (client_config.hostname)
add_option_string(&packet, client_config.hostname);
struct vendor {
char vendor;
char length;
char str[sizeof "ndhc"];
} vendor_id = { DHCP_VENDOR, sizeof "ndhc" - 1, "ndhc"};
add_option_string(&packet, (uint8_t *)&vendor_id);
return packet;
}
// Broadcast a DHCP discover packet to the network, with an optionally
// requested IP
int send_discover(uint32_t xid, uint32_t requested)
int send_discover(struct client_state_t *cs)
{
struct dhcpmsg packet = init_packet(DHCPDISCOVER, xid);
if (requested)
add_u32_option(&packet, DHCP_REQUESTED_IP, requested);
// Request a RFC-specified max size to work around buggy servers.
add_u32_option(&packet, DHCP_MAX_SIZE, htons(576));
struct dhcpmsg packet = init_packet(DHCPDISCOVER, cs->xid);
if (cs->clientAddr)
add_u32_option(&packet, DHCP_REQUESTED_IP, cs->clientAddr);
add_u32_option(&packet, DHCP_MAX_SIZE,
htons(sizeof(struct ip_udp_dhcp_packet)));
add_option_request_list(&packet);
add_option_vendor_string(&packet);
log_line("Sending discover...");
return send_dhcp_raw(&packet);
}
// Broadcasts a DHCP request message
int send_selecting(uint32_t xid, uint32_t server, uint32_t requested)
int send_selecting(struct client_state_t *cs)
{
struct dhcpmsg packet = init_packet(DHCPREQUEST, xid);
add_u32_option(&packet, DHCP_REQUESTED_IP, requested);
add_u32_option(&packet, DHCP_SERVER_ID, server);
struct dhcpmsg packet = init_packet(DHCPREQUEST, cs->xid);
add_u32_option(&packet, DHCP_REQUESTED_IP, cs->clientAddr);
add_u32_option(&packet, DHCP_SERVER_ID, cs->serverAddr);
add_u32_option(&packet, DHCP_MAX_SIZE,
htons(sizeof(struct ip_udp_dhcp_packet)));
add_option_request_list(&packet);
add_option_vendor_string(&packet);
log_line("Sending select for %s...",
inet_ntoa((struct in_addr){.s_addr = requested}));
inet_ntoa((struct in_addr){.s_addr = cs->clientAddr}));
return send_dhcp_raw(&packet);
}
// Unicasts or broadcasts a DHCP renew message
int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
int send_renew(struct client_state_t *cs)
{
struct dhcpmsg packet = init_packet(DHCPREQUEST, xid);
packet.ciaddr = ciaddr;
struct dhcpmsg packet = init_packet(DHCPREQUEST, cs->xid);
packet.ciaddr = cs->clientAddr;
add_u32_option(&packet, DHCP_MAX_SIZE,
htons(sizeof(struct ip_udp_dhcp_packet)));
add_option_request_list(&packet);
add_option_vendor_string(&packet);
log_line("Sending renew...");
if (server)
return send_dhcp_cooked(&packet, ciaddr, server);
else
return send_dhcp_raw(&packet);
return send_dhcp_cooked(&packet, cs->clientAddr, cs->serverAddr);
}
// Broadcast a DHCP decline message
int send_decline(uint32_t xid, uint32_t server, uint32_t requested)
int send_rebind(struct client_state_t *cs)
{
struct dhcpmsg packet = init_packet(DHCPDECLINE, xid);
// DHCPDECLINE uses "requested ip", not ciaddr, to store offered IP
add_u32_option(&packet, DHCP_REQUESTED_IP, requested);
struct dhcpmsg packet = init_packet(DHCPREQUEST, cs->xid);
packet.ciaddr = cs->clientAddr;
add_u32_option(&packet, DHCP_REQUESTED_IP, cs->clientAddr);
add_u32_option(&packet, DHCP_MAX_SIZE,
htons(sizeof(struct ip_udp_dhcp_packet)));
add_option_request_list(&packet);
add_option_vendor_string(&packet);
log_line("Sending rebind...");
return send_dhcp_raw(&packet);
}
int send_decline(struct client_state_t *cs, uint32_t server)
{
struct dhcpmsg packet = init_packet(DHCPDECLINE, cs->xid);
add_u32_option(&packet, DHCP_REQUESTED_IP, cs->clientAddr);
add_u32_option(&packet, DHCP_SERVER_ID, server);
log_line("Sending decline...");
return send_dhcp_raw(&packet);
}
// Unicasts a DHCP release message
int send_release(uint32_t server, uint32_t ciaddr)
int send_release(struct client_state_t *cs)
{
struct dhcpmsg packet = init_packet(DHCPRELEASE, libc_random_u32());
packet.ciaddr = ciaddr;
add_u32_option(&packet, DHCP_REQUESTED_IP, ciaddr);
add_u32_option(&packet, DHCP_SERVER_ID, server);
packet.ciaddr = cs->clientAddr;
add_u32_option(&packet, DHCP_REQUESTED_IP, cs->clientAddr);
add_u32_option(&packet, DHCP_SERVER_ID, cs->serverAddr);
log_line("Sending release...");
return send_dhcp_cooked(&packet, ciaddr, server);
return send_dhcp_cooked(&packet, cs->clientAddr, cs->serverAddr);
}

View File

@ -79,10 +79,11 @@ void set_listen_raw(struct client_state_t *cs);
void set_listen_cooked(struct client_state_t *cs);
void set_listen_none(struct client_state_t *cs);
void handle_packet(struct client_state_t *cs);
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_decline(uint32_t xid, uint32_t server, uint32_t requested);
int send_release(uint32_t server, uint32_t ciaddr);
int send_discover(struct client_state_t *cs);
int send_selecting(struct client_state_t *cs);
int send_renew(struct client_state_t *cs);
int send_rebind(struct client_state_t *cs);
int send_decline(struct client_state_t *cs, uint32_t server);
int send_release(struct client_state_t *cs);
#endif

View File

@ -63,7 +63,7 @@ static int delay_timeout(int numpackets)
static void requesting_timeout(struct client_state_t *cs)
{
if (cs->packetNum < 5) {
send_selecting(cs->xid, cs->serverAddr, cs->requestedIP);
send_selecting(cs);
cs->timeout = delay_timeout(cs->packetNum);
cs->packetNum++;
} else {
@ -107,7 +107,7 @@ static void renewing_timeout(struct client_state_t *cs)
if (ct < rbt) {
long long wt = (rbt - ct) / 2;
if (wt >= 30000)
send_renew(cs->xid, cs->serverAddr, cs->requestedIP);
send_renew(cs);
else
wt = rbt - ct;
cs->timeout = wt;
@ -133,7 +133,7 @@ static void rebinding_timeout(struct client_state_t *cs)
if (ct < elt) {
long long wt = (elt - ct) / 2;
if (wt >= 30000)
send_renew(cs->xid, 0, cs->requestedIP);
send_rebind(cs);
else
wt = elt - ct;
cs->timeout = wt;
@ -176,7 +176,7 @@ static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet,
ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_SELECTING;
cs->timeout = 30000;
cs->requestedIP = 0;
cs->clientAddr = 0;
cs->packetNum = 0;
set_listen_raw(cs);
}
@ -188,7 +188,7 @@ static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet,
ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_SELECTING;
cs->timeout = 3000;
cs->requestedIP = 0;
cs->clientAddr = 0;
cs->packetNum = 0;
set_listen_raw(cs);
}
@ -203,7 +203,7 @@ static void selecting_packet(struct client_state_t *cs, struct dhcpmsg *packet,
if ((temp = get_option_data(packet, DHCP_SERVER_ID, &optlen))) {
memcpy(&cs->serverAddr, temp, 4);
cs->xid = packet->xid;
cs->requestedIP = packet->yiaddr;
cs->clientAddr = packet->yiaddr;
cs->dhcpState = DS_REQUESTING;
cs->timeout = 0;
cs->packetNum = 0;
@ -221,7 +221,7 @@ static void selecting_timeout(struct client_state_t *cs)
{
if (cs->packetNum == 0)
cs->xid = libc_random_u32();
send_discover(cs->xid, cs->requestedIP);
send_discover(cs);
cs->timeout = delay_timeout(cs->packetNum);
cs->packetNum++;
if (cs->init && cs->packetNum >= 2) {
@ -245,9 +245,9 @@ static void anfrelease(struct client_state_t *cs)
static void nfrelease(struct client_state_t *cs)
{
log_line("Unicasting a release of %s to %s.",
inet_ntoa((struct in_addr){.s_addr=cs->requestedIP}),
inet_ntoa((struct in_addr){.s_addr=cs->clientAddr}),
inet_ntoa((struct in_addr){.s_addr=cs->serverAddr}));
send_release(cs->serverAddr, cs->requestedIP);
send_release(cs);
ifchange(NULL, IFCHANGE_DECONFIG);
frelease(cs);
}
@ -270,7 +270,7 @@ static void frenew(struct client_state_t *cs)
arp_close_fd(cs);
cs->dhcpState = DS_RENEWING;
set_listen_cooked(cs);
send_renew(cs->xid, cs->serverAddr, cs->requestedIP);
send_renew(cs);
break;
case DS_ARP_CHECK:
// Cancel arp ping in progress and treat as previous state.