diff options
Diffstat (limited to 'networking/udhcp')
-rw-r--r-- | networking/udhcp/Config.in | 67 | ||||
-rw-r--r-- | networking/udhcp/Kbuild | 18 | ||||
-rw-r--r-- | networking/udhcp/arpping.c | 114 | ||||
-rw-r--r-- | networking/udhcp/clientpacket.c | 224 | ||||
-rw-r--r-- | networking/udhcp/clientsocket.c | 59 | ||||
-rw-r--r-- | networking/udhcp/common.c | 75 | ||||
-rw-r--r-- | networking/udhcp/common.h | 108 | ||||
-rw-r--r-- | networking/udhcp/dhcpc.c | 509 | ||||
-rw-r--r-- | networking/udhcp/dhcpc.h | 50 | ||||
-rw-r--r-- | networking/udhcp/dhcpd.c | 226 | ||||
-rw-r--r-- | networking/udhcp/dhcpd.h | 190 | ||||
-rw-r--r-- | networking/udhcp/dhcprelay.c | 340 | ||||
-rw-r--r-- | networking/udhcp/dumpleases.c | 74 | ||||
-rw-r--r-- | networking/udhcp/files.c | 395 | ||||
-rw-r--r-- | networking/udhcp/leases.c | 145 | ||||
-rw-r--r-- | networking/udhcp/options.c | 174 | ||||
-rw-r--r-- | networking/udhcp/options.h | 37 | ||||
-rw-r--r-- | networking/udhcp/packet.c | 211 | ||||
-rw-r--r-- | networking/udhcp/pidfile.c | 66 | ||||
-rw-r--r-- | networking/udhcp/script.c | 213 | ||||
-rw-r--r-- | networking/udhcp/serverpacket.c | 261 | ||||
-rw-r--r-- | networking/udhcp/signalpipe.c | 77 | ||||
-rw-r--r-- | networking/udhcp/socket.c | 130 | ||||
-rw-r--r-- | networking/udhcp/static_leases.c | 99 |
24 files changed, 3862 insertions, 0 deletions
diff --git a/networking/udhcp/Config.in b/networking/udhcp/Config.in new file mode 100644 index 0000000..f633473 --- /dev/null +++ b/networking/udhcp/Config.in @@ -0,0 +1,67 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +config APP_UDHCPD + bool "udhcp Server (udhcpd)" + default n + help + uDHCPd is a DHCP server geared primarily toward embedded systems, + while striving to be fully functional and RFC compliant. + + See http://udhcp.busybox.net for further details. + +config APP_DHCPRELAY + bool "dhcprelay" + default n + depends on APP_UDHCPD + help + dhcprelay listens for dhcp requests on one or more interfaces + and forwards these requests to a different interface or dhcp + server. + +config APP_DUMPLEASES + bool "Lease display utility (dumpleases)" + default n + depends on APP_UDHCPD + help + dumpleases displays the leases written out by the udhcpd server. + Lease times are stored in the file by time remaining in lease, or + by the absolute time that it expires in seconds from epoch. + + See http://udhcp.busybox.net for further details. + +config APP_UDHCPC + bool "udhcp Client (udhcpc)" + default n + help + uDHCPc is a DHCP client geared primarily toward embedded systems, + while striving to be fully functional and RFC compliant. + + The udhcp client negotiates a lease with the DHCP server and + notifies a set of scripts when a lease is obtained or lost. + + See http://udhcp.busybox.net for further details. + +config FEATURE_UDHCP_SYSLOG + bool "Log udhcp messages to syslog" + default n + depends on APP_UDHCPD || APP_UDHCPC + select FEATURE_SYSLOG + help + If not daemonized, udhcpd prints its messages to stdout/stderr. + If this option is selected, it will also log them to syslog. + + See http://udhcp.busybox.net for further details. + +config FEATURE_UDHCP_DEBUG + bool "Compile udhcp with noisy debugging messages" + default n + depends on APP_UDHCPD || APP_UDHCPC + help + If selected, udhcpd will output extra debugging output. If using + this option, compile uDHCP with "-g", and do not fork the daemon to + the background. + + See http://udhcp.busybox.net for further details. diff --git a/networking/udhcp/Kbuild b/networking/udhcp/Kbuild new file mode 100644 index 0000000..dc2c01f --- /dev/null +++ b/networking/udhcp/Kbuild @@ -0,0 +1,18 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> +# +# Licensed under the GPL v2 or later, see the file LICENSE in this tarball. +# + +lib-y:= +lib-$(CONFIG_APP_UDHCPC) += common.o options.o packet.o pidfile.o \ + signalpipe.o socket.o +lib-$(CONFIG_APP_UDHCPD) += common.o options.o packet.o pidfile.o \ + signalpipe.o socket.o +lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \ + script.o +lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \ + serverpacket.o static_leases.o +lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o +lib-$(CONFIG_APP_DHCPRELAY) += dhcprelay.o diff --git a/networking/udhcp/arpping.c b/networking/udhcp/arpping.c new file mode 100644 index 0000000..9c8b9c5 --- /dev/null +++ b/networking/udhcp/arpping.c @@ -0,0 +1,114 @@ +/* vi: set sw=4 ts=4: */ +/* + * arpping.c + * + * Mostly stolen from: dhcpcd - DHCP client daemon + * by Yoichi Hariguchi <yoichi@fore.com> + */ + +#include <netinet/if_ether.h> +#include <net/if_arp.h> + +#include "common.h" +#include "dhcpd.h" + + +struct arpMsg { + /* Ethernet header */ + u_char h_dest[6]; /* destination ether addr */ + u_char h_source[6]; /* source ether addr */ + u_short h_proto; /* packet type ID field */ + + /* ARP packet */ + uint16_t htype; /* hardware type (must be ARPHRD_ETHER) */ + uint16_t ptype; /* protocol type (must be ETH_P_IP) */ + uint8_t hlen; /* hardware address length (must be 6) */ + uint8_t plen; /* protocol address length (must be 4) */ + uint16_t operation; /* ARP opcode */ + uint8_t sHaddr[6]; /* sender's hardware address */ + uint8_t sInaddr[4]; /* sender's IP address */ + uint8_t tHaddr[6]; /* target's hardware address */ + uint8_t tInaddr[4]; /* target's IP address */ + uint8_t pad[18]; /* pad for min. Ethernet payload (60 bytes) */ +} ATTRIBUTE_PACKED; + +/* args: yiaddr - what IP to ping + * ip - our ip + * mac - our arp address + * interface - interface to use + * retn: 1 addr free + * 0 addr used + * -1 error + */ + +/* FIXME: match response against chaddr */ +int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *mac, char *interface) +{ + int timeout = 2; + int s; /* socket */ + int rv = 1; /* return value */ + struct sockaddr addr; /* for interface name */ + struct arpMsg arp; + fd_set fdset; + struct timeval tm; + time_t prevTime; + + + s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)); + if (s == -1) { + bb_perror_msg(bb_msg_can_not_create_raw_socket); + return -1; + } + + if (setsockopt_broadcast(s) == -1) { + bb_perror_msg("cannot setsocketopt on raw socket"); + close(s); + return -1; + } + + /* send arp request */ + memset(&arp, 0, sizeof(arp)); + memcpy(arp.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */ + memcpy(arp.h_source, 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.sInaddr, &ip, sizeof(ip)); /* source IP address */ + memcpy(arp.sHaddr, mac, 6); /* source hardware address */ + memcpy(arp.tInaddr, &yiaddr, sizeof(yiaddr)); /* target IP address */ + + memset(&addr, 0, sizeof(addr)); + strcpy(addr.sa_data, interface); + if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) + rv = 0; + + /* wait arp reply, and check it */ + tm.tv_usec = 0; + prevTime = uptime(); + while (timeout > 0) { + FD_ZERO(&fdset); + FD_SET(s, &fdset); + tm.tv_sec = timeout; + if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) { + bb_perror_msg("error on ARPING request"); + if (errno != EINTR) rv = 0; + } else if (FD_ISSET(s, &fdset)) { + if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0; + if (arp.operation == htons(ARPOP_REPLY) && + memcmp(arp.tHaddr, mac, 6) == 0 && + *((uint32_t *) arp.sInaddr) == yiaddr) { + DEBUG("Valid arp reply received for this address"); + rv = 0; + break; + } + } + timeout -= uptime() - prevTime; + prevTime = uptime(); + } + close(s); + DEBUG("%salid arp replies for this address", rv ? "No v" : "V"); + return rv; +} diff --git a/networking/udhcp/clientpacket.c b/networking/udhcp/clientpacket.c new file mode 100644 index 0000000..15cbda2 --- /dev/null +++ b/networking/udhcp/clientpacket.c @@ -0,0 +1,224 @@ +/* vi: set sw=4 ts=4: */ +/* clientpacket.c + * + * Packet generation and dispatching functions for the DHCP client. + * + * Russ Dill <Russ.Dill@asu.edu> July 2001 + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include <features.h> +#if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION +#include <netpacket/packet.h> +#include <net/ethernet.h> +#else +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#endif + +#include "common.h" +#include "dhcpd.h" +#include "dhcpc.h" +#include "options.h" + + +/* Create a random xid */ +unsigned long random_xid(void) +{ + static int initialized; + if (!initialized) { + unsigned long seed; + + if (open_read_close("/dev/urandom", &seed, sizeof(seed)) < 0) { + bb_info_msg("Cannot load seed " + "from /dev/urandom: %s", strerror(errno)); + seed = time(0); + } + srand(seed); + initialized++; + } + return rand(); +} + + +/* initialize a packet with the proper defaults */ +static void init_packet(struct dhcpMessage *packet, char type) +{ + udhcp_init_header(packet, type); + memcpy(packet->chaddr, client_config.arp, 6); + if (client_config.clientid) + add_option_string(packet->options, client_config.clientid); + if (client_config.hostname) add_option_string(packet->options, client_config.hostname); + if (client_config.fqdn) add_option_string(packet->options, client_config.fqdn); + add_option_string(packet->options, client_config.vendorclass); +} + + +/* Add a parameter request list for stubborn DHCP servers. Pull the data + * from the struct in options.c. Don't do bounds checking here because it + * goes towards the head of the packet. */ +static void add_requests(struct dhcpMessage *packet) +{ + int end = end_option(packet->options); + int i, len = 0; + + packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; + for (i = 0; dhcp_options[i].code; i++) + if (dhcp_options[i].flags & OPTION_REQ) + packet->options[end + OPT_DATA + len++] = dhcp_options[i].code; + packet->options[end + OPT_LEN] = len; + packet->options[end + OPT_DATA + len] = DHCP_END; + +} + + +/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ +int send_discover(unsigned long xid, unsigned long requested) +{ + struct dhcpMessage packet; + + init_packet(&packet, DHCPDISCOVER); + packet.xid = xid; + if (requested) + add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); + + add_requests(&packet); + bb_info_msg("Sending discover..."); + return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, + SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); +} + + +/* Broadcasts a DHCP request message */ +int send_selecting(unsigned long xid, unsigned long server, unsigned long requested) +{ + struct dhcpMessage packet; + struct in_addr addr; + + init_packet(&packet, DHCPREQUEST); + packet.xid = xid; + + add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); + add_simple_option(packet.options, DHCP_SERVER_ID, server); + + add_requests(&packet); + addr.s_addr = requested; + bb_info_msg("Sending select for %s...", inet_ntoa(addr)); + return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, + SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); +} + + +/* Unicasts or broadcasts a DHCP renew message */ +int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr) +{ + struct dhcpMessage packet; + int ret = 0; + + init_packet(&packet, DHCPREQUEST); + packet.xid = xid; + packet.ciaddr = ciaddr; + + add_requests(&packet); + bb_info_msg("Sending renew..."); + if (server) + ret = udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); + else ret = udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, + SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); + return ret; +} + + +/* Unicasts a DHCP release message */ +int send_release(unsigned long server, unsigned long ciaddr) +{ + struct dhcpMessage packet; + + init_packet(&packet, DHCPRELEASE); + packet.xid = random_xid(); + packet.ciaddr = ciaddr; + + add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr); + add_simple_option(packet.options, DHCP_SERVER_ID, server); + + bb_info_msg("Sending release..."); + return udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); +} + + +/* return -1 on errors that are fatal for the socket, -2 for those that aren't */ +int get_raw_packet(struct dhcpMessage *payload, int fd) +{ + int bytes; + struct udp_dhcp_packet packet; + uint32_t source, dest; + uint16_t check; + + memset(&packet, 0, sizeof(struct udp_dhcp_packet)); + bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet)); + if (bytes < 0) { + DEBUG("Cannot read on raw listening socket - ignoring"); + usleep(500000); /* possible down interface, looping condition */ + return -1; + } + + if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) { + DEBUG("Message too short, ignoring"); + return -2; + } + + if (bytes < ntohs(packet.ip.tot_len)) { + DEBUG("Truncated packet"); + return -2; + } + + /* ignore any extra garbage bytes */ + bytes = ntohs(packet.ip.tot_len); + + /* Make sure its the right packet for us, and that it passes sanity checks */ + if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION + || packet.ip.ihl != sizeof(packet.ip) >> 2 + || packet.udp.dest != htons(CLIENT_PORT) + || bytes > (int) sizeof(struct udp_dhcp_packet) + || ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip)) + ) { + DEBUG("Unrelated/bogus packet"); + return -2; + } + + /* check IP checksum */ + check = packet.ip.check; + packet.ip.check = 0; + if (check != udhcp_checksum(&(packet.ip), sizeof(packet.ip))) { + DEBUG("bad IP header checksum, ignoring"); + return -1; + } + + /* verify the UDP checksum by replacing the header with a psuedo header */ + source = packet.ip.saddr; + dest = packet.ip.daddr; + check = packet.udp.check; + packet.udp.check = 0; + memset(&packet.ip, 0, sizeof(packet.ip)); + + packet.ip.protocol = IPPROTO_UDP; + packet.ip.saddr = source; + packet.ip.daddr = dest; + packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */ + if (check && check != udhcp_checksum(&packet, bytes)) { + bb_error_msg("packet with bad UDP checksum received, ignoring"); + return -2; + } + + memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp))); + + if (ntohl(payload->cookie) != DHCP_MAGIC) { + bb_error_msg("received bogus message (bad magic) - ignoring"); + return -2; + } + DEBUG("oooooh!!! got some!"); + return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); + +} diff --git a/networking/udhcp/clientsocket.c b/networking/udhcp/clientsocket.c new file mode 100644 index 0000000..8520619 --- /dev/null +++ b/networking/udhcp/clientsocket.c @@ -0,0 +1,59 @@ +/* vi: set sw=4 ts=4: */ +/* + * clientsocket.c -- DHCP client socket creation + * + * udhcp client + * + * Russ Dill <Russ.Dill@asu.edu> July 2001 + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <features.h> +#if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION) +#include <netpacket/packet.h> +#include <net/ethernet.h> +#else +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#endif + +#include "common.h" + + +int raw_socket(int ifindex) +{ + int fd; + struct sockaddr_ll sock; + + DEBUG("Opening raw socket on ifindex %d", ifindex); + fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + if (fd < 0) { + bb_perror_msg("socket"); + return -1; + } + + sock.sll_family = AF_PACKET; + sock.sll_protocol = htons(ETH_P_IP); + sock.sll_ifindex = ifindex; + if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) { + bb_perror_msg("bind"); + close(fd); + return -1; + } + + return fd; +} diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c new file mode 100644 index 0000000..3e916f4 --- /dev/null +++ b/networking/udhcp/common.c @@ -0,0 +1,75 @@ +/* vi: set sw=4 ts=4: */ +/* common.c + * + * Functions for debugging and logging as well as some other + * simple helper functions. + * + * Russ Dill <Russ.Dill@asu.edu> 2001-2003 + * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003 + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include <syslog.h> + +#include "common.h" + + +long uptime(void) +{ + struct sysinfo info; + sysinfo(&info); + return info.uptime; +} + +/* + * This function makes sure our first socket calls + * aren't going to fd 1 (printf badness...) and are + * not later closed by daemon() + */ +static inline void sanitize_fds(void) +{ + int fd = xopen(bb_dev_null, O_RDWR); + while (fd < 3) + fd = dup(fd); + close(fd); +} + + +void udhcp_background(const char *pidfile) +{ +#ifdef __uClinux__ + bb_error_msg("cannot background in uclinux (yet)"); +#else /* __uClinux__ */ + int pid_fd; + + /* hold lock during fork. */ + pid_fd = pidfile_acquire(pidfile); + setsid(); + xdaemon(0, 0); + logmode &= ~LOGMODE_STDIO; + pidfile_write_release(pid_fd); +#endif /* __uClinux__ */ +} + +void udhcp_start_log_and_pid(const char *pidfile) +{ + int pid_fd; + + /* Make sure our syslog fd isn't overwritten */ + sanitize_fds(); + + /* do some other misc startup stuff while we are here to save bytes */ + pid_fd = pidfile_acquire(pidfile); + pidfile_write_release(pid_fd); + + /* equivelent of doing a fflush after every \n */ + setlinebuf(stdout); + + if (ENABLE_FEATURE_UDHCP_SYSLOG) { + openlog(applet_name, LOG_PID, LOG_LOCAL0); + logmode |= LOGMODE_SYSLOG; + } + + bb_info_msg("%s (v%s) started", applet_name, BB_VER); +} diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h new file mode 100644 index 0000000..70a7693 --- /dev/null +++ b/networking/udhcp/common.h @@ -0,0 +1,108 @@ +/* vi: set sw=4 ts=4: */ +/* common.h + * + * Russ Dill <Russ.Dill@asu.edu> September 2001 + * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003 + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#ifndef _COMMON_H +#define _COMMON_H + +#include "busybox.h" + +#ifdef CONFIG_INSTALL_NO_USR +# define DEFAULT_SCRIPT "/share/udhcpc/default.script" +#else +# define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script" +#endif + +#define COMBINED_BINARY + + +/*** packet.h ***/ + +#include <netinet/udp.h> +#include <netinet/ip.h> + +struct dhcpMessage { + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint32_t ciaddr; + uint32_t yiaddr; + uint32_t siaddr; + uint32_t giaddr; + uint8_t chaddr[16]; + uint8_t sname[64]; + uint8_t file[128]; + uint32_t cookie; + uint8_t options[308]; /* 312 - cookie */ +}; + +struct udp_dhcp_packet { + struct iphdr ip; + struct udphdr udp; + struct dhcpMessage data; +}; + +void udhcp_init_header(struct dhcpMessage *packet, char type); +int udhcp_get_packet(struct dhcpMessage *packet, int fd); +uint16_t udhcp_checksum(void *addr, int count); +int udhcp_raw_packet(struct dhcpMessage *payload, + uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex); +int udhcp_kernel_packet(struct dhcpMessage *payload, + uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port); + + +/**/ + +void udhcp_background(const char *pidfile); +void udhcp_start_log_and_pid(const char *pidfile); + +void udhcp_run_script(struct dhcpMessage *packet, const char *name); + +// Still need to clean these up... + +/* from pidfile.h */ +#define pidfile_acquire udhcp_pidfile_acquire +#define pidfile_write_release udhcp_pidfile_write_release +/* from options.h */ +#define get_option udhcp_get_option +#define end_option udhcp_end_option +#define add_option_string udhcp_add_option_string +#define add_simple_option udhcp_add_simple_option +#define option_lengths udhcp_option_lengths +/* from socket.h */ +#define listen_socket udhcp_listen_socket +#define read_interface udhcp_read_interface +/* from dhcpc.h */ +#define client_config udhcp_client_config +/* from dhcpd.h */ +#define server_config udhcp_server_config + +long uptime(void); +void udhcp_sp_setup(void); +int udhcp_sp_fd_set(fd_set *rfds, int extra_fd); +int udhcp_sp_read(fd_set *rfds); +int raw_socket(int ifindex); +int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp); +int listen_socket(uint32_t ip, int port, char *inf); +int pidfile_acquire(const char *pidfile); +void pidfile_write_release(int pid_fd); +int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *arp, char *interface); + +#if ENABLE_FEATURE_UDHCP_DEBUG +# define DEBUG(str, args...) bb_info_msg(str, ## args) +#else +# define DEBUG(str, args...) do {;} while (0) +#endif + +#endif diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c new file mode 100644 index 0000000..71315ff --- /dev/null +++ b/networking/udhcp/dhcpc.c @@ -0,0 +1,509 @@ +/* vi: set sw=4 ts=4: */ +/* dhcpc.c + * + * udhcp DHCP client + * + * Russ Dill <Russ.Dill@asu.edu> July 2001 + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include <getopt.h> + +#include "common.h" +#include "dhcpd.h" +#include "dhcpc.h" +#include "options.h" + + +static int state; +/* Something is definitely wrong here. IPv4 addresses + * in variables of type long?? BTW, we use inet_ntoa() + * in the code. Manpage says that struct in_addr has a member of type long (!) + * which holds IPv4 address, and the struct is passed by value (!!) + */ +static unsigned long requested_ip; /* = 0 */ +static unsigned long server_addr; +static unsigned long timeout; +static int packet_num; /* = 0 */ +static int fd = -1; + +#define LISTEN_NONE 0 +#define LISTEN_KERNEL 1 +#define LISTEN_RAW 2 +static int listen_mode; + +struct client_config_t client_config; + + +/* just a little helper */ +static void change_mode(int new_mode) +{ + DEBUG("entering %s listen mode", + new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none"); + if (fd >= 0) close(fd); + fd = -1; + listen_mode = new_mode; +} + + +/* perform a renew */ +static void perform_renew(void) +{ + bb_info_msg("Performing a DHCP renew"); + switch (state) { + case BOUND: + change_mode(LISTEN_KERNEL); + case RENEWING: + case REBINDING: + state = RENEW_REQUESTED; + break; + case RENEW_REQUESTED: /* impatient are we? fine, square 1 */ + udhcp_run_script(NULL, "deconfig"); + case REQUESTING: + case RELEASED: + change_mode(LISTEN_RAW); + state = INIT_SELECTING; + break; + case INIT_SELECTING: + break; + } + + /* start things over */ + packet_num = 0; + + /* Kill any timeouts because the user wants this to hurry along */ + timeout = 0; +} + + +/* perform a release */ +static void perform_release(void) +{ + char buffer[16]; + struct in_addr temp_addr; + + /* send release packet */ + if (state == BOUND || state == RENEWING || state == REBINDING) { + temp_addr.s_addr = server_addr; + sprintf(buffer, "%s", inet_ntoa(temp_addr)); + temp_addr.s_addr = requested_ip; + bb_info_msg("Unicasting a release of %s to %s", + inet_ntoa(temp_addr), buffer); + send_release(server_addr, requested_ip); /* unicast */ + udhcp_run_script(NULL, "deconfig"); + } + bb_info_msg("Entering released state"); + + change_mode(LISTEN_NONE); + state = RELEASED; + timeout = 0x7fffffff; +} + + +static void client_background(void) +{ + udhcp_background(client_config.pidfile); + client_config.foreground = 1; /* Do not fork again. */ + client_config.background_if_no_lease = 0; +} + + +static uint8_t* alloc_dhcp_option(int code, const char *str, int extra) +{ + uint8_t *storage; + int len = strlen(str); + if (len > 255) len = 255; + storage = xzalloc(len + extra + OPT_DATA); + storage[OPT_CODE] = code; + storage[OPT_LEN] = len + extra; + memcpy(storage + extra + OPT_DATA, str, len); + return storage; +} + + +int udhcpc_main(int argc, char *argv[]) +{ + uint8_t *temp, *message; + char *str_c, *str_V, *str_h, *str_F, *str_r, *str_T, *str_t; + unsigned long t1 = 0, t2 = 0, xid = 0; + unsigned long start = 0, lease = 0; + long now; + unsigned opt; + int max_fd; + int sig; + int retval; + int len; + int no_clientid = 0; + fd_set rfds; + struct timeval tv; + struct dhcpMessage packet; + struct in_addr temp_addr; + + enum { + OPT_c = 1 << 0, + OPT_C = 1 << 1, + OPT_V = 1 << 2, + OPT_f = 1 << 3, + OPT_b = 1 << 4, + OPT_H = 1 << 5, + OPT_h = 1 << 6, + OPT_F = 1 << 7, + OPT_i = 1 << 8, + OPT_n = 1 << 9, + OPT_p = 1 << 10, + OPT_q = 1 << 11, + OPT_R = 1 << 12, + OPT_r = 1 << 13, + OPT_s = 1 << 14, + OPT_T = 1 << 15, + OPT_t = 1 << 16, + OPT_v = 1 << 17, + }; +#if ENABLE_GETOPT_LONG + static const struct option arg_options[] = { + { "clientid", required_argument, 0, 'c' }, + { "clientid-none", no_argument, 0, 'C' }, + { "vendorclass", required_argument, 0, 'V' }, + { "foreground", no_argument, 0, 'f' }, + { "background", no_argument, 0, 'b' }, + { "hostname", required_argument, 0, 'H' }, + { "hostname", required_argument, 0, 'h' }, + { "fqdn", required_argument, 0, 'F' }, + { "interface", required_argument, 0, 'i' }, + { "now", no_argument, 0, 'n' }, + { "pidfile", required_argument, 0, 'p' }, + { "quit", no_argument, 0, 'q' }, + { "release", no_argument, 0, 'R' }, + { "request", required_argument, 0, 'r' }, + { "script", required_argument, 0, 's' }, + { "timeout", required_argument, 0, 'T' }, + { "version", no_argument, 0, 'v' }, + { "retries", required_argument, 0, 't' }, + { 0, 0, 0, 0 } + }; +#endif + /* Default options. */ + client_config.interface = "eth0"; + client_config.script = DEFAULT_SCRIPT; + client_config.retries = 3; + client_config.timeout = 3; + + /* Parse command line */ + opt_complementary = "?:c--C:C--c" // mutually exclusive + ":hH:Hh"; // -h and -H are the same +#if ENABLE_GETOPT_LONG + applet_long_options = arg_options; +#endif + opt = getopt32(argc, argv, "c:CV:fbH:h:F:i:np:qRr:s:T:t:v", + &str_c, &str_V, &str_h, &str_h, &str_F, + &client_config.interface, &client_config.pidfile, &str_r, + &client_config.script, &str_T, &str_t + ); + + if (opt & OPT_c) + client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, str_c, 0); + if (opt & OPT_C) + no_clientid = 1; + if (opt & OPT_V) + client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0); + if (opt & OPT_f) + client_config.foreground = 1; + if (opt & OPT_b) + client_config.background_if_no_lease = 1; + if (opt & OPT_h) + client_config.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0); + if (opt & OPT_F) { + client_config.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3); + /* Flags: 0000NEOS + S: 1 => Client requests Server to update A RR in DNS as well as PTR + O: 1 => Server indicates to client that DNS has been updated regardless + E: 1 => Name data is DNS format, i.e. <4>host<6>domain<4>com<0> not "host.domain.com" + N: 1 => Client requests Server to not update DNS + */ + client_config.fqdn[OPT_DATA + 0] = 0x1; + /* client_config.fqdn[OPT_DATA + 1] = 0; - redundant */ + /* client_config.fqdn[OPT_DATA + 2] = 0; - redundant */ + } + // if (opt & OPT_i) client_config.interface = ... + if (opt & OPT_n) + client_config.abort_if_no_lease = 1; + // if (opt & OPT_p) client_config.pidfile = ... + if (opt & OPT_q) + client_config.quit_after_lease = 1; + if (opt & OPT_R) + client_config.release_on_quit = 1; + if (opt & OPT_r) + requested_ip = inet_addr(str_r); + // if (opt & OPT_s) client_config.script = ... + if (opt & OPT_T) + client_config.timeout = xatoi_u(str_T); + if (opt & OPT_t) + client_config.retries = xatoi_u(str_t); + if (opt & OPT_v) { + printf("version %s\n\n", BB_VER); + return 0; + } + + /* Start the log, sanitize fd's, and write a pid file */ + udhcp_start_log_and_pid(client_config.pidfile); + + if (read_interface(client_config.interface, &client_config.ifindex, + NULL, client_config.arp) < 0) + return 1; + + /* if not set, and not suppressed, setup the default client ID */ + if (!client_config.clientid && !no_clientid) { + client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7); + client_config.clientid[OPT_DATA] = 1; + memcpy(client_config.clientid + OPT_DATA+1, client_config.arp, 6); + } + + if (!client_config.vendorclass) + client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, "udhcp "BB_VER, 0); + + /* setup the signal pipe */ + udhcp_sp_setup(); + + state = INIT_SELECTING; + udhcp_run_script(NULL, "deconfig"); + change_mode(LISTEN_RAW); + + for (;;) { + tv.tv_sec = timeout - uptime(); + tv.tv_usec = 0; + + if (listen_mode != LISTEN_NONE && fd < 0) { + if (listen_mode == LISTEN_KERNEL) + fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface); + else + fd = raw_socket(client_config.ifindex); + if (fd < 0) { + bb_perror_msg("FATAL: cannot listen on socket"); + return 0; + } + } + max_fd = udhcp_sp_fd_set(&rfds, fd); + + if (tv.tv_sec > 0) { + DEBUG("Waiting on select..."); + retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); + } else retval = 0; /* If we already timed out, fall through */ + + now = uptime(); + if (retval == 0) { + /* timeout dropped to zero */ + switch (state) { + case INIT_SELECTING: + if (packet_num < client_config.retries) { + if (packet_num == 0) + xid = random_xid(); + + /* send discover packet */ + send_discover(xid, requested_ip); /* broadcast */ + + timeout = now + client_config.timeout; + packet_num++; + } else { + udhcp_run_script(NULL, "leasefail"); + if (client_config.background_if_no_lease) { + bb_info_msg("No lease, forking to background"); + client_background(); + } else if (client_config.abort_if_no_lease) { + bb_info_msg("No lease, failing"); + return 1; + } + /* wait to try again */ + packet_num = 0; + timeout = now + 60; + } + break; + case RENEW_REQUESTED: + case REQUESTING: + if (packet_num < client_config.retries) { + /* send request packet */ + if (state == RENEW_REQUESTED) + send_renew(xid, server_addr, requested_ip); /* unicast */ + else send_selecting(xid, server_addr, requested_ip); /* broadcast */ + + timeout = now + ((packet_num == 2) ? 10 : 2); + packet_num++; + } else { + /* timed out, go back to init state */ + if (state == RENEW_REQUESTED) udhcp_run_script(NULL, "deconfig"); + state = INIT_SELECTING; + timeout = now; + packet_num = 0; + change_mode(LISTEN_RAW); + } + break; + case BOUND: + /* Lease is starting to run out, time to enter renewing state */ + state = RENEWING; + change_mode(LISTEN_KERNEL); + DEBUG("Entering renew state"); + /* fall right through */ + case RENEWING: + /* Either set a new T1, or enter REBINDING state */ + if ((t2 - t1) <= (lease / 14400 + 1)) { + /* timed out, enter rebinding state */ + state = REBINDING; + timeout = now + (t2 - t1); + DEBUG("Entering rebinding state"); + } else { + /* send a request packet */ + send_renew(xid, server_addr, requested_ip); /* unicast */ + + t1 = (t2 - t1) / 2 + t1; + timeout = t1 + start; + } + break; + case REBINDING: + /* Either set a new T2, or enter INIT state */ + if ((lease - t2) <= (lease / 14400 + 1)) { + /* timed out, enter init state */ + state = INIT_SELECTING; + bb_info_msg("Lease lost, entering init state"); + udhcp_run_script(NULL, "deconfig"); + timeout = now; + packet_num = 0; + change_mode(LISTEN_RAW); + } else { + /* send a request packet */ + send_renew(xid, 0, requested_ip); /* broadcast */ + + t2 = (lease - t2) / 2 + t2; + timeout = t2 + start; + } + break; + case RELEASED: + /* yah, I know, *you* say it would never happen */ + timeout = 0x7fffffff; + break; + } + } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) { + /* a packet is ready, read it */ + + if (listen_mode == LISTEN_KERNEL) + len = udhcp_get_packet(&packet, fd); + else len = get_raw_packet(&packet, fd); + + if (len == -1 && errno != EINTR) { + DEBUG("error on read, %s, reopening socket", strerror(errno)); + change_mode(listen_mode); /* just close and reopen */ + } + if (len < 0) continue; + + if (packet.xid != xid) { + DEBUG("Ignoring XID %lx (our xid is %lx)", + (unsigned long) packet.xid, xid); + continue; + } + + /* Ignore packets that aren't for us */ + if (memcmp(packet.chaddr, client_config.arp, 6)) { + DEBUG("Packet does not have our chaddr - ignoring"); + continue; + } + + if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { + bb_error_msg("cannot get option from packet - ignoring"); + continue; + } + + switch (state) { + case INIT_SELECTING: + /* Must be a DHCPOFFER to one of our xid's */ + if (*message == DHCPOFFER) { + temp = get_option(&packet, DHCP_SERVER_ID); + if (temp) { + server_addr = *(uint32_t*)temp; + xid = packet.xid; + requested_ip = packet.yiaddr; + + /* enter requesting state */ + state = REQUESTING; + timeout = now; + packet_num = 0; + } else { + bb_error_msg("no server ID in message"); + } + } + break; + case RENEW_REQUESTED: + case REQUESTING: + case RENEWING: + case REBINDING: + if (*message == DHCPACK) { + temp = get_option(&packet, DHCP_LEASE_TIME); + if (!temp) { + bb_error_msg("no lease time with ACK, using 1 hour lease"); + lease = 60 * 60; + } else { + lease = ntohl(*(uint32_t*)temp); + } + + /* enter bound state */ + t1 = lease / 2; + + /* little fixed point for n * .875 */ + t2 = (lease * 0x7) >> 3; + temp_addr.s_addr = packet.yiaddr; + bb_info_msg("Lease of %s obtained, lease time %ld", + inet_ntoa(temp_addr), lease); + start = now; + timeout = t1 + start; + requested_ip = packet.yiaddr; + udhcp_run_script(&packet, + ((state == RENEWING || state == REBINDING) ? "renew" : "bound")); + + state = BOUND; + change_mode(LISTEN_NONE); + if (client_config.quit_after_lease) { + if (client_config.release_on_quit) + perform_release(); + return 0; + } + if (!client_config.foreground) + client_background(); + + } else if (*message == DHCPNAK) { + /* return to init state */ + bb_info_msg("Received DHCP NAK"); + udhcp_run_script(&packet, "nak"); + if (state != REQUESTING) + udhcp_run_script(NULL, "deconfig"); + state = INIT_SELECTING; + timeout = now; + requested_ip = 0; + packet_num = 0; + change_mode(LISTEN_RAW); + sleep(3); /* avoid excessive network traffic */ + } + break; + /* case BOUND, RELEASED: - ignore all packets */ + } + } else if (retval > 0 && (sig = udhcp_sp_read(&rfds))) { + switch (sig) { + case SIGUSR1: + perform_renew(); + break; + case SIGUSR2: + perform_release(); + break; + case SIGTERM: + bb_info_msg("Received SIGTERM"); + if (client_config.release_on_quit) + perform_release(); + return 0; + } + } else if (retval == -1 && errno == EINTR) { + /* a signal was caught */ + } else { + /* An error occured */ + bb_perror_msg("select"); + } + + } + return 0; +} diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h new file mode 100644 index 0000000..fd17917 --- /dev/null +++ b/networking/udhcp/dhcpc.h @@ -0,0 +1,50 @@ +/* vi: set sw=4 ts=4: */ +/* dhcpc.h */ +#ifndef _DHCPC_H +#define _DHCPC_H + +#define INIT_SELECTING 0 +#define REQUESTING 1 +#define BOUND 2 +#define RENEWING 3 +#define REBINDING 4 +#define INIT_REBOOT 5 +#define RENEW_REQUESTED 6 +#define RELEASED 7 + +struct client_config_t { + /* TODO: combine flag fields into single "unsigned opt" */ + /* (can be set directly to the result of getopt32) */ + char foreground; /* Do not fork */ + char quit_after_lease; /* Quit after obtaining lease */ + char release_on_quit; /* perform release on quit */ + char abort_if_no_lease; /* Abort if no lease */ + char background_if_no_lease; /* Fork to background if no lease */ + char *interface; /* The name of the interface to use */ + char *pidfile; /* Optionally store the process ID */ + char *script; /* User script to run at dhcp events */ + uint8_t *clientid; /* Optional client id to use */ + uint8_t *vendorclass; /* Optional vendor class-id to use */ + uint8_t *hostname; /* Optional hostname to use */ + uint8_t *fqdn; /* Optional fully qualified domain name to use */ + int ifindex; /* Index number of the interface to use */ + int retries; /* Max number of request packets */ + int timeout; /* Number of seconds to try to get a lease */ + uint8_t arp[6]; /* Our arp address */ +}; + +extern struct client_config_t client_config; + + +/*** clientpacket.h ***/ + +unsigned long random_xid(void); +int send_discover(unsigned long xid, unsigned long requested); +int send_selecting(unsigned long xid, unsigned long server, unsigned long requested); +int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); +int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); +int send_release(unsigned long server, unsigned long ciaddr); +int get_raw_packet(struct dhcpMessage *payload, int fd); + + +#endif diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c new file mode 100644 index 0000000..7438036 --- /dev/null +++ b/networking/udhcp/dhcpd.c @@ -0,0 +1,226 @@ +/* vi: set sw=4 ts=4: */ +/* dhcpd.c + * + * udhcp Server + * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au> + * Chris Trew <ctrew@moreton.com.au> + * + * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "common.h" +#include "dhcpd.h" +#include "options.h" + + +/* globals */ +struct dhcpOfferedAddr *leases; +struct server_config_t server_config; + + +int udhcpd_main(int argc, char *argv[]) +{ + fd_set rfds; + struct timeval tv; + int server_socket = -1, bytes, retval, max_sock; + struct dhcpMessage packet; + uint8_t *state, *server_id, *requested; + uint32_t server_id_align, requested_align, static_lease_ip; + unsigned long timeout_end, num_ips; + struct option_set *option; + struct dhcpOfferedAddr *lease, static_lease; + + read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]); + + /* Start the log, sanitize fd's, and write a pid file */ + udhcp_start_log_and_pid(server_config.pidfile); + + if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) { + memcpy(&server_config.lease, option->data + 2, 4); + server_config.lease = ntohl(server_config.lease); + } + else server_config.lease = LEASE_TIME; + + /* Sanity check */ + num_ips = ntohl(server_config.end) - ntohl(server_config.start) + 1; + if (server_config.max_leases > num_ips) { + bb_error_msg("max_leases value (%lu) not sane, " + "setting to %lu instead", + server_config.max_leases, num_ips); + server_config.max_leases = num_ips; + } + + leases = xzalloc(server_config.max_leases * sizeof(struct dhcpOfferedAddr)); + read_leases(server_config.lease_file); + + if (read_interface(server_config.interface, &server_config.ifindex, + &server_config.server, server_config.arp) < 0) + return 1; + + if (!ENABLE_FEATURE_UDHCP_DEBUG) + udhcp_background(server_config.pidfile); /* hold lock during fork. */ + + /* Setup the signal pipe */ + udhcp_sp_setup(); + + timeout_end = time(0) + server_config.auto_time; + while (1) { /* loop until universe collapses */ + + if (server_socket < 0) { + server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface); + if (server_socket < 0) { + bb_perror_msg("FATAL: cannot create server socket"); + return 2; + } + } + + max_sock = udhcp_sp_fd_set(&rfds, server_socket); + if (server_config.auto_time) { + tv.tv_sec = timeout_end - time(0); + tv.tv_usec = 0; + } + if (!server_config.auto_time || tv.tv_sec > 0) { + retval = select(max_sock + 1, &rfds, NULL, NULL, + server_config.auto_time ? &tv : NULL); + } else retval = 0; /* If we already timed out, fall through */ + + if (retval == 0) { + write_leases(); + timeout_end = time(0) + server_config.auto_time; + continue; + } else if (retval < 0 && errno != EINTR) { + DEBUG("error on select"); + continue; + } + + switch (udhcp_sp_read(&rfds)) { + case SIGUSR1: + bb_info_msg("Received a SIGUSR1"); + write_leases(); + /* why not just reset the timeout, eh */ + timeout_end = time(0) + server_config.auto_time; + continue; + case SIGTERM: + bb_info_msg("Received a SIGTERM"); + return 0; + case 0: break; /* no signal */ + default: continue; /* signal or error (probably EINTR) */ + } + + if ((bytes = udhcp_get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */ + if (bytes == -1 && errno != EINTR) { + DEBUG("error on read, %s, reopening socket", strerror(errno)); + close(server_socket); + server_socket = -1; + } + continue; + } + + if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { + bb_error_msg("cannot get option from packet, ignoring"); + continue; + } + + /* Look for a static lease */ + static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr); + + if (static_lease_ip) { + bb_info_msg("Found static lease: %x", static_lease_ip); + + memcpy(&static_lease.chaddr, &packet.chaddr, 16); + static_lease.yiaddr = static_lease_ip; + static_lease.expires = 0; + + lease = &static_lease; + + } else { + lease = find_lease_by_chaddr(packet.chaddr); + } + + switch (state[0]) { + case DHCPDISCOVER: + DEBUG("Received DISCOVER"); + + if (sendOffer(&packet) < 0) { + bb_error_msg("send OFFER failed"); + } + break; + case DHCPREQUEST: + DEBUG("received REQUEST"); + + requested = get_option(&packet, DHCP_REQUESTED_IP); + server_id = get_option(&packet, DHCP_SERVER_ID); + + if (requested) memcpy(&requested_align, requested, 4); + if (server_id) memcpy(&server_id_align, server_id, 4); + + if (lease) { + if (server_id) { + /* SELECTING State */ + DEBUG("server_id = %08x", ntohl(server_id_align)); + if (server_id_align == server_config.server && requested && + requested_align == lease->yiaddr) { + sendACK(&packet, lease->yiaddr); + } + } else { + if (requested) { + /* INIT-REBOOT State */ + if (lease->yiaddr == requested_align) + sendACK(&packet, lease->yiaddr); + else sendNAK(&packet); + } else { + /* RENEWING or REBINDING State */ + if (lease->yiaddr == packet.ciaddr) + sendACK(&packet, lease->yiaddr); + else { + /* don't know what to do!!!! */ + sendNAK(&packet); + } + } + } + + /* what to do if we have no record of the client */ + } else if (server_id) { + /* SELECTING State */ + + } else if (requested) { + /* INIT-REBOOT State */ + if ((lease = find_lease_by_yiaddr(requested_align))) { + if (lease_expired(lease)) { + /* probably best if we drop this lease */ + memset(lease->chaddr, 0, 16); + /* make some contention for this address */ + } else sendNAK(&packet); + } else if (requested_align < server_config.start || + requested_align > server_config.end) { + sendNAK(&packet); + } /* else remain silent */ + + } else { + /* RENEWING or REBINDING State */ + } + break; + case DHCPDECLINE: + DEBUG("Received DECLINE"); + if (lease) { + memset(lease->chaddr, 0, 16); + lease->expires = time(0) + server_config.decline_time; + } + break; + case DHCPRELEASE: + DEBUG("Received RELEASE"); + if (lease) lease->expires = time(0); + break; + case DHCPINFORM: + DEBUG("Received INFORM"); + send_inform(&packet); + break; + default: + bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]); + } + } + + return 0; +} diff --git a/networking/udhcp/dhcpd.h b/networking/udhcp/dhcpd.h new file mode 100644 index 0000000..40959e4 --- /dev/null +++ b/networking/udhcp/dhcpd.h @@ -0,0 +1,190 @@ +/* vi: set sw=4 ts=4: */ +/* dhcpd.h */ +#ifndef _DHCPD_H +#define _DHCPD_H + +/************************************/ +/* Defaults _you_ may want to tweak */ +/************************************/ + +/* the period of time the client is allowed to use that address */ +#define LEASE_TIME (60*60*24*10) /* 10 days of seconds */ +#define LEASES_FILE "/var/lib/misc/udhcpd.leases" + +/* where to find the DHCP server configuration file */ +#define DHCPD_CONF_FILE "/etc/udhcpd.conf" + +/*****************************************************************/ +/* Do not modify below here unless you know what you are doing!! */ +/*****************************************************************/ + +/* DHCP protocol -- see RFC 2131 */ +#define SERVER_PORT 67 +#define CLIENT_PORT 68 + +#define DHCP_MAGIC 0x63825363 + +/* DHCP option codes (partial list) */ +#define DHCP_PADDING 0x00 +#define DHCP_SUBNET 0x01 +#define DHCP_TIME_OFFSET 0x02 +#define DHCP_ROUTER 0x03 +#define DHCP_TIME_SERVER 0x04 +#define DHCP_NAME_SERVER 0x05 +#define DHCP_DNS_SERVER 0x06 +#define DHCP_LOG_SERVER 0x07 +#define DHCP_COOKIE_SERVER 0x08 +#define DHCP_LPR_SERVER 0x09 +#define DHCP_HOST_NAME 0x0c +#define DHCP_BOOT_SIZE 0x0d +#define DHCP_DOMAIN_NAME 0x0f +#define DHCP_SWAP_SERVER 0x10 +#define DHCP_ROOT_PATH 0x11 +#define DHCP_IP_TTL 0x17 +#define DHCP_MTU 0x1a +#define DHCP_BROADCAST 0x1c +#define DHCP_NTP_SERVER 0x2a +#define DHCP_WINS_SERVER 0x2c +#define DHCP_REQUESTED_IP 0x32 +#define DHCP_LEASE_TIME 0x33 +#define DHCP_OPTION_OVER 0x34 +#define DHCP_MESSAGE_TYPE 0x35 +#define DHCP_SERVER_ID 0x36 +#define DHCP_PARAM_REQ 0x37 +#define DHCP_MESSAGE 0x38 +#define DHCP_MAX_SIZE 0x39 +#define DHCP_T1 0x3a +#define DHCP_T2 0x3b +#define DHCP_VENDOR 0x3c +#define DHCP_CLIENT_ID 0x3d +#define DHCP_FQDN 0x51 + +#define DHCP_END 0xFF + + +#define BOOTREQUEST 1 +#define BOOTREPLY 2 + +#define ETH_10MB 1 +#define ETH_10MB_LEN 6 + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +#define BROADCAST_FLAG 0x8000 + +#define OPTION_FIELD 0 +#define FILE_FIELD 1 +#define SNAME_FIELD 2 + +/* miscellaneous defines */ +#define MAC_BCAST_ADDR (uint8_t *) "\xff\xff\xff\xff\xff\xff" +#define OPT_CODE 0 +#define OPT_LEN 1 +#define OPT_DATA 2 + +struct option_set { + uint8_t *data; + struct option_set *next; +}; + +struct static_lease { + uint8_t *mac; + uint32_t *ip; + struct static_lease *next; +}; + +struct server_config_t { + uint32_t server; /* Our IP, in network order */ + uint32_t start; /* Start address of leases, network order */ + uint32_t end; /* End of leases, network order */ + struct option_set *options; /* List of DHCP options loaded from the config file */ + char *interface; /* The name of the interface to use */ + int ifindex; /* Index number of the interface to use */ + uint8_t arp[6]; /* Our arp address */ + unsigned long lease; /* lease time in seconds (host order) */ + unsigned long max_leases; /* maximum number of leases (including reserved address) */ + char remaining; /* should the lease file be interpreted as lease time remaining, or + * as the time the lease expires */ + unsigned long auto_time; /* how long should udhcpd wait before writing a config file. + * if this is zero, it will only write one on SIGUSR1 */ + unsigned long decline_time; /* how long an address is reserved if a client returns a + * decline message */ + unsigned long conflict_time; /* how long an arp conflict offender is leased for */ + unsigned long offer_time; /* how long an offered address is reserved */ + unsigned long min_lease; /* minimum lease a client can request*/ + char *lease_file; + char *pidfile; + char *notify_file; /* What to run whenever leases are written */ + uint32_t siaddr; /* next server bootp option */ + char *sname; /* bootp server name */ + char *boot_file; /* bootp boot file option */ + struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */ +}; + +extern struct server_config_t server_config; +extern struct dhcpOfferedAddr *leases; + + +/*** leases.h ***/ + +struct dhcpOfferedAddr { + uint8_t chaddr[16]; + uint32_t yiaddr; /* network order */ + uint32_t expires; /* host order */ +}; + +extern uint8_t blank_chaddr[]; + +void clear_lease(uint8_t *chaddr, uint32_t yiaddr); +struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease); +int lease_expired(struct dhcpOfferedAddr *lease); +struct dhcpOfferedAddr *oldest_expired_lease(void); +struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr); +struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr); +uint32_t find_address(int check_expired); + + +/*** static_leases.h ***/ + +/* Config file will pass static lease info to this function which will add it + * to a data structure that can be searched later */ +int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip); +/* Check to see if a mac has an associated static lease */ +uint32_t getIpByMac(struct static_lease *lease_struct, void *arg); +/* Check to see if an ip is reserved as a static ip */ +uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip); +/* Print out static leases just to check what's going on (debug code) */ +void printStaticLeases(struct static_lease **lease_struct); + + +/*** serverpacket.h ***/ + +int sendOffer(struct dhcpMessage *oldpacket); +int sendNAK(struct dhcpMessage *oldpacket); +int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr); +int send_inform(struct dhcpMessage *oldpacket); + + +/*** files.h ***/ + +struct config_keyword { + const char *keyword; + int (* const handler)(const char *line, void *var); + void *var; + const char *def; +}; + +int read_config(const char *file); +void write_leases(void); +void read_leases(const char *file); +struct option_set *find_option(struct option_set *opt_list, char code); + + +#endif diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c new file mode 100644 index 0000000..e3a8168 --- /dev/null +++ b/networking/udhcp/dhcprelay.c @@ -0,0 +1,340 @@ +/* vi: set sw=4 ts=4: */ +/* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com> + * + * Licensed under GPL v2, see file LICENSE in this tarball for details. + * + * DHCP Relay for 'DHCPv4 Configuration of IPSec Tunnel Mode' support + * Copyright (C) 2002 Mario Strasser <mast@gmx.net>, + * Zuercher Hochschule Winterthur, + * Netbeat AG + * Upstream has GPL v2 or later + */ + +#include "common.h" +#include "dhcpd.h" +#include "options.h" + +/* constants */ +#define SELECT_TIMEOUT 5 /* select timeout in sec. */ +#define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */ +#define MAX_INTERFACES 9 + + +/* This list holds information about clients. The xid_* functions manipulate this list. */ +static struct xid_item { + u_int32_t xid; + struct sockaddr_in ip; + int client; + time_t timestamp; + struct xid_item *next; +} dhcprelay_xid_list = {0, {0}, 0, 0, NULL}; + + +static struct xid_item * xid_add(u_int32_t xid, struct sockaddr_in *ip, int client) +{ + struct xid_item *item; + + /* create new xid entry */ + item = xmalloc(sizeof(struct xid_item)); + + /* add xid entry */ + item->ip = *ip; + item->xid = xid; + item->client = client; + item->timestamp = time(NULL); + item->next = dhcprelay_xid_list.next; + dhcprelay_xid_list.next = item; + + return item; +} + + +static void xid_expire(void) +{ + struct xid_item *item = dhcprelay_xid_list.next; + struct xid_item *last = &dhcprelay_xid_list; + time_t current_time = time(NULL); + + while (item != NULL) { + if ((current_time-item->timestamp) > MAX_LIFETIME) { + last->next = item->next; + free(item); + item = last->next; + } else { + last = item; + item = item->next; + } + } +} + +static struct xid_item * xid_find(u_int32_t xid) +{ + struct xid_item *item = dhcprelay_xid_list.next; + while (item != NULL) { + if (item->xid == xid) { + return item; + } + item = item->next; + } + return NULL; +} + +static void xid_del(u_int32_t xid) +{ + struct xid_item *item = dhcprelay_xid_list.next; + struct xid_item *last = &dhcprelay_xid_list; + while (item != NULL) { + if (item->xid == xid) { + last->next = item->next; + free(item); + item = last->next; + } else { + last = item; + item = item->next; + } + } +} + + +/** + * get_dhcp_packet_type - gets the message type of a dhcp packet + * p - pointer to the dhcp packet + * returns the message type on success, -1 otherwise + */ +static int get_dhcp_packet_type(struct dhcpMessage *p) +{ + u_char *op; + + /* it must be either a BOOTREQUEST or a BOOTREPLY */ + if (p->op != BOOTREQUEST && p->op != BOOTREPLY) + return -1; + /* get message type option */ + op = get_option(p, DHCP_MESSAGE_TYPE); + if (op != NULL) + return op[0]; + return -1; +} + +/** + * signal_handler - handles signals ;-) + * sig - sent signal + */ +static int dhcprelay_stopflag; +static void dhcprelay_signal_handler(int sig) +{ + dhcprelay_stopflag = 1; +} + +/** + * get_client_devices - parses the devices list + * dev_list - comma separated list of devices + * returns array + */ +static char ** get_client_devices(char *dev_list, int *client_number) +{ + char *s, *list, **client_dev; + int i, cn; + + /* copy list */ + list = xstrdup(dev_list); + if (list == NULL) return NULL; + + /* get number of items */ + for (s = dev_list, cn = 1; *s; s++) + if (*s == ',') + cn++; + + client_dev = xzalloc(cn * sizeof(*client_dev)); + + /* parse list */ + s = strtok(list, ","); + i = 0; + while (s != NULL) { + client_dev[i++] = xstrdup(s); + s = strtok(NULL, ","); + } + + /* free copy and exit */ + free(list); + *client_number = cn; + return client_dev; +} + + +/* Creates listen sockets (in fds) and returns the number allocated. */ +static int init_sockets(char **client, int num_clients, + char *server, int *fds, int *max_socket) +{ + int i; + + /* talk to real server on bootps */ + fds[0] = listen_socket(htonl(INADDR_ANY), 67, server); + if (fds[0] < 0) return -1; + *max_socket = fds[0]; + + /* array starts at 1 since server is 0 */ + num_clients++; + + for (i=1; i < num_clients; i++) { + /* listen for clients on bootps */ + fds[i] = listen_socket(htonl(INADDR_ANY), 67, client[i-1]); + if (fds[i] < 0) return -1; + if (fds[i] > *max_socket) *max_socket = fds[i]; + } + + return i; +} + + +/** + * pass_on() - forwards dhcp packets from client to server + * p - packet to send + * client - number of the client + */ +static void pass_on(struct dhcpMessage *p, int packet_len, int client, int *fds, + struct sockaddr_in *client_addr, struct sockaddr_in *server_addr) +{ + int res, type; + struct xid_item *item; + + /* check packet_type */ + type = get_dhcp_packet_type(p); + if (type != DHCPDISCOVER && type != DHCPREQUEST + && type != DHCPDECLINE && type != DHCPRELEASE + && type != DHCPINFORM + ) { + return; + } + + /* create new xid entry */ + item = xid_add(p->xid, client_addr, client); + + /* forward request to LAN (server) */ + res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr, + sizeof(struct sockaddr_in)); + if (res != packet_len) { + bb_perror_msg("pass_on"); + return; + } +} + +/** + * pass_back() - forwards dhcp packets from server to client + * p - packet to send + */ +static void pass_back(struct dhcpMessage *p, int packet_len, int *fds) +{ + int res, type; + struct xid_item *item; + + /* check xid */ + item = xid_find(p->xid); + if (!item) { + return; + } + + /* check packet type */ + type = get_dhcp_packet_type(p); + if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) { + return; + } + + if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY)) + item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST); + if (item->client > MAX_INTERFACES) + return; + res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*)(&item->ip), + sizeof(item->ip)); + if (res != packet_len) { + bb_perror_msg("pass_back"); + return; + } + + /* remove xid entry */ + xid_del(p->xid); +} + +static void dhcprelay_loop(int *fds, int num_sockets, int max_socket, char **clients, + struct sockaddr_in *server_addr, uint32_t gw_ip) +{ + struct dhcpMessage dhcp_msg; + fd_set rfds; + size_t packlen, addr_size; + struct sockaddr_in client_addr; + struct timeval tv; + int i; + + while (!dhcprelay_stopflag) { + FD_ZERO(&rfds); + for (i = 0; i < num_sockets; i++) + FD_SET(fds[i], &rfds); + tv.tv_sec = SELECT_TIMEOUT; + tv.tv_usec = 0; + if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) { + /* server */ + if (FD_ISSET(fds[0], &rfds)) { + packlen = udhcp_get_packet(&dhcp_msg, fds[0]); + if (packlen > 0) { + pass_back(&dhcp_msg, packlen, fds); + } + } + for (i = 1; i < num_sockets; i++) { + /* clients */ + if (!FD_ISSET(fds[i], &rfds)) + continue; + addr_size = sizeof(struct sockaddr_in); + packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0, + (struct sockaddr *)(&client_addr), &addr_size); + if (packlen <= 0) + continue; + if (read_interface(clients[i-1], NULL, &dhcp_msg.giaddr, NULL) < 0) + dhcp_msg.giaddr = gw_ip; + pass_on(&dhcp_msg, packlen, i, fds, &client_addr, server_addr); + } + } + xid_expire(); + } +} + +int dhcprelay_main(int argc, char **argv) +{ + int i, num_sockets, max_socket, fds[MAX_INTERFACES]; + uint32_t gw_ip; + char **clients; + struct sockaddr_in server_addr; + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(67); + if (argc == 4) { + if (!inet_aton(argv[3], &server_addr.sin_addr)) + bb_perror_msg_and_die("didn't grok server"); + } else if (argc == 3) { + server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + } else { + bb_show_usage(); + } + clients = get_client_devices(argv[1], &num_sockets); + if (!clients) return 0; + + signal(SIGTERM, dhcprelay_signal_handler); + signal(SIGQUIT, dhcprelay_signal_handler); + signal(SIGINT, dhcprelay_signal_handler); + + num_sockets = init_sockets(clients, num_sockets, argv[2], fds, &max_socket); + if (num_sockets == -1) + bb_perror_msg_and_die("init_sockets() failed"); + + if (read_interface(argv[2], NULL, &gw_ip, NULL) == -1) + return 1; + + dhcprelay_loop(fds, num_sockets, max_socket, clients, &server_addr, gw_ip); + + if (ENABLE_FEATURE_CLEAN_UP) { + for (i = 0; i < num_sockets; i++) { + close(fds[i]); + free(clients[i]); + } + } + + return 0; +} diff --git a/networking/udhcp/dumpleases.c b/networking/udhcp/dumpleases.c new file mode 100644 index 0000000..a0e81bb --- /dev/null +++ b/networking/udhcp/dumpleases.c @@ -0,0 +1,74 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ +#include <getopt.h> + +#include "common.h" +#include "dhcpd.h" + + +#define REMAINING 0 +#define ABSOLUTE 1 + +int dumpleases_main(int argc, char *argv[]) +{ + int fp; + int i, c, mode = REMAINING; + unsigned long expires; + const char *file = LEASES_FILE; + struct dhcpOfferedAddr lease; + struct in_addr addr; + + static const struct option options[] = { + {"absolute", 0, 0, 'a'}, + {"remaining", 0, 0, 'r'}, + {"file", 1, 0, 'f'}, + {0, 0, 0, 0} + }; + + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "arf:", options, &option_index); + if (c == -1) break; + + switch (c) { + case 'a': mode = ABSOLUTE; break; + case 'r': mode = REMAINING; break; + case 'f': + file = optarg; + break; + default: + bb_show_usage(); + } + } + + fp = xopen(file, O_RDONLY); + + printf("Mac Address IP-Address Expires %s\n", mode == REMAINING ? "in" : "at"); + /* "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */ + while (full_read(fp, &lease, sizeof(lease)) == sizeof(lease)) { + printf(":%02x"+1, lease.chaddr[0]); + for (i = 1; i < 6; i++) { + printf(":%02x", lease.chaddr[i]); + } + addr.s_addr = lease.yiaddr; + printf(" %-15s ", inet_ntoa(addr)); + expires = ntohl(lease.expires); + if (mode == REMAINING) { + if (!expires) + printf("expired\n"); + else { + unsigned d, h, m; + d = expires / (24*60*60); expires %= (24*60*60); + h = expires / (60*60); expires %= (60*60); + m = expires / 60; expires %= 60; + if (d) printf("%u days ", d); + printf("%02u:%02u:%02u\n", h, m, (unsigned)expires); + } + } else fputs(ctime(&expires), stdout); + } + /* close(fp); */ + + return 0; +} diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c new file mode 100644 index 0000000..5e399e1 --- /dev/null +++ b/networking/udhcp/files.c @@ -0,0 +1,395 @@ +/* vi: set sw=4 ts=4: */ +/* + * files.c -- DHCP server file manipulation * + * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 + */ + +#include <netinet/ether.h> + +#include "common.h" +#include "dhcpd.h" +#include "options.h" + + +/* + * Domain names may have 254 chars, and string options can be 254 + * chars long. However, 80 bytes will be enough for most, and won't + * hog up memory. If you have a special application, change it + */ +#define READ_CONFIG_BUF_SIZE 80 + +/* on these functions, make sure you datatype matches */ +static int read_ip(const char *line, void *arg) +{ + struct in_addr *addr = arg; + struct hostent *host; + int retval = 1; + + if (!inet_aton(line, addr)) { + host = gethostbyname(line); + if (host) + addr->s_addr = *((unsigned long *) host->h_addr_list[0]); + else retval = 0; + } + return retval; +} + +static int read_mac(const char *line, void *arg) +{ + uint8_t *mac_bytes = arg; + struct ether_addr *temp_ether_addr; + int retval = 1; + + temp_ether_addr = ether_aton(line); + + if (temp_ether_addr == NULL) + retval = 0; + else + memcpy(mac_bytes, temp_ether_addr, 6); + + return retval; +} + + +static int read_str(const char *line, void *arg) +{ + char **dest = arg; + + free(*dest); + *dest = strdup(line); + + return 1; +} + + +static int read_u32(const char *line, void *arg) +{ + *((uint32_t*)arg) = bb_strtou32(line, NULL, 10); + return errno == 0; +} + + +static int read_yn(const char *line, void *arg) +{ + char *dest = arg; + int retval = 1; + + if (!strcasecmp("yes", line)) + *dest = 1; + else if (!strcasecmp("no", line)) + *dest = 0; + else retval = 0; + + return retval; +} + + +/* find option 'code' in opt_list */ +struct option_set *find_option(struct option_set *opt_list, char code) +{ + while (opt_list && opt_list->data[OPT_CODE] < code) + opt_list = opt_list->next; + + if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list; + else return NULL; +} + + +/* add an option to the opt_list */ +static void attach_option(struct option_set **opt_list, + const struct dhcp_option *option, char *buffer, int length) +{ + struct option_set *existing, *new, **curr; + + /* add it to an existing option */ + existing = find_option(*opt_list, option->code); + if (existing) { + DEBUG("Attaching option %s to existing member of list", option->name); + if (option->flags & OPTION_LIST) { + if (existing->data[OPT_LEN] + length <= 255) { + existing->data = realloc(existing->data, + existing->data[OPT_LEN] + length + 2); + memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length); + existing->data[OPT_LEN] += length; + } /* else, ignore the data, we could put this in a second option in the future */ + } /* else, ignore the new data */ + } else { + DEBUG("Attaching option %s to list", option->name); + + /* make a new option */ + new = xmalloc(sizeof(struct option_set)); + new->data = xmalloc(length + 2); + new->data[OPT_CODE] = option->code; + new->data[OPT_LEN] = length; + memcpy(new->data + 2, buffer, length); + + curr = opt_list; + while (*curr && (*curr)->data[OPT_CODE] < option->code) + curr = &(*curr)->next; + + new->next = *curr; + *curr = new; + } +} + + +/* read a dhcp option and add it to opt_list */ +static int read_opt(const char *const_line, void *arg) +{ + struct option_set **opt_list = arg; + char *opt, *val, *endptr; + const struct dhcp_option *option; + int retval = 0, length; + char buffer[8]; + char *line; + uint16_t *result_u16 = (uint16_t *) buffer; + uint32_t *result_u32 = (uint32_t *) buffer; + + /* Cheat, the only const line we'll actually get is "" */ + line = (char *) const_line; + opt = strtok(line, " \t="); + if (!opt) return 0; + + option = dhcp_options; + while (1) { + if (!option->code) + return 0; + if (!strcasecmp(option->name, opt)) + break; + option++; + } + + do { + val = strtok(NULL, ", \t"); + if (!val) break; + length = option_lengths[option->flags & TYPE_MASK]; + retval = 0; + opt = buffer; /* new meaning for variable opt */ + switch (option->flags & TYPE_MASK) { + case OPTION_IP: + retval = read_ip(val, buffer); + break; + case OPTION_IP_PAIR: + retval = read_ip(val, buffer); + if (!(val = strtok(NULL, ", \t/-"))) retval = 0; + if (retval) retval = read_ip(val, buffer + 4); + break; + case OPTION_STRING: + length = strlen(val); + if (length > 0) { + if (length > 254) length = 254; + opt = val; + retval = 1; + } + break; + case OPTION_BOOLEAN: + retval = read_yn(val, buffer); + break; + case OPTION_U8: + buffer[0] = strtoul(val, &endptr, 0); + retval = (endptr[0] == '\0'); + break; + case OPTION_U16: + *result_u16 = htons(strtoul(val, &endptr, 0)); + retval = (endptr[0] == '\0'); + break; + case OPTION_S16: + *result_u16 = htons(strtol(val, &endptr, 0)); + retval = (endptr[0] == '\0'); + break; + case OPTION_U32: + *result_u32 = htonl(strtoul(val, &endptr, 0)); + retval = (endptr[0] == '\0'); + break; + case OPTION_S32: + *result_u32 = htonl(strtol(val, &endptr, 0)); + retval = (endptr[0] == '\0'); + break; + default: + break; + } + if (retval) + attach_option(opt_list, option, opt, length); + } while (retval && option->flags & OPTION_LIST); + return retval; +} + +static int read_staticlease(const char *const_line, void *arg) +{ + char *line; + char *mac_string; + char *ip_string; + uint8_t *mac_bytes; + uint32_t *ip; + + + /* Allocate memory for addresses */ + mac_bytes = xmalloc(sizeof(unsigned char) * 8); + ip = xmalloc(sizeof(uint32_t)); + + /* Read mac */ + line = (char *) const_line; + mac_string = strtok(line, " \t"); + read_mac(mac_string, mac_bytes); + + /* Read ip */ + ip_string = strtok(NULL, " \t"); + read_ip(ip_string, ip); + + addStaticLease(arg, mac_bytes, ip); + + if (ENABLE_FEATURE_UDHCP_DEBUG) printStaticLeases(arg); + + return 1; +} + + +static const struct config_keyword keywords[] = { + /* keyword handler variable address default */ + {"start", read_ip, &(server_config.start), "192.168.0.20"}, + {"end", read_ip, &(server_config.end), "192.168.0.254"}, + {"interface", read_str, &(server_config.interface), "eth0"}, + {"option", read_opt, &(server_config.options), ""}, + {"opt", read_opt, &(server_config.options), ""}, + {"max_leases", read_u32, &(server_config.max_leases), "254"}, + {"remaining", read_yn, &(server_config.remaining), "yes"}, + {"auto_time", read_u32, &(server_config.auto_time), "7200"}, + {"decline_time",read_u32, &(server_config.decline_time),"3600"}, + {"conflict_time",read_u32,&(server_config.conflict_time),"3600"}, + {"offer_time", read_u32, &(server_config.offer_time), "60"}, + {"min_lease", read_u32, &(server_config.min_lease), "60"}, + {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE}, + {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"}, + {"notify_file", read_str, &(server_config.notify_file), ""}, + {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"}, + {"sname", read_str, &(server_config.sname), ""}, + {"boot_file", read_str, &(server_config.boot_file), ""}, + {"static_lease",read_staticlease, &(server_config.static_leases), ""}, + /*ADDME: static lease */ + {"", NULL, NULL, ""} +}; + + +int read_config(const char *file) +{ + FILE *in; + char buffer[READ_CONFIG_BUF_SIZE], *token, *line; + int i, lm = 0; + + for (i = 0; keywords[i].keyword[0]; i++) + if (keywords[i].def[0]) + keywords[i].handler(keywords[i].def, keywords[i].var); + + in = fopen(file, "r"); + if (!in) { + bb_error_msg("cannot open config file: %s", file); + return 0; + } + + while (fgets(buffer, READ_CONFIG_BUF_SIZE, in)) { + char debug_orig[READ_CONFIG_BUF_SIZE]; + char *p; + + lm++; + p = strchr(buffer, '\n'); + if (p) *p = '\0'; + if (ENABLE_FEATURE_UDHCP_DEBUG) strcpy(debug_orig, buffer); + p = strchr(buffer, '#'); + if (p) *p = '\0'; + + if (!(token = strtok(buffer, " \t"))) continue; + if (!(line = strtok(NULL, ""))) continue; + + /* eat leading whitespace */ + line = skip_whitespace(line); + /* eat trailing whitespace */ + i = strlen(line) - 1; + while (i >= 0 && isspace(line[i])) + line[i--] = '\0'; + + for (i = 0; keywords[i].keyword[0]; i++) + if (!strcasecmp(token, keywords[i].keyword)) + if (!keywords[i].handler(line, keywords[i].var)) { + bb_error_msg("cannot parse line %d of %s", lm, file); + if (ENABLE_FEATURE_UDHCP_DEBUG) + bb_error_msg("cannot parse '%s'", debug_orig); + /* reset back to the default value */ + keywords[i].handler(keywords[i].def, keywords[i].var); + } + } + fclose(in); + return 1; +} + + +void write_leases(void) +{ + int fp; + unsigned i; + time_t curr = time(0); + unsigned long tmp_time; + + fp = open(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (fp < 0) { + bb_error_msg("cannot open %s for writing", server_config.lease_file); + return; + } + + for (i = 0; i < server_config.max_leases; i++) { + if (leases[i].yiaddr != 0) { + + /* screw with the time in the struct, for easier writing */ + tmp_time = leases[i].expires; + + if (server_config.remaining) { + if (lease_expired(&(leases[i]))) + leases[i].expires = 0; + else leases[i].expires -= curr; + } /* else stick with the time we got */ + leases[i].expires = htonl(leases[i].expires); + // FIXME: error check?? + full_write(fp, &leases[i], sizeof(leases[i])); + + /* then restore it when done */ + leases[i].expires = tmp_time; + } + } + close(fp); + + if (server_config.notify_file) { + char *cmd = xasprintf("%s %s", server_config.notify_file, server_config.lease_file); + system(cmd); + free(cmd); + } +} + + +void read_leases(const char *file) +{ + int fp; + unsigned int i = 0; + struct dhcpOfferedAddr lease; + + fp = open(file, O_RDONLY); + if (fp < 0) { + bb_error_msg("cannot open %s for reading", file); + return; + } + + while (i < server_config.max_leases + && full_read(fp, &lease, sizeof(lease)) == sizeof(lease) + ) { + /* ADDME: is it a static lease */ + if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) { + lease.expires = ntohl(lease.expires); + if (!server_config.remaining) lease.expires -= time(0); + if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) { + bb_error_msg("too many leases while loading %s", file); + break; + } + i++; + } + } + DEBUG("Read %d leases", i); + close(fp); +} diff --git a/networking/udhcp/leases.c b/networking/udhcp/leases.c new file mode 100644 index 0000000..2f7847d --- /dev/null +++ b/networking/udhcp/leases.c @@ -0,0 +1,145 @@ +/* vi: set sw=4 ts=4: */ +/* + * leases.c -- tools to manage DHCP leases + * Russ Dill <Russ.Dill@asu.edu> July 2001 + */ + +#include "common.h" +#include "dhcpd.h" + + +uint8_t blank_chaddr[] = {[0 ... 15] = 0}; + +/* clear every lease out that chaddr OR yiaddr matches and is nonzero */ +void clear_lease(uint8_t *chaddr, uint32_t yiaddr) +{ + unsigned int i, j; + + for (j = 0; j < 16 && !chaddr[j]; j++); + + for (i = 0; i < server_config.max_leases; i++) + if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) || + (yiaddr && leases[i].yiaddr == yiaddr)) { + memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr)); + } +} + + +/* add a lease into the table, clearing out any old ones */ +struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease) +{ + struct dhcpOfferedAddr *oldest; + + /* clean out any old ones */ + clear_lease(chaddr, yiaddr); + + oldest = oldest_expired_lease(); + + if (oldest) { + memcpy(oldest->chaddr, chaddr, 16); + oldest->yiaddr = yiaddr; + oldest->expires = time(0) + lease; + } + + return oldest; +} + + +/* true if a lease has expired */ +int lease_expired(struct dhcpOfferedAddr *lease) +{ + return (lease->expires < (unsigned long) time(0)); +} + + +/* Find the oldest expired lease, NULL if there are no expired leases */ +struct dhcpOfferedAddr *oldest_expired_lease(void) +{ + struct dhcpOfferedAddr *oldest = NULL; + unsigned long oldest_lease = time(0); + unsigned int i; + + + for (i = 0; i < server_config.max_leases; i++) + if (oldest_lease > leases[i].expires) { + oldest_lease = leases[i].expires; + oldest = &(leases[i]); + } + return oldest; + +} + + +/* Find the first lease that matches chaddr, NULL if no match */ +struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr) +{ + unsigned int i; + + for (i = 0; i < server_config.max_leases; i++) + if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]); + + return NULL; +} + + +/* Find the first lease that matches yiaddr, NULL is no match */ +struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr) +{ + unsigned int i; + + for (i = 0; i < server_config.max_leases; i++) + if (leases[i].yiaddr == yiaddr) return &(leases[i]); + + return NULL; +} + + +/* check is an IP is taken, if it is, add it to the lease table */ +static int check_ip(uint32_t addr) +{ + struct in_addr temp; + + if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) { + temp.s_addr = addr; + bb_info_msg("%s belongs to someone, reserving it for %ld seconds", + inet_ntoa(temp), server_config.conflict_time); + add_lease(blank_chaddr, addr, server_config.conflict_time); + return 1; + } else return 0; +} + + +/* find an assignable address, it check_expired is true, we check all the expired leases as well. + * Maybe this should try expired leases by age... */ +uint32_t find_address(int check_expired) +{ + uint32_t addr, ret; + struct dhcpOfferedAddr *lease = NULL; + + addr = ntohl(server_config.start); /* addr is in host order here */ + for (;addr <= ntohl(server_config.end); addr++) { + + /* ie, 192.168.55.0 */ + if (!(addr & 0xFF)) continue; + + /* ie, 192.168.55.255 */ + if ((addr & 0xFF) == 0xFF) continue; + + /* Only do if it isn't an assigned as a static lease */ + if (!reservedIp(server_config.static_leases, htonl(addr))) { + + /* lease is not taken */ + ret = htonl(addr); + lease = find_lease_by_yiaddr(ret); + + /* no lease or it expired and we are checking for expired leases */ + if ( (!lease || (check_expired && lease_expired(lease))) + && /* and it isn't on the network */ !check_ip(ret) + ) { + return ret; + break; + } + } + } + return 0; +} diff --git a/networking/udhcp/options.c b/networking/udhcp/options.c new file mode 100644 index 0000000..4a46da5 --- /dev/null +++ b/networking/udhcp/options.c @@ -0,0 +1,174 @@ +/* vi: set sw=4 ts=4: */ +/* + * options.c -- DHCP server option packet tools + * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 + */ + +#include "common.h" +#include "dhcpd.h" +#include "options.h" + + +/* supported options are easily added here */ +const struct dhcp_option dhcp_options[] = { + /* name[10] flags code */ + {"subnet", OPTION_IP | OPTION_REQ, 0x01}, + {"timezone", OPTION_S32, 0x02}, + {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03}, + {"timesvr", OPTION_IP | OPTION_LIST, 0x04}, + {"namesvr", OPTION_IP | OPTION_LIST, 0x05}, + {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06}, + {"logsvr", OPTION_IP | OPTION_LIST, 0x07}, + {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08}, + {"lprsvr", OPTION_IP | OPTION_LIST, 0x09}, + {"hostname", OPTION_STRING | OPTION_REQ, 0x0c}, + {"bootsize", OPTION_U16, 0x0d}, + {"domain", OPTION_STRING | OPTION_REQ, 0x0f}, + {"swapsvr", OPTION_IP, 0x10}, + {"rootpath", OPTION_STRING, 0x11}, + {"ipttl", OPTION_U8, 0x17}, + {"mtu", OPTION_U16, 0x1a}, + {"broadcast", OPTION_IP | OPTION_REQ, 0x1c}, + {"nisdomain", OPTION_STRING | OPTION_REQ, 0x28}, + {"nissrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x29}, + {"ntpsrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a}, + {"wins", OPTION_IP | OPTION_LIST, 0x2c}, + {"requestip", OPTION_IP, 0x32}, + {"lease", OPTION_U32, 0x33}, + {"dhcptype", OPTION_U8, 0x35}, + {"serverid", OPTION_IP, 0x36}, + {"message", OPTION_STRING, 0x38}, + {"vendorclass", OPTION_STRING, 0x3C}, + {"clientid", OPTION_STRING, 0x3D}, + {"tftp", OPTION_STRING, 0x42}, + {"bootfile", OPTION_STRING, 0x43}, + {"userclass", OPTION_STRING, 0x4D}, + /* MSIE's "Web Proxy Autodiscovery Protocol" support */ + {"wpad", OPTION_STRING, 0xfc}, + {"", 0x00, 0x00} +}; + +/* Lengths of the different option types */ +const unsigned char option_lengths[] = { + [OPTION_IP] = 4, + [OPTION_IP_PAIR] = 8, + [OPTION_BOOLEAN] = 1, + [OPTION_STRING] = 1, + [OPTION_U8] = 1, + [OPTION_U16] = 2, + [OPTION_S16] = 2, + [OPTION_U32] = 4, + [OPTION_S32] = 4 +}; + + +/* get an option with bounds checking (warning, not aligned). */ +uint8_t *get_option(struct dhcpMessage *packet, int code) +{ + int i, length; + uint8_t *optionptr; + int over = 0, done = 0, curr = OPTION_FIELD; + + optionptr = packet->options; + i = 0; + length = 308; + while (!done) { + if (i >= length) { + bb_error_msg("bogus packet, option fields too long"); + return NULL; + } + if (optionptr[i + OPT_CODE] == code) { + if (i + 1 + optionptr[i + OPT_LEN] >= length) { + bb_error_msg("bogus packet, option fields too long"); + return NULL; + } + return optionptr + i + 2; + } + switch (optionptr[i + OPT_CODE]) { + case DHCP_PADDING: + i++; + break; + case DHCP_OPTION_OVER: + if (i + 1 + optionptr[i + OPT_LEN] >= length) { + bb_error_msg("bogus packet, option fields too long"); + return NULL; + } + over = optionptr[i + 3]; + i += optionptr[OPT_LEN] + 2; + break; + case DHCP_END: + if (curr == OPTION_FIELD && over & FILE_FIELD) { + optionptr = packet->file; + i = 0; + length = 128; + curr = FILE_FIELD; + } else if (curr == FILE_FIELD && over & SNAME_FIELD) { + optionptr = packet->sname; + i = 0; + length = 64; + curr = SNAME_FIELD; + } else done = 1; + break; + default: + i += optionptr[OPT_LEN + i] + 2; + } + } + return NULL; +} + + +/* return the position of the 'end' option (no bounds checking) */ +int end_option(uint8_t *optionptr) +{ + int i = 0; + + while (optionptr[i] != DHCP_END) { + if (optionptr[i] == DHCP_PADDING) i++; + else i += optionptr[i + OPT_LEN] + 2; + } + return i; +} + + +/* add an option string to the options (an option string contains an option code, + * length, then data) */ +int add_option_string(uint8_t *optionptr, uint8_t *string) +{ + int end = end_option(optionptr); + + /* end position + string length + option code/length + end option */ + if (end + string[OPT_LEN] + 2 + 1 >= 308) { + bb_error_msg("option 0x%02x did not fit into the packet", + string[OPT_CODE]); + return 0; + } + DEBUG("adding option 0x%02x", string[OPT_CODE]); + memcpy(optionptr + end, string, string[OPT_LEN] + 2); + optionptr[end + string[OPT_LEN] + 2] = DHCP_END; + return string[OPT_LEN] + 2; +} + + +/* add a one to four byte option to a packet */ +int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data) +{ + const struct dhcp_option *dh; + + for (dh = dhcp_options; dh->code; dh++) { + if (dh->code == code) { + uint8_t option[6], len; + + option[OPT_CODE] = code; + len = option_lengths[dh->flags & TYPE_MASK]; + option[OPT_LEN] = len; + if (BB_BIG_ENDIAN) data <<= 8 * (4 - len); + /* This memcpy is for broken processors which can't + * handle a simple unaligned 32-bit assignment */ + memcpy(&option[OPT_DATA], &data, 4); + return add_option_string(optionptr, option); + } + } + + bb_error_msg("cannot add option 0x%02x", code); + return 0; +} diff --git a/networking/udhcp/options.h b/networking/udhcp/options.h new file mode 100644 index 0000000..588504e --- /dev/null +++ b/networking/udhcp/options.h @@ -0,0 +1,37 @@ +/* vi: set sw=4 ts=4: */ +/* options.h */ +#ifndef _OPTIONS_H +#define _OPTIONS_H + +#define TYPE_MASK 0x0F + +enum { + OPTION_IP=1, + OPTION_IP_PAIR, + OPTION_STRING, + OPTION_BOOLEAN, + OPTION_U8, + OPTION_U16, + OPTION_S16, + OPTION_U32, + OPTION_S32 +}; + +#define OPTION_REQ 0x10 /* have the client request this option */ +#define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */ + +struct dhcp_option { + char name[12]; + char flags; + uint8_t code; +}; + +extern const struct dhcp_option dhcp_options[]; +extern const unsigned char option_lengths[]; + +uint8_t *get_option(struct dhcpMessage *packet, int code); +int end_option(uint8_t *optionptr); +int add_option_string(uint8_t *optionptr, uint8_t *string); +int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data); + +#endif diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c new file mode 100644 index 0000000..dec9d0a --- /dev/null +++ b/networking/udhcp/packet.c @@ -0,0 +1,211 @@ +/* vi: set sw=4 ts=4: */ + +#include <netinet/in.h> +#if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION +#include <netpacket/packet.h> +#include <net/ethernet.h> +#else +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#endif + +#include "common.h" +#include "dhcpd.h" +#include "options.h" + + +void udhcp_init_header(struct dhcpMessage *packet, char type) +{ + memset(packet, 0, sizeof(struct dhcpMessage)); + switch (type) { + case DHCPDISCOVER: + case DHCPREQUEST: + case DHCPRELEASE: + case DHCPINFORM: + packet->op = BOOTREQUEST; + break; + case DHCPOFFER: + case DHCPACK: + case DHCPNAK: + packet->op = BOOTREPLY; + } + packet->htype = ETH_10MB; + packet->hlen = ETH_10MB_LEN; + packet->cookie = htonl(DHCP_MAGIC); + packet->options[0] = DHCP_END; + add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type); +} + + +/* read a packet from socket fd, return -1 on read error, -2 on packet error */ +int udhcp_get_packet(struct dhcpMessage *packet, int fd) +{ + static const char broken_vendors[][8] = { + "MSFT 98", + "" + }; + int bytes; + int i; + char unsigned *vendor; + + memset(packet, 0, sizeof(struct dhcpMessage)); + bytes = read(fd, packet, sizeof(struct dhcpMessage)); + if (bytes < 0) { + DEBUG("cannot read on listening socket, ignoring"); + return -1; + } + + if (ntohl(packet->cookie) != DHCP_MAGIC) { + bb_error_msg("received bogus message, ignoring"); + return -2; + } + DEBUG("Received a packet"); + + if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) { + for (i = 0; broken_vendors[i][0]; i++) { + if (vendor[OPT_LEN - 2] == (uint8_t)strlen(broken_vendors[i]) + && !strncmp((char*)vendor, broken_vendors[i], vendor[OPT_LEN - 2]) + ) { + DEBUG("broken client (%s), forcing broadcast", + broken_vendors[i]); + packet->flags |= htons(BROADCAST_FLAG); + } + } + } + + return bytes; +} + + +uint16_t udhcp_checksum(void *addr, int count) +{ + /* Compute Internet Checksum for "count" bytes + * beginning at location "addr". + */ + int32_t sum = 0; + uint16_t *source = (uint16_t *) addr; + + while (count > 1) { + /* This is the inner loop */ + sum += *source++; + count -= 2; + } + + /* Add left-over byte, if any */ + if (count > 0) { + /* Make sure that the left-over byte is added correctly both + * with little and big endian hosts */ + uint16_t tmp = 0; + *(uint8_t *) (&tmp) = * (uint8_t *) source; + sum += tmp; + } + /* Fold 32-bit sum to 16 bits */ + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return ~sum; +} + + +/* Construct a ip/udp header for a packet, and specify the source and dest hardware address */ +void BUG_sizeof_struct_udp_dhcp_packet_must_be_576(void); +int udhcp_raw_packet(struct dhcpMessage *payload, + uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex) +{ + int fd; + int result; + struct sockaddr_ll dest; + struct udp_dhcp_packet packet; + + fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + if (fd < 0) { + bb_perror_msg("socket"); + return -1; + } + + memset(&dest, 0, sizeof(dest)); + memset(&packet, 0, sizeof(packet)); + + dest.sll_family = AF_PACKET; + dest.sll_protocol = htons(ETH_P_IP); + dest.sll_ifindex = ifindex; + dest.sll_halen = 6; + memcpy(dest.sll_addr, dest_arp, 6); + if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) { + bb_perror_msg("bind"); + close(fd); + return -1; + } + + packet.ip.protocol = IPPROTO_UDP; + packet.ip.saddr = source_ip; + packet.ip.daddr = dest_ip; + packet.udp.source = htons(source_port); + packet.udp.dest = htons(dest_port); + packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */ + packet.ip.tot_len = packet.udp.len; + memcpy(&(packet.data), payload, sizeof(struct dhcpMessage)); + packet.udp.check = udhcp_checksum(&packet, sizeof(struct udp_dhcp_packet)); + + packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet)); + packet.ip.ihl = sizeof(packet.ip) >> 2; + packet.ip.version = IPVERSION; + packet.ip.ttl = IPDEFTTL; + packet.ip.check = udhcp_checksum(&(packet.ip), sizeof(packet.ip)); + + if (sizeof(struct udp_dhcp_packet) != 576) + BUG_sizeof_struct_udp_dhcp_packet_must_be_576(); + + result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, + (struct sockaddr *) &dest, sizeof(dest)); + if (result <= 0) { + bb_perror_msg("sendto"); + } + close(fd); + return result; +} + + +/* Let the kernel do all the work for packet generation */ +int udhcp_kernel_packet(struct dhcpMessage *payload, + uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port) +{ + int fd, result; + struct sockaddr_in client; + + fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) + return -1; + + if (setsockopt_reuseaddr(fd) == -1) { + close(fd); + return -1; + } + + memset(&client, 0, sizeof(client)); + client.sin_family = AF_INET; + client.sin_port = htons(source_port); + client.sin_addr.s_addr = source_ip; + + if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) { + close(fd); + return -1; + } + + memset(&client, 0, sizeof(client)); + client.sin_family = AF_INET; + client.sin_port = htons(dest_port); + client.sin_addr.s_addr = dest_ip; + + if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) { + close(fd); + return -1; + } + + result = write(fd, payload, sizeof(struct dhcpMessage)); + close(fd); + return result; +} diff --git a/networking/udhcp/pidfile.c b/networking/udhcp/pidfile.c new file mode 100644 index 0000000..bcb2608 --- /dev/null +++ b/networking/udhcp/pidfile.c @@ -0,0 +1,66 @@ +/* vi: set sw=4 ts=4: */ +/* pidfile.c + * + * Functions to assist in the writing and removing of pidfiles. + * + * Russ Dill <Russ.Dill@asu.edu> September 2001 + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "common.h" + + +static const char *saved_pidfile; + +static void pidfile_delete(void) +{ + if (saved_pidfile) unlink(saved_pidfile); +} + + +int pidfile_acquire(const char *pidfile) +{ + int pid_fd; + if (!pidfile) return -1; + + pid_fd = open(pidfile, O_CREAT|O_WRONLY|O_TRUNC, 0644); + if (pid_fd < 0) { + bb_perror_msg("cannot open pidfile %s", pidfile); + } else { + lockf(pid_fd, F_LOCK, 0); + if (!saved_pidfile) + atexit(pidfile_delete); + saved_pidfile = pidfile; + } + + return pid_fd; +} + + +void pidfile_write_release(int pid_fd) +{ + FILE *out; + + if (pid_fd < 0) return; + + out = fdopen(pid_fd, "w"); + if (out) { + fprintf(out, "%d\n", getpid()); + fclose(out); + } + lockf(pid_fd, F_UNLCK, 0); + close(pid_fd); +} diff --git a/networking/udhcp/script.c b/networking/udhcp/script.c new file mode 100644 index 0000000..07f6836 --- /dev/null +++ b/networking/udhcp/script.c @@ -0,0 +1,213 @@ +/* vi: set sw=4 ts=4: */ +/* script.c + * + * Functions to call the DHCP client notification scripts + * + * Russ Dill <Russ.Dill@asu.edu> July 2001 + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "common.h" +#include "dhcpd.h" +#include "dhcpc.h" +#include "options.h" + + +/* get a rough idea of how long an option will be (rounding up...) */ +static const int max_option_length[] = { + [OPTION_IP] = sizeof("255.255.255.255 "), + [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, + [OPTION_STRING] = 1, + [OPTION_BOOLEAN] = sizeof("yes "), + [OPTION_U8] = sizeof("255 "), + [OPTION_U16] = sizeof("65535 "), + [OPTION_S16] = sizeof("-32768 "), + [OPTION_U32] = sizeof("4294967295 "), + [OPTION_S32] = sizeof("-2147483684 "), +}; + + +static inline int upper_length(int length, int opt_index) +{ + return max_option_length[opt_index] * + (length / option_lengths[opt_index]); +} + + +static int sprintip(char *dest, char *pre, uint8_t *ip) +{ + return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]); +} + + +/* really simple implementation, just count the bits */ +static int mton(struct in_addr *mask) +{ + int i; + unsigned long bits = ntohl(mask->s_addr); + /* too bad one can't check the carry bit, etc in c bit + * shifting */ + for (i = 0; i < 32 && !((bits >> i) & 1); i++); + return 32 - i; +} + + +/* Fill dest with the text of option 'option'. */ +static void fill_options(char *dest, uint8_t *option, + const struct dhcp_option *type_p) +{ + int type, optlen; + uint16_t val_u16; + int16_t val_s16; + uint32_t val_u32; + int32_t val_s32; + int len = option[OPT_LEN - 2]; + + dest += sprintf(dest, "%s=", type_p->name); + + type = type_p->flags & TYPE_MASK; + optlen = option_lengths[type]; + for (;;) { + switch (type) { + case OPTION_IP_PAIR: + dest += sprintip(dest, "", option); + *(dest++) = '/'; + option += 4; + optlen = 4; + case OPTION_IP: /* Works regardless of host byte order. */ + dest += sprintip(dest, "", option); + break; + case OPTION_BOOLEAN: + dest += sprintf(dest, *option ? "yes" : "no"); + break; + case OPTION_U8: + dest += sprintf(dest, "%u", *option); + break; + case OPTION_U16: + memcpy(&val_u16, option, 2); + dest += sprintf(dest, "%u", ntohs(val_u16)); + break; + case OPTION_S16: + memcpy(&val_s16, option, 2); + dest += sprintf(dest, "%d", ntohs(val_s16)); + break; + case OPTION_U32: + memcpy(&val_u32, option, 4); + dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32)); + break; + case OPTION_S32: + memcpy(&val_s32, option, 4); + dest += sprintf(dest, "%ld", (long) ntohl(val_s32)); + break; + case OPTION_STRING: + memcpy(dest, option, len); + dest[len] = '\0'; + return; /* Short circuit this case */ + } + option += optlen; + len -= optlen; + if (len <= 0) break; + dest += sprintf(dest, " "); + } +} + + +/* put all the parameters into an environment */ +static char **fill_envp(struct dhcpMessage *packet) +{ + int num_options = 0; + int i, j; + char **envp; + uint8_t *temp; + struct in_addr subnet; + char over = 0; + + if (packet == NULL) + num_options = 0; + else { + for (i = 0; dhcp_options[i].code; i++) + if (get_option(packet, dhcp_options[i].code)) { + num_options++; + if (dhcp_options[i].code == DHCP_SUBNET) + num_options++; /* for mton */ + } + if (packet->siaddr) num_options++; + if ((temp = get_option(packet, DHCP_OPTION_OVER))) + over = *temp; + if (!(over & FILE_FIELD) && packet->file[0]) num_options++; + if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++; + } + + envp = xzalloc(sizeof(char *) * (num_options + 5)); + j = 0; + envp[j++] = xasprintf("interface=%s", client_config.interface); + envp[j++] = xasprintf("PATH=%s", + getenv("PATH") ? : "/bin:/usr/bin:/sbin:/usr/sbin"); + envp[j++] = xasprintf("HOME=%s", getenv("HOME") ? : "/"); + + if (packet == NULL) return envp; + + envp[j] = xmalloc(sizeof("ip=255.255.255.255")); + sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr); + + for (i = 0; dhcp_options[i].code; i++) { + temp = get_option(packet, dhcp_options[i].code); + if (!temp) + continue; + envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], + dhcp_options[i].flags & TYPE_MASK) + strlen(dhcp_options[i].name) + 2); + fill_options(envp[j++], temp, &dhcp_options[i]); + + /* Fill in a subnet bits option for things like /24 */ + if (dhcp_options[i].code == DHCP_SUBNET) { + memcpy(&subnet, temp, 4); + envp[j++] = xasprintf("mask=%d", mton(&subnet)); + } + } + if (packet->siaddr) { + envp[j] = xmalloc(sizeof("siaddr=255.255.255.255")); + sprintip(envp[j++], "siaddr=", (uint8_t *) &packet->siaddr); + } + if (!(over & FILE_FIELD) && packet->file[0]) { + /* watch out for invalid packets */ + packet->file[sizeof(packet->file) - 1] = '\0'; + envp[j++] = xasprintf("boot_file=%s", packet->file); + } + if (!(over & SNAME_FIELD) && packet->sname[0]) { + /* watch out for invalid packets */ + packet->sname[sizeof(packet->sname) - 1] = '\0'; + envp[j++] = xasprintf("sname=%s", packet->sname); + } + return envp; +} + + +/* Call a script with a par file and env vars */ +void udhcp_run_script(struct dhcpMessage *packet, const char *name) +{ + int pid; + char **envp, **curr; + + if (client_config.script == NULL) + return; + + DEBUG("vfork'ing and execle'ing %s", client_config.script); + + envp = fill_envp(packet); + /* call script */ + pid = vfork(); + if (pid) { + waitpid(pid, NULL, 0); + for (curr = envp; *curr; curr++) free(*curr); + free(envp); + return; + } else if (pid == 0) { + /* close fd's? */ + /* exec script */ + execle(client_config.script, client_config.script, + name, NULL, envp); + bb_perror_msg("script %s failed", client_config.script); + exit(1); + } +} diff --git a/networking/udhcp/serverpacket.c b/networking/udhcp/serverpacket.c new file mode 100644 index 0000000..8889fda --- /dev/null +++ b/networking/udhcp/serverpacket.c @@ -0,0 +1,261 @@ +/* vi: set sw=4 ts=4: */ +/* serverpacket.c + * + * Construct and send DHCP server packets + * + * Russ Dill <Russ.Dill@asu.edu> July 2001 + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "common.h" +#include "dhcpd.h" +#include "options.h" + + +/* send a packet to giaddr using the kernel ip stack */ +static int send_packet_to_relay(struct dhcpMessage *payload) +{ + DEBUG("Forwarding packet to relay"); + + return udhcp_kernel_packet(payload, server_config.server, SERVER_PORT, + payload->giaddr, SERVER_PORT); +} + + +/* send a packet to a specific arp address and ip address by creating our own ip packet */ +static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast) +{ + uint8_t *chaddr; + uint32_t ciaddr; + + if (force_broadcast) { + DEBUG("broadcasting packet to client (NAK)"); + ciaddr = INADDR_BROADCAST; + chaddr = MAC_BCAST_ADDR; + } else if (payload->ciaddr) { + DEBUG("unicasting packet to client ciaddr"); + ciaddr = payload->ciaddr; + chaddr = payload->chaddr; + } else if (ntohs(payload->flags) & BROADCAST_FLAG) { + DEBUG("broadcasting packet to client (requested)"); + ciaddr = INADDR_BROADCAST; + chaddr = MAC_BCAST_ADDR; + } else { + DEBUG("unicasting packet to client yiaddr"); + ciaddr = payload->yiaddr; + chaddr = payload->chaddr; + } + return udhcp_raw_packet(payload, server_config.server, SERVER_PORT, + ciaddr, CLIENT_PORT, chaddr, server_config.ifindex); +} + + +/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */ +static int send_packet(struct dhcpMessage *payload, int force_broadcast) +{ + int ret; + + if (payload->giaddr) + ret = send_packet_to_relay(payload); + else ret = send_packet_to_client(payload, force_broadcast); + return ret; +} + + +static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type) +{ + udhcp_init_header(packet, type); + packet->xid = oldpacket->xid; + memcpy(packet->chaddr, oldpacket->chaddr, 16); + packet->flags = oldpacket->flags; + packet->giaddr = oldpacket->giaddr; + packet->ciaddr = oldpacket->ciaddr; + add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server); +} + + +/* add in the bootp options */ +static void add_bootp_options(struct dhcpMessage *packet) +{ + packet->siaddr = server_config.siaddr; + if (server_config.sname) + strncpy((char*)packet->sname, server_config.sname, sizeof(packet->sname) - 1); + if (server_config.boot_file) + strncpy((char*)packet->file, server_config.boot_file, sizeof(packet->file) - 1); +} + + +/* send a DHCP OFFER to a DHCP DISCOVER */ +int sendOffer(struct dhcpMessage *oldpacket) +{ + struct dhcpMessage packet; + struct dhcpOfferedAddr *lease = NULL; + uint32_t req_align, lease_time_align = server_config.lease; + uint8_t *req, *lease_time; + struct option_set *curr; + struct in_addr addr; + + uint32_t static_lease_ip; + + init_packet(&packet, oldpacket, DHCPOFFER); + + static_lease_ip = getIpByMac(server_config.static_leases, oldpacket->chaddr); + + /* ADDME: if static, short circuit */ + if (!static_lease_ip) { + /* the client is in our lease/offered table */ + lease = find_lease_by_chaddr(oldpacket->chaddr); + if (lease) { + if (!lease_expired(lease)) + lease_time_align = lease->expires - time(0); + packet.yiaddr = lease->yiaddr; + + /* Or the client has a requested ip */ + } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) + /* Don't look here (ugly hackish thing to do) */ + && memcpy(&req_align, req, 4) + /* and the ip is in the lease range */ + && ntohl(req_align) >= ntohl(server_config.start) + && ntohl(req_align) <= ntohl(server_config.end) + && !static_lease_ip /* Check that its not a static lease */ + /* and is not already taken/offered */ + && (!(lease = find_lease_by_yiaddr(req_align)) + /* or its taken, but expired */ /* ADDME: or maybe in here */ + || lease_expired(lease)) + ) { + packet.yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */ + /* otherwise, find a free IP */ + } else { + /* Is it a static lease? (No, because find_address skips static lease) */ + packet.yiaddr = find_address(0); + /* try for an expired lease */ + if (!packet.yiaddr) packet.yiaddr = find_address(1); + } + + if (!packet.yiaddr) { + bb_error_msg("no IP addresses to give - OFFER abandoned"); + return -1; + } + if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) { + bb_error_msg("lease pool is full - OFFER abandoned"); + return -1; + } + lease_time = get_option(oldpacket, DHCP_LEASE_TIME); + if (lease_time) { + memcpy(&lease_time_align, lease_time, 4); + lease_time_align = ntohl(lease_time_align); + if (lease_time_align > server_config.lease) + lease_time_align = server_config.lease; + } + + /* Make sure we aren't just using the lease time from the previous offer */ + if (lease_time_align < server_config.min_lease) + lease_time_align = server_config.lease; + /* ADDME: end of short circuit */ + } else { + /* It is a static lease... use it */ + packet.yiaddr = static_lease_ip; + } + + add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); + + curr = server_config.options; + while (curr) { + if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) + add_option_string(packet.options, curr->data); + curr = curr->next; + } + + add_bootp_options(&packet); + + addr.s_addr = packet.yiaddr; + bb_info_msg("Sending OFFER of %s", inet_ntoa(addr)); + return send_packet(&packet, 0); +} + + +int sendNAK(struct dhcpMessage *oldpacket) +{ + struct dhcpMessage packet; + + init_packet(&packet, oldpacket, DHCPNAK); + + DEBUG("Sending NAK"); + return send_packet(&packet, 1); +} + + +int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr) +{ + struct dhcpMessage packet; + struct option_set *curr; + uint8_t *lease_time; + uint32_t lease_time_align = server_config.lease; + struct in_addr addr; + + init_packet(&packet, oldpacket, DHCPACK); + packet.yiaddr = yiaddr; + + if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) { + memcpy(&lease_time_align, lease_time, 4); + lease_time_align = ntohl(lease_time_align); + if (lease_time_align > server_config.lease) + lease_time_align = server_config.lease; + else if (lease_time_align < server_config.min_lease) + lease_time_align = server_config.lease; + } + + add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); + + curr = server_config.options; + while (curr) { + if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) + add_option_string(packet.options, curr->data); + curr = curr->next; + } + + add_bootp_options(&packet); + + addr.s_addr = packet.yiaddr; + bb_info_msg("Sending ACK to %s", inet_ntoa(addr)); + + if (send_packet(&packet, 0) < 0) + return -1; + + add_lease(packet.chaddr, packet.yiaddr, lease_time_align); + + return 0; +} + + +int send_inform(struct dhcpMessage *oldpacket) +{ + struct dhcpMessage packet; + struct option_set *curr; + + init_packet(&packet, oldpacket, DHCPACK); + + curr = server_config.options; + while (curr) { + if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) + add_option_string(packet.options, curr->data); + curr = curr->next; + } + + add_bootp_options(&packet); + + return send_packet(&packet, 0); +} diff --git a/networking/udhcp/signalpipe.c b/networking/udhcp/signalpipe.c new file mode 100644 index 0000000..3615965 --- /dev/null +++ b/networking/udhcp/signalpipe.c @@ -0,0 +1,77 @@ +/* vi: set sw=4 ts=4: */ +/* signalpipe.c + * + * Signal pipe infrastructure. A reliable way of delivering signals. + * + * Russ Dill <Russ.Dill@asu.edu> December 2003 + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "common.h" + + +static int signal_pipe[2]; + +static void signal_handler(int sig) +{ + if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) + bb_perror_msg("cannot send signal"); +} + + +/* Call this before doing anything else. Sets up the socket pair + * and installs the signal handler */ +void udhcp_sp_setup(void) +{ + socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); + fcntl(signal_pipe[0], F_SETFD, FD_CLOEXEC); + fcntl(signal_pipe[1], F_SETFD, FD_CLOEXEC); + signal(SIGUSR1, signal_handler); + signal(SIGUSR2, signal_handler); + signal(SIGTERM, signal_handler); +} + + +/* Quick little function to setup the rfds. Will return the + * max_fd for use with select. Limited in that you can only pass + * one extra fd */ +int udhcp_sp_fd_set(fd_set *rfds, int extra_fd) +{ + FD_ZERO(rfds); + FD_SET(signal_pipe[0], rfds); + if (extra_fd >= 0) { + fcntl(extra_fd, F_SETFD, FD_CLOEXEC); + FD_SET(extra_fd, rfds); + } + return signal_pipe[0] > extra_fd ? signal_pipe[0] : extra_fd; +} + + +/* Read a signal from the signal pipe. Returns 0 if there is + * no signal, -1 on error (and sets errno appropriately), and + * your signal on success */ +int udhcp_sp_read(fd_set *rfds) +{ + int sig; + + if (!FD_ISSET(signal_pipe[0], rfds)) + return 0; + + if (read(signal_pipe[0], &sig, sizeof(sig)) < 0) + return -1; + + return sig; +} diff --git a/networking/udhcp/socket.c b/networking/udhcp/socket.c new file mode 100644 index 0000000..2bae68f --- /dev/null +++ b/networking/udhcp/socket.c @@ -0,0 +1,130 @@ +/* vi: set sw=4 ts=4: */ +/* + * socket.c -- DHCP server client/server socket creation + * + * udhcp client/server + * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au> + * Chris Trew <ctrew@moreton.com.au> + * + * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <net/if.h> +#include <features.h> +#if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION +#include <netpacket/packet.h> +#include <net/ethernet.h> +#else +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#endif + +#include "common.h" + + +int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp) +{ + int fd; + struct ifreq ifr; + struct sockaddr_in *our_ip; + + memset(&ifr, 0, sizeof(struct ifreq)); + fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (fd < 0) { + bb_perror_msg("socket failed"); + return -1; + } + + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name)); + if (addr) { + if (ioctl(fd, SIOCGIFADDR, &ifr) != 0) { + bb_perror_msg("SIOCGIFADDR failed, is the interface up and configured?"); + close(fd); + return -1; + } + our_ip = (struct sockaddr_in *) &ifr.ifr_addr; + *addr = our_ip->sin_addr.s_addr; + DEBUG("%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr)); + } + + if (ifindex) { + if (ioctl(fd, SIOCGIFINDEX, &ifr) != 0) { + bb_perror_msg("SIOCGIFINDEX failed"); + close(fd); + return -1; + } + DEBUG("adapter index %d", ifr.ifr_ifindex); + *ifindex = ifr.ifr_ifindex; + } + + if (arp) { + if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) { + bb_perror_msg("SIOCGIFHWADDR failed"); + close(fd); + return -1; + } + memcpy(arp, ifr.ifr_hwaddr.sa_data, 6); + DEBUG("adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x", + arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]); + } + + return 0; +} + + +int listen_socket(uint32_t ip, int port, char *inf) +{ + struct ifreq interface; + int fd; + struct sockaddr_in addr; + + DEBUG("Opening listen socket on 0x%08x:%d %s", ip, port, inf); + fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) { + bb_perror_msg("socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = ip; + + if (setsockopt_reuseaddr(fd) == -1) { + close(fd); + return -1; + } + if (setsockopt_broadcast(fd) == -1) { + close(fd); + return -1; + } + + strncpy(interface.ifr_name, inf, IFNAMSIZ); + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface)) < 0) { + close(fd); + return -1; + } + + if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) { + close(fd); + return -1; + } + + return fd; +} diff --git a/networking/udhcp/static_leases.c b/networking/udhcp/static_leases.c new file mode 100644 index 0000000..aabfb81 --- /dev/null +++ b/networking/udhcp/static_leases.c @@ -0,0 +1,99 @@ +/* vi: set sw=4 ts=4: */ +/* + * static_leases.c -- Couple of functions to assist with storing and + * retrieving data for static leases + * + * Wade Berrier <wberrier@myrealbox.com> September 2004 + * + */ + +#include "common.h" +#include "dhcpd.h" + + +/* Takes the address of the pointer to the static_leases linked list, + * Address to a 6 byte mac address + * Address to a 4 byte ip address */ +int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip) +{ + struct static_lease *cur; + struct static_lease *new_static_lease; + + /* Build new node */ + new_static_lease = xmalloc(sizeof(struct static_lease)); + new_static_lease->mac = mac; + new_static_lease->ip = ip; + new_static_lease->next = NULL; + + /* If it's the first node to be added... */ + if (*lease_struct == NULL) { + *lease_struct = new_static_lease; + } else { + cur = *lease_struct; + while (cur->next) { + cur = cur->next; + } + + cur->next = new_static_lease; + } + + return 1; +} + +/* Check to see if a mac has an associated static lease */ +uint32_t getIpByMac(struct static_lease *lease_struct, void *arg) +{ + uint32_t return_ip; + struct static_lease *cur = lease_struct; + uint8_t *mac = arg; + + return_ip = 0; + + while (cur) { + /* If the client has the correct mac */ + if (memcmp(cur->mac, mac, 6) == 0) { + return_ip = *(cur->ip); + } + + cur = cur->next; + } + + return return_ip; +} + +/* Check to see if an ip is reserved as a static ip */ +uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip) +{ + struct static_lease *cur = lease_struct; + + uint32_t return_val = 0; + + while (cur) { + /* If the client has the correct ip */ + if (*cur->ip == ip) + return_val = 1; + + cur = cur->next; + } + + return return_val; +} + +#if ENABLE_FEATURE_UDHCP_DEBUG +/* Print out static leases just to check what's going on */ +/* Takes the address of the pointer to the static_leases linked list */ +void printStaticLeases(struct static_lease **arg) +{ + /* Get a pointer to the linked list */ + struct static_lease *cur = *arg; + + while (cur) { + /* printf("PrintStaticLeases: Lease mac Address: %x\n", cur->mac); */ + printf("PrintStaticLeases: Lease mac Value: %x\n", *(cur->mac)); + /* printf("PrintStaticLeases: Lease ip Address: %x\n", cur->ip); */ + printf("PrintStaticLeases: Lease ip Value: %x\n", *(cur->ip)); + + cur = cur->next; + } +} +#endif |