diff options
Diffstat (limited to 'networking/udhcp')
-rw-r--r-- | networking/udhcp/Config.in | 122 | ||||
-rw-r--r-- | networking/udhcp/Kbuild | 25 | ||||
-rw-r--r-- | networking/udhcp/arpping.c | 116 | ||||
-rw-r--r-- | networking/udhcp/clientpacket.c | 271 | ||||
-rw-r--r-- | networking/udhcp/clientsocket.c | 108 | ||||
-rw-r--r-- | networking/udhcp/common.c | 11 | ||||
-rw-r--r-- | networking/udhcp/common.h | 110 | ||||
-rw-r--r-- | networking/udhcp/dhcpc.c | 646 | ||||
-rw-r--r-- | networking/udhcp/dhcpc.h | 56 | ||||
-rw-r--r-- | networking/udhcp/dhcpd.c | 272 | ||||
-rw-r--r-- | networking/udhcp/dhcpd.h | 125 | ||||
-rw-r--r-- | networking/udhcp/dhcprelay.c | 314 | ||||
-rw-r--r-- | networking/udhcp/domain_codec.c | 205 | ||||
-rw-r--r-- | networking/udhcp/dumpleases.c | 66 | ||||
-rw-r--r-- | networking/udhcp/files.c | 413 | ||||
-rw-r--r-- | networking/udhcp/leases.c | 149 | ||||
-rw-r--r-- | networking/udhcp/options.c | 234 | ||||
-rw-r--r-- | networking/udhcp/options.h | 121 | ||||
-rw-r--r-- | networking/udhcp/packet.c | 238 | ||||
-rw-r--r-- | networking/udhcp/script.c | 239 | ||||
-rw-r--r-- | networking/udhcp/serverpacket.c | 266 | ||||
-rw-r--r-- | networking/udhcp/signalpipe.c | 82 | ||||
-rw-r--r-- | networking/udhcp/socket.c | 111 | ||||
-rw-r--r-- | networking/udhcp/static_leases.c | 99 |
24 files changed, 4399 insertions, 0 deletions
diff --git a/networking/udhcp/Config.in b/networking/udhcp/Config.in new file mode 100644 index 0000000..d4b76e1 --- /dev/null +++ b/networking/udhcp/Config.in @@ -0,0 +1,122 @@ +# +# 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. + +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. + +config FEATURE_UDHCPD_WRITE_LEASES_EARLY + bool "Rewrite the lease file at every new acknowledge" + default n + depends on APP_UDHCPD + help + If selected, udhcpd will write a new file with leases every + time a new lease has been accepted, thus eliminating the need + to send SIGUSR1 for the initial writing or updating. Any timed + rewriting remains undisturbed + +config DHCPD_LEASES_FILE + string "Absolute path to lease file" + default "/var/lib/misc/udhcpd.leases" + depends on APP_UDHCPD + help + udhcpd stores addresses in a lease file. This is the absolute path + of the file. Normally it is safe to leave it untouched. + +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 + runs a script when a lease is obtained or lost. + +config FEATURE_UDHCPC_ARPING + bool "Verify that the offered address is free, using ARP ping" + default y + depends on APP_UDHCPC + help + If selected, udhcpc will send ARP probes and make sure + the offered address is really not in use by anyone. The client + will DHCPDECLINE the offer if the address is in use, + and restart the discover process. + +config FEATURE_UDHCP_PORT + bool "Enable '-P port' option for udhcpd and udhcpc" + default n + depends on APP_UDHCPD || APP_UDHCPC + help + At the cost of ~300 bytes, enables -P port option. + This feature is typically not needed. + +config 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. + +config FEATURE_UDHCP_RFC3397 + bool "Support for RFC3397 domain search (experimental)" + default n + depends on APP_UDHCPD || APP_UDHCPC + help + If selected, both client and server will support passing of domain + search lists via option 119, specified in RFC3397. + +config UDHCPC_DEFAULT_SCRIPT + string "Absolute path to config script" + default "/usr/share/udhcpc/default.script" + depends on APP_UDHCPC + help + This script is called after udhcpc receives an answer. See + examples/udhcp for a working example. Normally it is safe + to leave this untouched. + +config UDHCPC_SLACK_FOR_BUGGY_SERVERS + int "DHCP options slack buffer size" + default 80 + range 0 924 + depends on APP_UDHCPD || APP_UDHCPC + help + Some buggy DHCP servers send DHCP offer packets with option + field larger than we expect (which might also be considered a + buffer overflow attempt). These packets are normally discarded. + If circumstances beyond your control force you to support such + servers, this may help. The upper limit (924) makes dhcpc accept + even 1500 byte packets (maximum-sized ethernet packets). + + This option does not make dhcp[cd] emit non-standard + sized packets. + + Known buggy DHCP servers: + 3Com OfficeConnect Remote 812 ADSL Router: + seems to confuse maximum allowed UDP packet size with + maximum size of entire IP packet, and sends packets which are + 28 bytes too large. + Seednet (ISP) VDSL: sends packets 2 bytes too large. diff --git a/networking/udhcp/Kbuild b/networking/udhcp/Kbuild new file mode 100644 index 0000000..e938076 --- /dev/null +++ b/networking/udhcp/Kbuild @@ -0,0 +1,25 @@ +# 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 \ + signalpipe.o socket.o +lib-$(CONFIG_APP_UDHCPD) += common.o options.o packet.o \ + signalpipe.o socket.o + +lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \ + script.o + +UDHCPC_NEEDS_ARPING-$(CONFIG_FEATURE_UDHCPC_ARPING) = y +lib-$(UDHCPC_NEEDS_ARPING-y) += arpping.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 +lib-$(CONFIG_FEATURE_UDHCP_RFC3397) += domain_codec.o diff --git a/networking/udhcp/arpping.c b/networking/udhcp/arpping.c new file mode 100644 index 0000000..e0710dc --- /dev/null +++ b/networking/udhcp/arpping.c @@ -0,0 +1,116 @@ +/* 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 */ + uint8_t h_dest[6]; /* 00 destination ether addr */ + uint8_t h_source[6]; /* 06 source ether addr */ + uint16_t h_proto; /* 0c packet type ID field */ + + /* ARP packet */ + uint16_t htype; /* 0e hardware type (must be ARPHRD_ETHER) */ + uint16_t ptype; /* 10 protocol type (must be ETH_P_IP) */ + uint8_t hlen; /* 12 hardware address length (must be 6) */ + uint8_t plen; /* 13 protocol address length (must be 4) */ + uint16_t operation; /* 14 ARP opcode */ + uint8_t sHaddr[6]; /* 16 sender's hardware address */ + uint8_t sInaddr[4]; /* 1c sender's IP address */ + uint8_t tHaddr[6]; /* 20 target's hardware address */ + uint8_t tInaddr[4]; /* 26 target's IP address */ + uint8_t pad[18]; /* 2a pad for min. ethernet payload (60 bytes) */ +} PACKED; + +enum { + ARP_MSG_SIZE = 0x2a +}; + + +/* Returns 1 if no reply received */ + +int FAST_FUNC arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *interface) +{ + int timeout_ms; + struct pollfd pfd[1]; +#define s (pfd[0].fd) /* socket */ + int rv = 1; /* "no reply received" yet */ + struct sockaddr addr; /* for interface name */ + struct arpMsg arp; + + 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 enable bcast on raw socket"); + goto ret; + } + + /* send arp request */ + memset(&arp, 0, sizeof(arp)); + memset(arp.h_dest, 0xff, 6); /* MAC DA */ + memcpy(arp.h_source, from_mac, 6); /* MAC SA */ + arp.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */ + arp.htype = htons(ARPHRD_ETHER); /* hardware type */ + arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */ + arp.hlen = 6; /* hardware address length */ + arp.plen = 4; /* protocol address length */ + arp.operation = htons(ARPOP_REQUEST); /* ARP op code */ + memcpy(arp.sHaddr, from_mac, 6); /* source hardware address */ + memcpy(arp.sInaddr, &from_ip, sizeof(from_ip)); /* source IP address */ + /* tHaddr is zero-fiiled */ /* target hardware address */ + memcpy(arp.tInaddr, &test_ip, sizeof(test_ip)); /* target IP address */ + + memset(&addr, 0, sizeof(addr)); + safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data)); + if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) { + // TODO: error message? caller didn't expect us to fail, + // just returning 1 "no reply received" misleads it. + goto ret; + } + + /* wait for arp reply, and check it */ + timeout_ms = 2000; + do { + int r; + unsigned prevTime = monotonic_us(); + + pfd[0].events = POLLIN; + r = safe_poll(pfd, 1, timeout_ms); + if (r < 0) + break; + if (r) { + r = read(s, &arp, sizeof(arp)); + if (r < 0) + break; + if (r >= ARP_MSG_SIZE + && arp.operation == htons(ARPOP_REPLY) + /* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */ + /* && memcmp(arp.tHaddr, from_mac, 6) == 0 */ + && *((uint32_t *) arp.sInaddr) == test_ip + ) { + rv = 0; + break; + } + } + timeout_ms -= ((unsigned)monotonic_us() - prevTime) / 1000; + } while (timeout_ms > 0); + + ret: + close(s); + DEBUG("%srp reply received for this address", rv ? "No a" : "A"); + return rv; +} diff --git a/networking/udhcp/clientpacket.c b/networking/udhcp/clientpacket.c new file mode 100644 index 0000000..3f9522f --- /dev/null +++ b/networking/udhcp/clientpacket.c @@ -0,0 +1,271 @@ +/* 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 (defined(__GLIBC__) && __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 */ +uint32_t FAST_FUNC random_xid(void) +{ + static smallint initialized; + + if (!initialized) { + srand(monotonic_us()); + initialized = 1; + } + 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); + if ((type != DHCPDECLINE) && (type != DHCPRELEASE)) + 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_param_req_option(struct dhcpMessage *packet) +{ + uint8_t c; + int end = end_option(packet->options); + int i, len = 0; + + for (i = 0; (c = dhcp_options[i].code) != 0; i++) { + if (((dhcp_options[i].flags & OPTION_REQ) + && !client_config.no_default_options) + || (client_config.opt_mask[c >> 3] & (1 << (c & 7))) + ) { + packet->options[end + OPT_DATA + len] = c; + len++; + } + } + if (len) { + packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; + packet->options[end + OPT_LEN] = len; + packet->options[end + OPT_DATA + len] = DHCP_END; + } +} + +/* RFC 2131 + * 4.4.4 Use of broadcast and unicast + * + * The DHCP client broadcasts DHCPDISCOVER, DHCPREQUEST and DHCPINFORM + * messages, unless the client knows the address of a DHCP server. + * The client unicasts DHCPRELEASE messages to the server. Because + * the client is declining the use of the IP address supplied by the server, + * the client broadcasts DHCPDECLINE messages. + * + * When the DHCP client knows the address of a DHCP server, in either + * INIT or REBOOTING state, the client may use that address + * in the DHCPDISCOVER or DHCPREQUEST rather than the IP broadcast address. + * The client may also use unicast to send DHCPINFORM messages + * to a known DHCP server. If the client receives no response to DHCP + * messages sent to the IP address of a known DHCP server, the DHCP + * client reverts to using the IP broadcast address. + */ + +static int raw_bcast_from_client_config_ifindex(struct dhcpMessage *packet) +{ + return udhcp_send_raw_packet(packet, + /*src*/ INADDR_ANY, CLIENT_PORT, + /*dst*/ INADDR_BROADCAST, SERVER_PORT, MAC_BCAST_ADDR, + client_config.ifindex); +} + + +#if ENABLE_FEATURE_UDHCPC_ARPING +/* Broadcast a DHCP decline message */ +int FAST_FUNC send_decline(uint32_t xid, uint32_t server, uint32_t requested) +{ + struct dhcpMessage packet; + + init_packet(&packet, DHCPDECLINE); + packet.xid = xid; + add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); + add_simple_option(packet.options, DHCP_SERVER_ID, server); + + bb_info_msg("Sending decline..."); + + return raw_bcast_from_client_config_ifindex(&packet); +} +#endif + +/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ +int FAST_FUNC send_discover(uint32_t xid, uint32_t requested) +{ + struct dhcpMessage packet; + + init_packet(&packet, DHCPDISCOVER); + packet.xid = xid; + if (requested) + add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); + + /* Explicitly saying that we want RFC-compliant packets helps + * some buggy DHCP servers to NOT send bigger packets */ + add_simple_option(packet.options, DHCP_MAX_SIZE, htons(576)); + + add_param_req_option(&packet); + + bb_info_msg("Sending discover..."); + return raw_bcast_from_client_config_ifindex(&packet); +} + + +/* Broadcasts a DHCP request message */ +/* RFC 2131 3.1 paragraph 3: + * "The client _broadcasts_ a DHCPREQUEST message..." + */ +int FAST_FUNC send_select(uint32_t xid, uint32_t server, uint32_t 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_param_req_option(&packet); + + addr.s_addr = requested; + bb_info_msg("Sending select for %s...", inet_ntoa(addr)); + return raw_bcast_from_client_config_ifindex(&packet); +} + + +/* Unicasts or broadcasts a DHCP renew message */ +int FAST_FUNC send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) +{ + struct dhcpMessage packet; + + init_packet(&packet, DHCPREQUEST); + packet.xid = xid; + packet.ciaddr = ciaddr; + + add_param_req_option(&packet); + bb_info_msg("Sending renew..."); + if (server) + return udhcp_send_kernel_packet(&packet, + ciaddr, CLIENT_PORT, + server, SERVER_PORT); + + return raw_bcast_from_client_config_ifindex(&packet); +} + + +/* Unicasts a DHCP release message */ +int FAST_FUNC send_release(uint32_t server, uint32_t ciaddr) +{ + struct dhcpMessage packet; + + init_packet(&packet, DHCPRELEASE); + packet.xid = random_xid(); + packet.ciaddr = ciaddr; + + add_simple_option(packet.options, DHCP_SERVER_ID, server); + + bb_info_msg("Sending release..."); + return udhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); +} + + +/* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */ +int FAST_FUNC udhcp_recv_raw_packet(struct dhcpMessage *payload, int fd) +{ + int bytes; + struct udp_dhcp_packet packet; + uint16_t check; + + memset(&packet, 0, sizeof(packet)); + bytes = safe_read(fd, &packet, sizeof(packet)); + if (bytes < 0) { + DEBUG("Cannot read on raw listening socket - ignoring"); + /* NB: possible down interface, etc. Caller should pause. */ + return bytes; /* returns -1 */ + } + + if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp))) { + DEBUG("Packet is too short, ignoring"); + return -2; + } + + if (bytes < ntohs(packet.ip.tot_len)) { + /* packet is bigger than sizeof(packet), we did partial read */ + DEBUG("Oversized packet, ignoring"); + 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(packet) - can't happen */ + || ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip)) + ) { + DEBUG("Unrelated/bogus packet"); + return -2; + } + + /* verify 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 -2; + } + + /* verify UDP checksum. IP header has to be modified for this */ + memset(&packet.ip, 0, offsetof(struct iphdr, protocol)); + /* ip.xx fields which are not memset: protocol, check, saddr, daddr */ + packet.ip.tot_len = packet.udp.len; /* yes, this is needed */ + check = packet.udp.check; + packet.udp.check = 0; + 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 (payload->cookie != htonl(DHCP_MAGIC)) { + bb_error_msg("received bogus message (bad magic), ignoring"); + return -2; + } + DEBUG("Got valid DHCP packet"); + 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..1dcc105 --- /dev/null +++ b/networking/udhcp/clientsocket.c @@ -0,0 +1,108 @@ +/* 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> +#include <asm/types.h> +#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION) +#include <netpacket/packet.h> +#include <net/ethernet.h> +#else +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#endif +#include <linux/filter.h> + +#include "common.h" +#include "dhcpd.h" +#include "dhcpc.h" + +int FAST_FUNC udhcp_raw_socket(int ifindex) +{ + int fd; + struct sockaddr_ll sock; + + /* + * Comment: + * + * I've selected not to see LL header, so BPF doesn't see it, too. + * The filter may also pass non-IP and non-ARP packets, but we do + * a more complete check when receiving the message in userspace. + * + * and filter shamelessly stolen from: + * + * http://www.flamewarmaster.de/software/dhcpclient/ + * + * There are a few other interesting ideas on that page (look under + * "Motivation"). Use of netlink events is most interesting. Think + * of various network servers listening for events and reconfiguring. + * That would obsolete sending HUP signals and/or make use of restarts. + * + * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>. + * License: GPL v2. + * + * TODO: make conditional? + */ +#define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68) + static const struct sock_filter filter_instr[] = { + /* check for udp */ + BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0), /* L5, L1, is UDP? */ + /* ugly check for arp on ethernet-like and IPv4 */ + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4), /* L3, L4 */ + /* skip IP header */ + BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */ + /* check udp source and destination ports */ + BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1), /* L3, L4 */ + /* returns */ + BPF_STMT(BPF_RET|BPF_K, 0x0fffffff ), /* L3: pass */ + BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */ + }; + static const struct sock_fprog filter_prog = { + .len = sizeof(filter_instr) / sizeof(filter_instr[0]), + /* casting const away: */ + .filter = (struct sock_filter *) filter_instr, + }; + + DEBUG("opening raw socket on ifindex %d", ifindex); + + fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + DEBUG("got raw socket fd %d", fd); + + if (SERVER_PORT == 67 && CLIENT_PORT == 68) { + /* Use only if standard ports are in use */ + /* Ignoring error (kernel may lack support for this) */ + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, + sizeof(filter_prog)) >= 0) + DEBUG("attached filter to raw socket fd %d", fd); + } + + sock.sll_family = AF_PACKET; + sock.sll_protocol = htons(ETH_P_IP); + sock.sll_ifindex = ifindex; + xbind(fd, (struct sockaddr *) &sock, sizeof(sock)); + DEBUG("bound to raw socket fd %d", fd); + + return fd; +} diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c new file mode 100644 index 0000000..a47bbaf --- /dev/null +++ b/networking/udhcp/common.c @@ -0,0 +1,11 @@ +/* vi: set sw=4 ts=4: */ +/* common.c + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "common.h" + +const uint8_t MAC_BCAST_ADDR[6] ALIGN2 = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h new file mode 100644 index 0000000..15f0d9a --- /dev/null +++ b/networking/udhcp/common.h @@ -0,0 +1,110 @@ +/* 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 "libbb.h" +#include <netinet/udp.h> +#include <netinet/ip.h> + +#if __GNUC_PREREQ(4,1) +# pragma GCC visibility push(hidden) +#endif + +#define DEFAULT_SCRIPT CONFIG_UDHCPC_DEFAULT_SCRIPT + +extern const uint8_t MAC_BCAST_ADDR[6]; /* six all-ones */ + +/*** packet.h ***/ + +#define DHCP_OPTIONS_BUFSIZE 308 + +struct dhcpMessage { + uint8_t op; /* 1 = BOOTREQUEST, 2 = BOOTREPLY */ + uint8_t htype; /* hardware address type. 1 = 10mb ethernet */ + uint8_t hlen; /* hardware address length */ + uint8_t hops; /* used by relay agents only */ + uint32_t xid; /* unique id */ + uint16_t secs; /* elapsed since client began acquisition/renewal */ + uint16_t flags; /* only one flag so far: */ +#define BROADCAST_FLAG 0x8000 /* "I need broadcast replies" */ + uint32_t ciaddr; /* client IP (if client is in BOUND, RENEW or REBINDING state) */ + uint32_t yiaddr; /* 'your' (client) IP address */ + uint32_t siaddr; /* IP address of next server to use in bootstrap, + * returned in DHCPOFFER, DHCPACK by server */ + uint32_t giaddr; /* relay agent IP address */ + uint8_t chaddr[16];/* link-layer client hardware address (MAC) */ + uint8_t sname[64]; /* server host name (ASCIZ) */ + uint8_t file[128]; /* boot file name (ASCIZ) */ + uint32_t cookie; /* fixed first four option bytes (99,130,83,99 dec) */ + uint8_t options[DHCP_OPTIONS_BUFSIZE + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS]; +} PACKED; + +struct udp_dhcp_packet { + struct iphdr ip; + struct udphdr udp; + struct dhcpMessage data; +} PACKED; + +/* Let's see whether compiler understood us right */ +struct BUG_bad_sizeof_struct_udp_dhcp_packet { + char BUG_bad_sizeof_struct_udp_dhcp_packet + [(sizeof(struct udp_dhcp_packet) != 576 + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS) ? -1 : 1]; +}; + +uint16_t udhcp_checksum(void *addr, int count) FAST_FUNC; + +void udhcp_init_header(struct dhcpMessage *packet, char type) FAST_FUNC; + +/*int udhcp_recv_raw_packet(struct dhcpMessage *payload, int fd); - in dhcpc.h */ +int udhcp_recv_kernel_packet(struct dhcpMessage *packet, int fd) FAST_FUNC; + +int udhcp_send_raw_packet(struct dhcpMessage *payload, + uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port, const uint8_t *dest_arp, + int ifindex) FAST_FUNC; + +int udhcp_send_kernel_packet(struct dhcpMessage *payload, + uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port) FAST_FUNC; + + +/**/ + +void udhcp_run_script(struct dhcpMessage *packet, const char *name) FAST_FUNC; + +// Still need to clean these up... + +/* 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 + +void udhcp_sp_setup(void) FAST_FUNC; +int udhcp_sp_fd_set(fd_set *rfds, int extra_fd) FAST_FUNC; +int udhcp_sp_read(const fd_set *rfds) FAST_FUNC; +int udhcp_read_interface(const char *interface, int *ifindex, uint32_t *addr, uint8_t *arp) FAST_FUNC; +int udhcp_raw_socket(int ifindex) FAST_FUNC; +int udhcp_listen_socket(/*uint32_t ip,*/ int port, const char *inf) FAST_FUNC; +/* Returns 1 if no reply received */ +int arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *interface) FAST_FUNC; + +#if ENABLE_UDHCP_DEBUG +# define DEBUG(str, args...) bb_info_msg("### " str, ## args) +#else +# define DEBUG(str, args...) do {;} while (0) +#endif + +#if __GNUC_PREREQ(4,1) +# pragma GCC visibility pop +#endif + +#endif diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c new file mode 100644 index 0000000..2d48980 --- /dev/null +++ b/networking/udhcp/dhcpc.c @@ -0,0 +1,646 @@ +/* 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 <syslog.h> + +/* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */ +#define WANT_PIDFILE 1 +#include "common.h" +#include "dhcpd.h" +#include "dhcpc.h" +#include "options.h" + + +static int sockfd = -1; + +#define LISTEN_NONE 0 +#define LISTEN_KERNEL 1 +#define LISTEN_RAW 2 +static smallint listen_mode; + +#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 +static smallint state; + +/* struct client_config_t client_config is in bb_common_bufsiz1 */ + + +/* just a little helper */ +static void change_listen_mode(int new_mode) +{ + DEBUG("entering %s listen mode", + new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none"); + if (sockfd >= 0) { + close(sockfd); + sockfd = -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_listen_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_listen_mode(LISTEN_RAW); + state = INIT_SELECTING; + break; + case INIT_SELECTING: + break; + } +} + + +/* perform a release */ +static void perform_release(uint32_t requested_ip, uint32_t server_addr) +{ + char buffer[sizeof("255.255.255.255")]; + struct in_addr temp_addr; + + /* send release packet */ + if (state == BOUND || state == RENEWING || state == REBINDING) { + temp_addr.s_addr = server_addr; + strcpy(buffer, 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_listen_mode(LISTEN_NONE); + state = RELEASED; +} + + +#if BB_MMU +static void client_background(void) +{ + bb_daemonize(0); + logmode &= ~LOGMODE_STDIO; + /* rewrite pidfile, as our pid is different now */ + write_pidfile(client_config.pidfile); +} +#endif + + +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) MAIN_EXTERNALLY_VISIBLE; +int udhcpc_main(int argc UNUSED_PARAM, char **argv) +{ + uint8_t *temp, *message; + char *str_c, *str_V, *str_h, *str_F, *str_r; + USE_FEATURE_UDHCP_PORT(char *str_P;) + llist_t *list_O = NULL; + int tryagain_timeout = 20; + int discover_timeout = 3; + int discover_retries = 3; + uint32_t server_addr = server_addr; /* for compiler */ + uint32_t requested_ip = 0; + uint32_t xid = 0; + uint32_t lease_seconds = 0; /* can be given as 32-bit quantity */ + int packet_num; + int timeout; /* must be signed */ + unsigned already_waited_sec; + unsigned opt; + int max_fd; + int retval; + struct timeval tv; + struct dhcpMessage packet; + fd_set rfds; + +#if ENABLE_GETOPT_LONG + static const char udhcpc_longopts[] ALIGN1 = + "clientid\0" Required_argument "c" + "clientid-none\0" No_argument "C" + "vendorclass\0" Required_argument "V" + "hostname\0" Required_argument "H" + "fqdn\0" Required_argument "F" + "interface\0" Required_argument "i" + "now\0" No_argument "n" + "pidfile\0" Required_argument "p" + "quit\0" No_argument "q" + "release\0" No_argument "R" + "request\0" Required_argument "r" + "script\0" Required_argument "s" + "timeout\0" Required_argument "T" + "version\0" No_argument "v" + "retries\0" Required_argument "t" + "tryagain\0" Required_argument "A" + "syslog\0" No_argument "S" + "request-option\0" Required_argument "O" + "no-default-options\0" No_argument "o" + "foreground\0" No_argument "f" + "background\0" No_argument "b" + USE_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a") + USE_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P") + ; +#endif + enum { + OPT_c = 1 << 0, + OPT_C = 1 << 1, + OPT_V = 1 << 2, + OPT_H = 1 << 3, + OPT_h = 1 << 4, + OPT_F = 1 << 5, + OPT_i = 1 << 6, + OPT_n = 1 << 7, + OPT_p = 1 << 8, + OPT_q = 1 << 9, + OPT_R = 1 << 10, + OPT_r = 1 << 11, + OPT_s = 1 << 12, + OPT_T = 1 << 13, + OPT_t = 1 << 14, + OPT_v = 1 << 15, + OPT_S = 1 << 16, + OPT_A = 1 << 17, + OPT_O = 1 << 18, + OPT_o = 1 << 19, + OPT_f = 1 << 20, +/* The rest has variable bit positions, need to be clever */ + OPTBIT_f = 20, + USE_FOR_MMU( OPTBIT_b,) + USE_FEATURE_UDHCPC_ARPING(OPTBIT_a,) + USE_FEATURE_UDHCP_PORT( OPTBIT_P,) + USE_FOR_MMU( OPT_b = 1 << OPTBIT_b,) + USE_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,) + USE_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,) + }; + + /* Default options. */ + USE_FEATURE_UDHCP_PORT(SERVER_PORT = 67;) + USE_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;) + client_config.interface = "eth0"; + client_config.script = DEFAULT_SCRIPT; + + /* Parse command line */ + /* Cc: mutually exclusive; O: list; -T,-t,-A take numeric param */ + opt_complementary = "c--C:C--c:O::T+:t+:A+"; + USE_GETOPT_LONG(applet_long_options = udhcpc_longopts;) + opt = getopt32(argv, "c:CV:H:h:F:i:np:qRr:s:T:t:vSA:O:of" + USE_FOR_MMU("b") + USE_FEATURE_UDHCPC_ARPING("a") + USE_FEATURE_UDHCP_PORT("P:") + , &str_c, &str_V, &str_h, &str_h, &str_F + , &client_config.interface, &client_config.pidfile, &str_r /* i,p */ + , &client_config.script /* s */ + , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */ + , &list_O + USE_FEATURE_UDHCP_PORT(, &str_P) + ); + if (opt & OPT_c) + client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, str_c, 0); + if (opt & OPT_V) + client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0); + if (opt & (OPT_h|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<3>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_r) + requested_ip = inet_addr(str_r); + if (opt & OPT_v) { + puts("version "BB_VER); + return 0; + } +#if ENABLE_FEATURE_UDHCP_PORT + if (opt & OPT_P) { + CLIENT_PORT = xatou16(str_P); + SERVER_PORT = CLIENT_PORT - 1; + } +#endif + if (opt & OPT_o) + client_config.no_default_options = 1; + while (list_O) { + char *optstr = llist_pop(&list_O); + int n = index_in_strings(dhcp_option_strings, optstr); + if (n < 0) + bb_error_msg_and_die("unknown option '%s'", optstr); + n = dhcp_options[n].code; + client_config.opt_mask[n >> 3] |= 1 << (n & 7); + } + + if (udhcp_read_interface(client_config.interface, &client_config.ifindex, + NULL, client_config.arp)) + return 1; +#if !BB_MMU + /* on NOMMU reexec (i.e., background) early */ + if (!(opt & OPT_f)) { + bb_daemonize_or_rexec(0 /* flags */, argv); + logmode = 0; + } +#endif + if (opt & OPT_S) { + openlog(applet_name, LOG_PID, LOG_LOCAL0); + logmode |= LOGMODE_SYSLOG; + } + + /* Make sure fd 0,1,2 are open */ + bb_sanitize_stdio(); + /* Equivalent of doing a fflush after every \n */ + setlinebuf(stdout); + + /* Create pidfile */ + write_pidfile(client_config.pidfile); + + /* Goes to stdout (unless NOMMU) and possibly syslog */ + bb_info_msg("%s (v"BB_VER") started", applet_name); + + /* if not set, and not suppressed, setup the default client ID */ + if (!client_config.clientid && !(opt & OPT_C)) { + 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_listen_mode(LISTEN_RAW); + packet_num = 0; + timeout = 0; + already_waited_sec = 0; + + /* Main event loop. select() waits on signal pipe and possibly + * on sockfd. + * "continue" statements in code below jump to the top of the loop. + */ + for (;;) { + unsigned timestamp_before_wait; + + if (listen_mode != LISTEN_NONE && sockfd < 0) { + if (listen_mode == LISTEN_KERNEL) + sockfd = udhcp_listen_socket(/*INADDR_ANY,*/ CLIENT_PORT, client_config.interface); + else + sockfd = udhcp_raw_socket(client_config.ifindex); + } + max_fd = udhcp_sp_fd_set(&rfds, sockfd); + + tv.tv_sec = timeout - already_waited_sec; + tv.tv_usec = 0; + retval = 0; /* If we already timed out, fall through, else... */ + if (tv.tv_sec > 0) { + timestamp_before_wait = (unsigned)monotonic_sec(); + DEBUG("Waiting on select..."); + retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); + if (retval < 0) { + /* EINTR? A signal was caught, don't panic */ + if (errno == EINTR) + continue; + /* Else: an error occured, panic! */ + bb_perror_msg_and_die("select"); + } + } + + /* If timeout dropped to zero, time to become active: + * resend discover/renew/whatever + */ + if (retval == 0) { + /* We will restart the wait in any case */ + already_waited_sec = 0; + + switch (state) { + case INIT_SELECTING: + if (packet_num < discover_retries) { + if (packet_num == 0) + xid = random_xid(); + + send_discover(xid, requested_ip); /* broadcast */ + + timeout = discover_timeout; + packet_num++; + continue; + } + leasefail: + udhcp_run_script(NULL, "leasefail"); +#if BB_MMU /* -b is not supported on NOMMU */ + if (opt & OPT_b) { /* background if no lease */ + bb_info_msg("No lease, forking to background"); + client_background(); + /* do not background again! */ + opt = ((opt & ~OPT_b) | OPT_f); + } else +#endif + if (opt & OPT_n) { /* abort if no lease */ + bb_info_msg("No lease, failing"); + retval = 1; + goto ret; + } + /* wait before trying again */ + timeout = tryagain_timeout; + packet_num = 0; + continue; + case RENEW_REQUESTED: + case REQUESTING: + if (packet_num < discover_retries) { + /* send request packet */ + if (state == RENEW_REQUESTED) /* unicast */ + send_renew(xid, server_addr, requested_ip); + else /* broadcast */ + send_select(xid, server_addr, requested_ip); + + timeout = discover_timeout; + packet_num++; + continue; + } + /* timed out, go back to init state */ + if (state == RENEW_REQUESTED) + udhcp_run_script(NULL, "deconfig"); + change_listen_mode(LISTEN_RAW); + /* "discover...select...discover..." loops + * were seen in the wild. Treat them similarly + * to "no response to discover" case */ + if (state == REQUESTING) { + state = INIT_SELECTING; + goto leasefail; + } + state = INIT_SELECTING; + timeout = 0; + packet_num = 0; + continue; + case BOUND: + /* Half of the lease passed, time to enter renewing state */ + change_listen_mode(LISTEN_KERNEL); + DEBUG("Entering renew state"); + state = RENEWING; + /* fall right through */ + case RENEWING: + if (timeout > 60) { + /* send a request packet */ + send_renew(xid, server_addr, requested_ip); /* unicast */ + timeout >>= 1; + continue; + } + /* Timed out, enter rebinding state */ + DEBUG("Entering rebinding state"); + state = REBINDING; + /* fall right through */ + case REBINDING: + /* Lease is *really* about to run out, + * try to find DHCP server using broadcast */ + if (timeout > 0) { + /* send a request packet */ + send_renew(xid, 0 /* INADDR_ANY*/, requested_ip); /* broadcast */ + timeout >>= 1; + continue; + } + /* Timed out, enter init state */ + bb_info_msg("Lease lost, entering init state"); + udhcp_run_script(NULL, "deconfig"); + change_listen_mode(LISTEN_RAW); + state = INIT_SELECTING; + /*timeout = 0; - already is */ + packet_num = 0; + continue; + /* case RELEASED: */ + } + /* yah, I know, *you* say it would never happen */ + timeout = INT_MAX; + continue; /* back to main loop */ + } + + /* select() didn't timeout, something did happen. */ + /* Is it a packet? */ + if (listen_mode != LISTEN_NONE && FD_ISSET(sockfd, &rfds)) { + int len; + /* A packet is ready, read it */ + + if (listen_mode == LISTEN_KERNEL) + len = udhcp_recv_kernel_packet(&packet, sockfd); + else + len = udhcp_recv_raw_packet(&packet, sockfd); + if (len == -1) { /* error is severe, reopen socket */ + DEBUG("error on read, %s, reopening socket", strerror(errno)); + sleep(discover_timeout); /* 3 seconds by default */ + change_listen_mode(listen_mode); /* just close and reopen */ + } + /* If this packet will turn out to be unrelated/bogus, + * we will go back and wait for next one. + * Be sure timeout is properly decreased. */ + already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; + if (len < 0) + continue; + + if (packet.xid != xid) { + DEBUG("Ignoring xid %x (our xid is %x)", + (unsigned)packet.xid, (unsigned)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; + } + + message = get_option(&packet, DHCP_MESSAGE_TYPE); + if (message == NULL) { + bb_error_msg("cannot get message type from packet - ignoring"); + continue; + } + + switch (state) { + case INIT_SELECTING: + /* Must be a DHCPOFFER to one of our xid's */ + if (*message == DHCPOFFER) { + /* TODO: why we don't just fetch server's IP from IP header? */ + temp = get_option(&packet, DHCP_SERVER_ID); + if (!temp) { + bb_error_msg("no server ID in message"); + continue; + /* still selecting - this server looks bad */ + } + /* it IS unaligned sometimes, don't "optimize" */ + server_addr = get_unaligned_u32p((uint32_t*)temp); + xid = packet.xid; + requested_ip = packet.yiaddr; + + /* enter requesting state */ + state = REQUESTING; + timeout = 0; + packet_num = 0; + already_waited_sec = 0; + } + continue; + 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_seconds = 60 * 60; + } else { + /* it IS unaligned sometimes, don't "optimize" */ + lease_seconds = get_unaligned_u32p((uint32_t*)temp); + lease_seconds = ntohl(lease_seconds); + lease_seconds &= 0x0fffffff; /* paranoia: must not be prone to overflows */ + if (lease_seconds < 10) /* and not too small */ + lease_seconds = 10; + } +#if ENABLE_FEATURE_UDHCPC_ARPING + if (opt & OPT_a) { +/* RFC 2131 3.1 paragraph 5: + * "The client receives the DHCPACK message with configuration + * parameters. The client SHOULD perform a final check on the + * parameters (e.g., ARP for allocated network address), and notes + * the duration of the lease specified in the DHCPACK message. At this + * point, the client is configured. If the client detects that the + * address is already in use (e.g., through the use of ARP), + * the client MUST send a DHCPDECLINE message to the server and restarts + * the configuration process..." */ + if (!arpping(packet.yiaddr, + (uint32_t) 0, + client_config.arp, + client_config.interface) + ) { + bb_info_msg("offered address is in use " + "(got ARP reply), declining"); + send_decline(xid, server_addr, packet.yiaddr); + + if (state != REQUESTING) + udhcp_run_script(NULL, "deconfig"); + change_listen_mode(LISTEN_RAW); + state = INIT_SELECTING; + requested_ip = 0; + timeout = tryagain_timeout; + packet_num = 0; + already_waited_sec = 0; + continue; /* back to main loop */ + } + } +#endif + /* enter bound state */ + timeout = lease_seconds / 2; + { + struct in_addr temp_addr; + temp_addr.s_addr = packet.yiaddr; + bb_info_msg("Lease of %s obtained, lease time %u", + inet_ntoa(temp_addr), (unsigned)lease_seconds); + } + requested_ip = packet.yiaddr; + udhcp_run_script(&packet, + ((state == RENEWING || state == REBINDING) ? "renew" : "bound")); + + state = BOUND; + change_listen_mode(LISTEN_NONE); + if (opt & OPT_q) { /* quit after lease */ + if (opt & OPT_R) /* release on quit */ + perform_release(requested_ip, server_addr); + goto ret0; + } +#if BB_MMU /* NOMMU case backgrounded earlier */ + if (!(opt & OPT_f)) { + client_background(); + /* do not background again! */ + opt = ((opt & ~OPT_b) | OPT_f); + } +#endif + already_waited_sec = 0; + continue; /* back to main loop */ + } + 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"); + change_listen_mode(LISTEN_RAW); + sleep(3); /* avoid excessive network traffic */ + state = INIT_SELECTING; + requested_ip = 0; + timeout = 0; + packet_num = 0; + already_waited_sec = 0; + } + continue; + /* case BOUND, RELEASED: - ignore all packets */ + } + continue; /* back to main loop */ + } + + /* select() didn't timeout, something did happen. + * But it wasn't a packet. It's a signal pipe then. */ + { + int signo = udhcp_sp_read(&rfds); + switch (signo) { + case SIGUSR1: + perform_renew(); + /* start things over */ + packet_num = 0; + /* Kill any timeouts because the user wants this to hurry along */ + timeout = 0; + break; + case SIGUSR2: + perform_release(requested_ip, server_addr); + timeout = INT_MAX; + break; + case SIGTERM: + bb_info_msg("Received SIGTERM"); + if (opt & OPT_R) /* release on quit */ + perform_release(requested_ip, server_addr); + goto ret0; + } + } + } /* for (;;) - main loop ends */ + + ret0: + retval = 0; + ret: + /*if (client_config.pidfile) - remove_pidfile has its own check */ + remove_pidfile(client_config.pidfile); + return retval; +} diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h new file mode 100644 index 0000000..a934849 --- /dev/null +++ b/networking/udhcp/dhcpc.h @@ -0,0 +1,56 @@ +/* vi: set sw=4 ts=4: */ +/* dhcpc.h */ + +#ifndef _DHCPC_H +#define _DHCPC_H + +#if __GNUC_PREREQ(4,1) +# pragma GCC visibility push(hidden) +#endif + +struct client_config_t { + uint8_t arp[6]; /* Our arp address */ + /* TODO: combine flag fields into single "unsigned opt" */ + /* (can be set directly to the result of getopt32) */ + char no_default_options; /* Do not include default optins in request */ + USE_FEATURE_UDHCP_PORT(uint16_t port;) + int ifindex; /* Index number of the interface to use */ + uint8_t opt_mask[256 / 8]; /* Bitmask of options to send (-O option) */ + const char *interface; /* The name of the interface to use */ + char *pidfile; /* Optionally store the process ID */ + const 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 */ +}; + +/* server_config sits in 1st half of bb_common_bufsiz1 */ +#define client_config (*(struct client_config_t*)(&bb_common_bufsiz1[COMMON_BUFSIZE / 2])) + +#if ENABLE_FEATURE_UDHCP_PORT +#define CLIENT_PORT (client_config.port) +#else +#define CLIENT_PORT 68 +#endif + + +/*** clientpacket.h ***/ + +uint32_t random_xid(void) FAST_FUNC; +int send_discover(uint32_t xid, uint32_t requested) FAST_FUNC; +int send_select(uint32_t xid, uint32_t server, uint32_t requested) FAST_FUNC; +#if ENABLE_FEATURE_UDHCPC_ARPING +int send_decline(uint32_t xid, uint32_t server, uint32_t requested) FAST_FUNC; +#endif +int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) FAST_FUNC; +int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) FAST_FUNC; +int send_release(uint32_t server, uint32_t ciaddr) FAST_FUNC; + +int udhcp_recv_raw_packet(struct dhcpMessage *payload, int fd) FAST_FUNC; + +#if __GNUC_PREREQ(4,1) +# pragma GCC visibility pop +#endif + +#endif diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c new file mode 100644 index 0000000..b512c45 --- /dev/null +++ b/networking/udhcp/dhcpd.c @@ -0,0 +1,272 @@ +/* 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 <syslog.h> +#include "common.h" +#include "dhcpc.h" +#include "dhcpd.h" +#include "options.h" + + +/* globals */ +struct dhcpOfferedAddr *leases; +/* struct server_config_t server_config is in bb_common_bufsiz1 */ + + +int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int udhcpd_main(int argc UNUSED_PARAM, 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 timeout_end; + unsigned num_ips; + unsigned opt; + struct option_set *option; + struct dhcpOfferedAddr *lease, static_lease; + USE_FEATURE_UDHCP_PORT(char *str_P;) + +#if ENABLE_FEATURE_UDHCP_PORT + SERVER_PORT = 67; + CLIENT_PORT = 68; +#endif + + opt = getopt32(argv, "fS" USE_FEATURE_UDHCP_PORT("P:", &str_P)); + argv += optind; + + if (!(opt & 1)) { /* no -f */ + bb_daemonize_or_rexec(0, argv); + logmode &= ~LOGMODE_STDIO; + } + + if (opt & 2) { /* -S */ + openlog(applet_name, LOG_PID, LOG_LOCAL0); + logmode |= LOGMODE_SYSLOG; + } +#if ENABLE_FEATURE_UDHCP_PORT + if (opt & 4) { /* -P */ + SERVER_PORT = xatou16(str_P); + CLIENT_PORT = SERVER_PORT + 1; + } +#endif + /* Would rather not do read_config before daemonization - + * otherwise NOMMU machines will parse config twice */ + read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE); + + /* Make sure fd 0,1,2 are open */ + bb_sanitize_stdio(); + /* Equivalent of doing a fflush after every \n */ + setlinebuf(stdout); + + /* Create pidfile */ + write_pidfile(server_config.pidfile); + /* if (!..) bb_perror_msg("cannot create pidfile %s", pidfile); */ + + bb_info_msg("%s (v"BB_VER") started", applet_name); + + option = find_option(server_config.options, DHCP_LEASE_TIME); + server_config.lease = LEASE_TIME; + if (option) { + memcpy(&server_config.lease, option->data + 2, 4); + server_config.lease = ntohl(server_config.lease); + } + + /* Sanity check */ + num_ips = server_config.end_ip - server_config.start_ip + 1; + if (server_config.max_leases > num_ips) { + bb_error_msg("max_leases=%u is too big, setting to %u", + (unsigned)server_config.max_leases, num_ips); + server_config.max_leases = num_ips; + } + + leases = xzalloc(server_config.max_leases * sizeof(*leases)); + read_leases(server_config.lease_file); + + if (udhcp_read_interface(server_config.interface, &server_config.ifindex, + &server_config.server, server_config.arp)) { + retval = 1; + goto ret; + } + + /* Setup the signal pipe */ + udhcp_sp_setup(); + + timeout_end = monotonic_sec() + server_config.auto_time; + while (1) { /* loop until universe collapses */ + + if (server_socket < 0) { + server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, + server_config.interface); + } + + max_sock = udhcp_sp_fd_set(&rfds, server_socket); + if (server_config.auto_time) { + tv.tv_sec = timeout_end - monotonic_sec(); + tv.tv_usec = 0; + } + retval = 0; + if (!server_config.auto_time || tv.tv_sec > 0) { + retval = select(max_sock + 1, &rfds, NULL, NULL, + server_config.auto_time ? &tv : NULL); + } + if (retval == 0) { + write_leases(); + timeout_end = monotonic_sec() + server_config.auto_time; + continue; + } + 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 = monotonic_sec() + server_config.auto_time; + continue; + case SIGTERM: + bb_info_msg("Received a SIGTERM"); + goto ret0; + case 0: break; /* no signal */ + default: continue; /* signal or error (probably EINTR) */ + } + + bytes = udhcp_recv_kernel_packet(&packet, server_socket); /* this waits for a packet - idle */ + if (bytes < 0) { + if (bytes == -1 && errno != EINTR) { + DEBUG("error on read, %s, reopening socket", strerror(errno)); + close(server_socket); + server_socket = -1; + } + continue; + } + + state = get_option(&packet, DHCP_MESSAGE_TYPE); + if (state == 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 (send_offer(&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 + ) { + send_ACK(&packet, lease->yiaddr); + } + } else if (requested) { + /* INIT-REBOOT State */ + if (lease->yiaddr == requested_align) + send_ACK(&packet, lease->yiaddr); + else + send_NAK(&packet); + } else if (lease->yiaddr == packet.ciaddr) { + /* RENEWING or REBINDING State */ + send_ACK(&packet, lease->yiaddr); + } else { /* don't know what to do!!!! */ + send_NAK(&packet); + } + + /* what to do if we have no record of the client */ + } else if (server_id) { + /* SELECTING State */ + + } else if (requested) { + /* INIT-REBOOT State */ + lease = find_lease_by_yiaddr(requested_align); + if (lease) { + if (lease_expired(lease)) { + /* probably best if we drop this lease */ + memset(lease->chaddr, 0, 16); + /* make some contention for this address */ + } else + send_NAK(&packet); + } else { + uint32_t r = ntohl(requested_align); + if (r < server_config.start_ip + || r > server_config.end_ip + ) { + send_NAK(&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]); + } + } + ret0: + retval = 0; + ret: + /*if (server_config.pidfile) - server_config.pidfile is never NULL */ + remove_pidfile(server_config.pidfile); + return retval; +} diff --git a/networking/udhcp/dhcpd.h b/networking/udhcp/dhcpd.h new file mode 100644 index 0000000..2d97528 --- /dev/null +++ b/networking/udhcp/dhcpd.h @@ -0,0 +1,125 @@ +/* vi: set sw=4 ts=4: */ +/* dhcpd.h */ + +#ifndef _DHCPD_H +#define _DHCPD_H + +#if __GNUC_PREREQ(4,1) +# pragma GCC visibility push(hidden) +#endif + +/************************************/ +/* 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 CONFIG_DHCPD_LEASES_FILE + +/* where to find the DHCP server configuration file */ +#define DHCPD_CONF_FILE "/etc/udhcpd.conf" + +struct option_set { + uint8_t *data; + struct option_set *next; +}; + +struct static_lease { + struct static_lease *next; + uint8_t *mac; + uint32_t *ip; +}; + +struct server_config_t { + uint32_t server; /* Our IP, in network order */ +#if ENABLE_FEATURE_UDHCP_PORT + uint16_t port; +#endif + /* start,end are in host order: we need to compare start <= ip <= end */ + uint32_t start_ip; /* Start address of leases, in host order */ + uint32_t end_ip; /* End of leases, in host 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 */ + char remaining; /* should the lease file be interpreted as lease time remaining, or + * as the time the lease expires */ + uint32_t lease; /* lease time in seconds (host order) */ + uint32_t max_leases; /* maximum number of leases (including reserved address) */ + uint32_t auto_time; /* how long should udhcpd wait before writing a config file. + * if this is zero, it will only write one on SIGUSR1 */ + uint32_t decline_time; /* how long an address is reserved if a client returns a + * decline message */ + uint32_t conflict_time; /* how long an arp conflict offender is leased for */ + uint32_t offer_time; /* how long an offered address is reserved */ + uint32_t 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 */ +}; + +#define server_config (*(struct server_config_t*)&bb_common_bufsiz1) +/* client_config sits in 2nd half of bb_common_bufsiz1 */ + +#if ENABLE_FEATURE_UDHCP_PORT +#define SERVER_PORT (server_config.port) +#else +#define SERVER_PORT 67 +#endif + +extern struct dhcpOfferedAddr *leases; + + +/*** leases.h ***/ + +struct dhcpOfferedAddr { + uint8_t chaddr[16]; + uint32_t yiaddr; /* network order */ + uint32_t expires; /* host order */ +}; + +struct dhcpOfferedAddr *add_lease(const uint8_t *chaddr, uint32_t yiaddr, unsigned long lease) FAST_FUNC; +int lease_expired(struct dhcpOfferedAddr *lease) FAST_FUNC; +struct dhcpOfferedAddr *find_lease_by_chaddr(const uint8_t *chaddr) FAST_FUNC; +struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr) FAST_FUNC; +uint32_t find_address(int check_expired) FAST_FUNC; + + +/*** 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) FAST_FUNC; +/* Check to see if a mac has an associated static lease */ +uint32_t getIpByMac(struct static_lease *lease_struct, void *arg) FAST_FUNC; +/* Check to see if an ip is reserved as a static ip */ +uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip) FAST_FUNC; +/* Print out static leases just to check what's going on (debug code) */ +void printStaticLeases(struct static_lease **lease_struct) FAST_FUNC; + + +/*** serverpacket.h ***/ + +int send_offer(struct dhcpMessage *oldpacket) FAST_FUNC; +int send_NAK(struct dhcpMessage *oldpacket) FAST_FUNC; +int send_ACK(struct dhcpMessage *oldpacket, uint32_t yiaddr) FAST_FUNC; +int send_inform(struct dhcpMessage *oldpacket) FAST_FUNC; + + +/*** files.h ***/ + +void read_config(const char *file) FAST_FUNC; +void write_leases(void) FAST_FUNC; +void read_leases(const char *file) FAST_FUNC; +struct option_set *find_option(struct option_set *opt_list, uint8_t code) FAST_FUNC; + + +#if __GNUC_PREREQ(4,1) +# pragma GCC visibility pop +#endif + +#endif diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c new file mode 100644 index 0000000..f3b2855 --- /dev/null +++ b/networking/udhcp/dhcprelay.c @@ -0,0 +1,314 @@ +/* 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 "options.h" + +/* constants */ +#define SERVER_PORT 67 +#define SELECT_TIMEOUT 5 /* select timeout in sec. */ +#define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */ + +/* This list holds information about clients. The xid_* functions manipulate this list. */ +struct xid_item { + unsigned timestamp; + int client; + uint32_t xid; + struct sockaddr_in ip; + struct xid_item *next; +}; + +#define dhcprelay_xid_list (*(struct xid_item*)&bb_common_bufsiz1) + +static struct xid_item *xid_add(uint32_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 = monotonic_sec(); + 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; + unsigned current_time = monotonic_sec(); + + 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(uint32_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(uint32_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) +{ + uint8_t *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; +} + +/** + * 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, **client_dev; + int i, cn; + + /* copy list */ + dev_list = xstrdup(dev_list); + + /* get number of items, replace ',' with NULs */ + s = dev_list; + cn = 1; + while (*s) { + if (*s == ',') { + *s = '\0'; + cn++; + } + s++; + } + *client_number = cn; + + /* create vector of pointers */ + client_dev = xzalloc(cn * sizeof(*client_dev)); + client_dev[0] = dev_list; + i = 1; + while (i != cn) { + client_dev[i] = client_dev[i - 1] + strlen(client_dev[i - 1]) + 1; + i++; + } + return client_dev; +} + + +/* Creates listen sockets (in fds) and returns numerically max fd. */ +static int init_sockets(char **client, int num_clients, + char *server, int *fds) +{ + int i, n; + + /* talk to real server on bootps */ + fds[0] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, server); + n = fds[0]; + + for (i = 1; i < num_clients; i++) { + /* listen for clients on bootps */ + fds[i] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, client[i-1]); + if (fds[i] > n) + n = fds[i]; + } + return n; +} + + +/** + * 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); + 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) NORETURN; +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; + socklen_t addr_size; + struct sockaddr_in client_addr; + struct timeval tv; + int i; + + while (1) { + 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_recv_kernel_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 (udhcp_read_interface(clients[i-1], NULL, &dhcp_msg.giaddr, NULL)) + 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) MAIN_EXTERNALLY_VISIBLE; +int dhcprelay_main(int argc, char **argv) +{ + int num_sockets, max_socket; + int *fds; + uint32_t gw_ip; + char **clients; + struct sockaddr_in server_addr; + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(SERVER_PORT); + 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); + num_sockets++; /* for server socket at fds[0] */ + fds = xmalloc(num_sockets * sizeof(fds[0])); + max_socket = init_sockets(clients, num_sockets, argv[2], fds); + + if (udhcp_read_interface(argv[2], NULL, &gw_ip, NULL)) + return 1; + + /* doesn't return */ + dhcprelay_loop(fds, num_sockets, max_socket, clients, &server_addr, gw_ip); + /* return 0; - not reached */ +} diff --git a/networking/udhcp/domain_codec.c b/networking/udhcp/domain_codec.c new file mode 100644 index 0000000..6f051c4 --- /dev/null +++ b/networking/udhcp/domain_codec.c @@ -0,0 +1,205 @@ +/* vi: set sw=4 ts=4: */ + +/* RFC1035 domain compression routines (C) 2007 Gabriel Somlo <somlo at cmu.edu> + * + * Loosely based on the isc-dhcpd implementation by dhankins@isc.org + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#if ENABLE_FEATURE_UDHCP_RFC3397 + +#include "common.h" +#include "options.h" + +#define NS_MAXDNAME 1025 /* max domain name length */ +#define NS_MAXCDNAME 255 /* max compressed domain name length */ +#define NS_MAXLABEL 63 /* max label length */ +#define NS_MAXDNSRCH 6 /* max domains in search path */ +#define NS_CMPRSFLGS 0xc0 /* name compression pointer flag */ + + +/* expand a RFC1035-compressed list of domain names "cstr", of length "clen"; + * returns a newly allocated string containing the space-separated domains, + * prefixed with the contents of string pre, or NULL if an error occurs. + */ +char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre) +{ + const uint8_t *c; + int crtpos, retpos, depth, plen = 0, len = 0; + char *dst = NULL; + + if (!cstr) + return NULL; + + if (pre) + plen = strlen(pre); + + /* We make two passes over the cstr string. First, we compute + * how long the resulting string would be. Then we allocate a + * new buffer of the required length, and fill it in with the + * expanded content. The advantage of this approach is not + * having to deal with requiring callers to supply their own + * buffer, then having to check if it's sufficiently large, etc. + */ + + while (!dst) { + + if (len > 0) { /* second pass? allocate dst buffer and copy pre */ + dst = xmalloc(len + plen); + memcpy(dst, pre, plen); + } + + crtpos = retpos = depth = len = 0; + + while (crtpos < clen) { + c = cstr + crtpos; + + if ((*c & NS_CMPRSFLGS) != 0) { /* pointer */ + if (crtpos + 2 > clen) /* no offset to jump to? abort */ + return NULL; + if (retpos == 0) /* toplevel? save return spot */ + retpos = crtpos + 2; + depth++; + crtpos = ((*c & 0x3f) << 8) | (*(c + 1) & 0xff); /* jump */ + } else if (*c) { /* label */ + if (crtpos + *c + 1 > clen) /* label too long? abort */ + return NULL; + if (dst) + memcpy(dst + plen + len, c + 1, *c); + len += *c + 1; + crtpos += *c + 1; + if (dst) + *(dst + plen + len - 1) = '.'; + } else { /* null: end of current domain name */ + if (retpos == 0) { /* toplevel? keep going */ + crtpos++; + } else { /* return to toplevel saved spot */ + crtpos = retpos; + retpos = depth = 0; + } + if (dst) + *(dst + plen + len - 1) = ' '; + } + + if (depth > NS_MAXDNSRCH || /* too many jumps? abort, it's a loop */ + len > NS_MAXDNAME * NS_MAXDNSRCH) /* result too long? abort */ + return NULL; + } + + if (!len) /* expanded string has 0 length? abort */ + return NULL; + + if (dst) + *(dst + plen + len - 1) = '\0'; + } + + return dst; +} + +/* Convert a domain name (src) from human-readable "foo.blah.com" format into + * RFC1035 encoding "\003foo\004blah\003com\000". Return allocated string, or + * NULL if an error occurs. + */ +static uint8_t *convert_dname(const char *src) +{ + uint8_t c, *res, *lp, *rp; + int len; + + res = xmalloc(strlen(src) + 2); + rp = lp = res; + rp++; + + for (;;) { + c = (uint8_t)*src++; + if (c == '.' || c == '\0') { /* end of label */ + len = rp - lp - 1; + /* label too long, too short, or two '.'s in a row? abort */ + if (len > NS_MAXLABEL || len == 0 || (c == '.' && *src == '.')) { + free(res); + return NULL; + } + *lp = len; + lp = rp++; + if (c == '\0' || *src == '\0') /* end of dname */ + break; + } else { + if (c >= 0x41 && c <= 0x5A) /* uppercase? convert to lower */ + c += 0x20; + *rp++ = c; + } + } + + *lp = 0; + if (rp - res > NS_MAXCDNAME) { /* dname too long? abort */ + free(res); + return NULL; + } + return res; +} + +/* returns the offset within cstr at which dname can be found, or -1 + */ +static int find_offset(const uint8_t *cstr, int clen, const uint8_t *dname) +{ + const uint8_t *c, *d; + int off, inc; + + /* find all labels in cstr */ + off = 0; + while (off < clen) { + c = cstr + off; + + if ((*c & NS_CMPRSFLGS) != 0) { /* pointer, skip */ + off += 2; + } else if (*c) { /* label, try matching dname */ + inc = *c + 1; + d = dname; + while (*c == *d && memcmp(c + 1, d + 1, *c) == 0) { + if (*c == 0) /* match, return offset */ + return off; + d += *c + 1; + c += *c + 1; + if ((*c & NS_CMPRSFLGS) != 0) /* pointer, jump */ + c = cstr + (((*c & 0x3f) << 8) | (*(c + 1) & 0xff)); + } + off += inc; + } else { /* null, skip */ + off++; + } + } + + return -1; +} + +/* computes string to be appended to cstr so that src would be added to + * the compression (best case, it's a 2-byte pointer to some offset within + * cstr; worst case, it's all of src, converted to rfc3011 format). + * The computed string is returned directly; its length is returned via retlen; + * NULL and 0, respectively, are returned if an error occurs. + */ +uint8_t* FAST_FUNC dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen) +{ + uint8_t *d, *dname; + int off; + + dname = convert_dname(src); + if (dname == NULL) { + *retlen = 0; + return NULL; + } + + for (d = dname; *d != 0; d += *d + 1) { + off = find_offset(cstr, clen, d); + if (off >= 0) { /* found a match, add pointer and terminate string */ + *d++ = NS_CMPRSFLGS; + *d = off; + break; + } + } + + *retlen = d - dname + 1; + return dname; +} + +#endif /* ENABLE_FEATURE_UDHCP_RFC3397 */ diff --git a/networking/udhcp/dumpleases.c b/networking/udhcp/dumpleases.c new file mode 100644 index 0000000..3e19390 --- /dev/null +++ b/networking/udhcp/dumpleases.c @@ -0,0 +1,66 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include "common.h" +#include "dhcpd.h" + +int dumpleases_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dumpleases_main(int argc UNUSED_PARAM, char **argv) +{ + int fd; + int i; + unsigned opt; + time_t expires; + const char *file = LEASES_FILE; + struct dhcpOfferedAddr lease; + struct in_addr addr; + + enum { + OPT_a = 0x1, // -a + OPT_r = 0x2, // -r + OPT_f = 0x4, // -f + }; +#if ENABLE_GETOPT_LONG + static const char dumpleases_longopts[] ALIGN1 = + "absolute\0" No_argument "a" + "remaining\0" No_argument "r" + "file\0" Required_argument "f" + ; + + applet_long_options = dumpleases_longopts; +#endif + opt_complementary = "=0:a--r:r--a"; + opt = getopt32(argv, "arf:", &file); + + fd = xopen(file, O_RDONLY); + + printf("Mac Address IP-Address Expires %s\n", (opt & OPT_a) ? "at" : "in"); + /* "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */ + while (full_read(fd, &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 (!(opt & OPT_a)) { /* no -a */ + if (!expires) + puts("expired"); + 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 /* -a */ + fputs(ctime(&expires), stdout); + } + /* close(fd); */ + + return 0; +} diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c new file mode 100644 index 0000000..0b97d76 --- /dev/null +++ b/networking/udhcp/files.c @@ -0,0 +1,413 @@ +/* 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" + + +/* on these functions, make sure your datatype matches */ +static int read_ip(const char *line, void *arg) +{ + len_and_sockaddr *lsa; + + lsa = host_and_af2sockaddr(line, 0, AF_INET); + if (!lsa) + return 0; + *(uint32_t*)arg = lsa->u.sin.sin_addr.s_addr; + free(lsa); + return 1; +} + +static int read_mac(const char *line, void *arg) +{ + struct ether_addr *temp_ether_addr; + + temp_ether_addr = ether_aton_r(line, (struct ether_addr *)arg); + if (temp_ether_addr == NULL) + return 0; + return 1; +} + + +static int read_str(const char *line, void *arg) +{ + char **dest = arg; + + free(*dest); + *dest = xstrdup(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; + + if (!strcasecmp("yes", line)) { + *dest = 1; + return 1; + } + if (!strcasecmp("no", line)) { + *dest = 0; + return 1; + } + return 0; +} + + +/* find option 'code' in opt_list */ +struct option_set* FAST_FUNC find_option(struct option_set *opt_list, uint8_t 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; + 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; + + existing = find_option(*opt_list, option->code); + if (!existing) { + DEBUG("Attaching option %02x to list", option->code); + +#if ENABLE_FEATURE_UDHCP_RFC3397 + if ((option->flags & TYPE_MASK) == OPTION_STR1035) + /* reuse buffer and length for RFC1035-formatted string */ + buffer = (char *)dname_enc(NULL, 0, buffer, &length); +#endif + + /* make a new option */ + new = xmalloc(sizeof(*new)); + 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; +#if ENABLE_FEATURE_UDHCP_RFC3397 + if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL) + free(buffer); +#endif + return; + } + + /* add it to an existing option */ + DEBUG("Attaching option %02x to existing member of list", option->code); + if (option->flags & OPTION_LIST) { +#if ENABLE_FEATURE_UDHCP_RFC3397 + if ((option->flags & TYPE_MASK) == OPTION_STR1035) + /* reuse buffer and length for RFC1035-formatted string */ + buffer = (char *)dname_enc(existing->data + 2, + existing->data[OPT_LEN], buffer, &length); +#endif + if (existing->data[OPT_LEN] + length <= 255) { + existing->data = xrealloc(existing->data, + existing->data[OPT_LEN] + length + 3); + if ((option->flags & TYPE_MASK) == OPTION_STRING) { + /* ' ' can bring us to 256 - bad */ + if (existing->data[OPT_LEN] + length >= 255) + return; + /* add space separator between STRING options in a list */ + existing->data[existing->data[OPT_LEN] + 2] = ' '; + existing->data[OPT_LEN]++; + } + 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 */ +#if ENABLE_FEATURE_UDHCP_RFC3397 + if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL) + free(buffer); +#endif + } /* else, ignore the new data */ +} + + +/* 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; + char *line; + const struct dhcp_option *option; + int retval, length, idx; + char buffer[8] ALIGNED(4); + 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; + + idx = index_in_strings(dhcp_option_strings, opt); /* NB: was strcasecmp! */ + if (idx < 0) + return 0; + option = &dhcp_options[idx]; + + retval = 0; + do { + val = strtok(NULL, ", \t"); + if (!val) break; + length = dhcp_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); + val = strtok(NULL, ", \t/-"); + if (!val) + retval = 0; + if (retval) + retval = read_ip(val, buffer + 4); + break; + case OPTION_STRING: +#if ENABLE_FEATURE_UDHCP_RFC3397 + case OPTION_STR1035: +#endif + 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; + /* htonX are macros in older libc's, using temp var + * in code below for safety */ + /* TODO: use bb_strtoX? */ + case OPTION_U16: { + unsigned long tmp = strtoul(val, &endptr, 0); + *result_u16 = htons(tmp); + retval = (endptr[0] == '\0' /*&& tmp < 0x10000*/); + break; + } + case OPTION_S16: { + long tmp = strtol(val, &endptr, 0); + *result_u16 = htons(tmp); + retval = (endptr[0] == '\0'); + break; + } + case OPTION_U32: { + unsigned long tmp = strtoul(val, &endptr, 0); + *result_u32 = htonl(tmp); + retval = (endptr[0] == '\0'); + break; + } + case OPTION_S32: { + long tmp = strtol(val, &endptr, 0); + *result_u32 = htonl(tmp); + 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_UDHCP_DEBUG) printStaticLeases(arg); + + return 1; +} + + +struct config_keyword { + const char *keyword; + int (*handler)(const char *line, void *var); + void *var; + const char *def; +}; + +static const struct config_keyword keywords[] = { + /* keyword handler variable address default */ + {"start", read_ip, &(server_config.start_ip), "192.168.0.20"}, + {"end", read_ip, &(server_config.end_ip), "192.168.0.254"}, + {"interface", read_str, &(server_config.interface), "eth0"}, + /* Avoid "max_leases value not sane" warning by setting default + * to default_end_ip - default_start_ip + 1: */ + {"max_leases", read_u32, &(server_config.max_leases), "235"}, + {"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"}, + {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"}, + /* keywords with no defaults must be last! */ + {"option", read_opt, &(server_config.options), ""}, + {"opt", read_opt, &(server_config.options), ""}, + {"notify_file", read_str, &(server_config.notify_file), ""}, + {"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 */ +}; +enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 }; + +void FAST_FUNC read_config(const char *file) +{ + parser_t *parser; + const struct config_keyword *k; + unsigned i; + char *token[2]; + + for (i = 0; i < KWS_WITH_DEFAULTS; i++) + keywords[i].handler(keywords[i].def, keywords[i].var); + + parser = config_open(file); + while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) { + for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) { + if (!strcasecmp(token[0], k->keyword)) { + if (!k->handler(token[1], k->var)) { + bb_error_msg("can't parse line %u in %s", + parser->lineno, file); + /* reset back to the default value */ + k->handler(k->def, k->var); + } + break; + } + } + } + config_close(parser); + + server_config.start_ip = ntohl(server_config.start_ip); + server_config.end_ip = ntohl(server_config.end_ip); +} + + +void FAST_FUNC write_leases(void) +{ + int fp; + unsigned i; + time_t curr = time(0); + unsigned long tmp_time; + + fp = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC); + if (fp < 0) { + 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) { +// TODO: vfork-based child creation + char *cmd = xasprintf("%s %s", server_config.notify_file, server_config.lease_file); + system(cmd); + free(cmd); + } +} + + +void FAST_FUNC read_leases(const char *file) +{ + int fp; + unsigned i; + struct dhcpOfferedAddr lease; + + fp = open_or_warn(file, O_RDONLY); + if (fp < 0) { + return; + } + + i = 0; + while (i < server_config.max_leases + && full_read(fp, &lease, sizeof(lease)) == sizeof(lease) + ) { + /* ADDME: is it a static lease */ + uint32_t y = ntohl(lease.yiaddr); + if (y >= server_config.start_ip && y <= server_config.end_ip) { + lease.expires = ntohl(lease.expires); + if (!server_config.remaining) + lease.expires -= time(NULL); + 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..ff52da9 --- /dev/null +++ b/networking/udhcp/leases.c @@ -0,0 +1,149 @@ +/* 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" + + +/* Find the oldest expired lease, NULL if there are no expired leases */ +static struct dhcpOfferedAddr *oldest_expired_lease(void) +{ + struct dhcpOfferedAddr *oldest = NULL; +// TODO: use monotonic_sec() + unsigned long oldest_lease = time(0); + unsigned 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; +} + + +/* clear every lease out that chaddr OR yiaddr matches and is nonzero */ +static void clear_lease(const uint8_t *chaddr, uint32_t yiaddr) +{ + unsigned i, j; + + for (j = 0; j < 16 && !chaddr[j]; j++) + continue; + + for (i = 0; i < server_config.max_leases; i++) + if ((j != 16 && memcmp(leases[i].chaddr, chaddr, 16) == 0) + || (yiaddr && leases[i].yiaddr == yiaddr) + ) { + memset(&(leases[i]), 0, sizeof(leases[i])); + } +} + + +/* add a lease into the table, clearing out any old ones */ +struct dhcpOfferedAddr* FAST_FUNC add_lease(const 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 FAST_FUNC lease_expired(struct dhcpOfferedAddr *lease) +{ + return (lease->expires < (unsigned long) time(0)); +} + + +/* Find the first lease that matches chaddr, NULL if no match */ +struct dhcpOfferedAddr* FAST_FUNC find_lease_by_chaddr(const uint8_t *chaddr) +{ + unsigned 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* FAST_FUNC find_lease_by_yiaddr(uint32_t yiaddr) +{ + unsigned 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 nobody_responds_to_arp(uint32_t addr) +{ + /* 16 zero bytes */ + static const uint8_t blank_chaddr[16] = { 0 }; + /* = { 0 } helps gcc to put it in rodata, not bss */ + + struct in_addr temp; + int r; + + r = arpping(addr, server_config.server, server_config.arp, server_config.interface); + if (r) + return r; + + temp.s_addr = addr; + bb_info_msg("%s belongs to someone, reserving it for %u seconds", + inet_ntoa(temp), (unsigned)server_config.conflict_time); + add_lease(blank_chaddr, addr, server_config.conflict_time); + return 0; +} + + +/* find an assignable address, if check_expired is true, we check all the expired leases as well. + * Maybe this should try expired leases by age... */ +uint32_t FAST_FUNC find_address(int check_expired) +{ + uint32_t addr, ret; + struct dhcpOfferedAddr *lease = NULL; + + addr = server_config.start_ip; /* addr is in host order here */ + for (; addr <= server_config.end_ip; 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 assigned as a static lease */ + ret = htonl(addr); + if (!reservedIp(server_config.static_leases, ret)) { + /* lease is not taken */ + 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))) + && nobody_responds_to_arp(ret) /* it isn't used on the network */ + ) { + return ret; + } + } + } + return 0; +} diff --git a/networking/udhcp/options.c b/networking/udhcp/options.c new file mode 100644 index 0000000..6bf99e2 --- /dev/null +++ b/networking/udhcp/options.c @@ -0,0 +1,234 @@ +/* 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[] = { + /* flags code */ + { OPTION_IP | OPTION_REQ, 0x01 }, /* DHCP_SUBNET */ + { OPTION_S32 , 0x02 }, /* DHCP_TIME_OFFSET */ + { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03 }, /* DHCP_ROUTER */ + { OPTION_IP | OPTION_LIST , 0x04 }, /* DHCP_TIME_SERVER */ + { OPTION_IP | OPTION_LIST , 0x05 }, /* DHCP_NAME_SERVER */ + { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06 }, /* DHCP_DNS_SERVER */ + { OPTION_IP | OPTION_LIST , 0x07 }, /* DHCP_LOG_SERVER */ + { OPTION_IP | OPTION_LIST , 0x08 }, /* DHCP_COOKIE_SERVER */ + { OPTION_IP | OPTION_LIST , 0x09 }, /* DHCP_LPR_SERVER */ + { OPTION_STRING | OPTION_REQ, 0x0c }, /* DHCP_HOST_NAME */ + { OPTION_U16 , 0x0d }, /* DHCP_BOOT_SIZE */ + { OPTION_STRING | OPTION_LIST | OPTION_REQ, 0x0f }, /* DHCP_DOMAIN_NAME */ + { OPTION_IP , 0x10 }, /* DHCP_SWAP_SERVER */ + { OPTION_STRING , 0x11 }, /* DHCP_ROOT_PATH */ + { OPTION_U8 , 0x17 }, /* DHCP_IP_TTL */ + { OPTION_U16 , 0x1a }, /* DHCP_MTU */ + { OPTION_IP | OPTION_REQ, 0x1c }, /* DHCP_BROADCAST */ + { OPTION_STRING , 0x28 }, /* nisdomain */ + { OPTION_IP | OPTION_LIST , 0x29 }, /* nissrv */ + { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a }, /* DHCP_NTP_SERVER */ + { OPTION_IP | OPTION_LIST , 0x2c }, /* DHCP_WINS_SERVER */ + { OPTION_IP , 0x32 }, /* DHCP_REQUESTED_IP */ + { OPTION_U32 , 0x33 }, /* DHCP_LEASE_TIME */ + { OPTION_U8 , 0x35 }, /* dhcptype */ + { OPTION_IP , 0x36 }, /* DHCP_SERVER_ID */ + { OPTION_STRING , 0x38 }, /* DHCP_MESSAGE */ + { OPTION_STRING , 0x3C }, /* DHCP_VENDOR */ + { OPTION_STRING , 0x3D }, /* DHCP_CLIENT_ID */ + { OPTION_STRING , 0x42 }, /* tftp */ + { OPTION_STRING , 0x43 }, /* bootfile */ + { OPTION_STRING , 0x4D }, /* userclass */ +#if ENABLE_FEATURE_UDHCP_RFC3397 + { OPTION_STR1035 | OPTION_LIST , 0x77 }, /* search */ +#endif + /* MSIE's "Web Proxy Autodiscovery Protocol" support */ + { OPTION_STRING , 0xfc }, /* wpad */ + + /* Options below have no match in dhcp_option_strings[], + * are not passed to dhcpc scripts, and cannot be specified + * with "option XXX YYY" syntax in dhcpd config file. */ + + { OPTION_U16 , 0x39 }, /* DHCP_MAX_SIZE */ + { } /* zeroed terminating entry */ +}; + +/* Used for converting options from incoming packets to env variables + * for udhcpc stript */ +/* Must match dhcp_options[] order */ +const char dhcp_option_strings[] ALIGN1 = + "subnet" "\0" /* DHCP_SUBNET */ + "timezone" "\0" /* DHCP_TIME_OFFSET */ + "router" "\0" /* DHCP_ROUTER */ + "timesrv" "\0" /* DHCP_TIME_SERVER */ + "namesrv" "\0" /* DHCP_NAME_SERVER */ + "dns" "\0" /* DHCP_DNS_SERVER */ + "logsrv" "\0" /* DHCP_LOG_SERVER */ + "cookiesrv" "\0" /* DHCP_COOKIE_SERVER */ + "lprsrv" "\0" /* DHCP_LPR_SERVER */ + "hostname" "\0" /* DHCP_HOST_NAME */ + "bootsize" "\0" /* DHCP_BOOT_SIZE */ + "domain" "\0" /* DHCP_DOMAIN_NAME */ + "swapsrv" "\0" /* DHCP_SWAP_SERVER */ + "rootpath" "\0" /* DHCP_ROOT_PATH */ + "ipttl" "\0" /* DHCP_IP_TTL */ + "mtu" "\0" /* DHCP_MTU */ + "broadcast" "\0" /* DHCP_BROADCAST */ + "nisdomain" "\0" /* */ + "nissrv" "\0" /* */ + "ntpsrv" "\0" /* DHCP_NTP_SERVER */ + "wins" "\0" /* DHCP_WINS_SERVER */ + "requestip" "\0" /* DHCP_REQUESTED_IP */ + "lease" "\0" /* DHCP_LEASE_TIME */ + "dhcptype" "\0" /* */ + "serverid" "\0" /* DHCP_SERVER_ID */ + "message" "\0" /* DHCP_MESSAGE */ + "vendorclass" "\0" /* DHCP_VENDOR */ + "clientid" "\0" /* DHCP_CLIENT_ID */ + "tftp" "\0" + "bootfile" "\0" + "userclass" "\0" +#if ENABLE_FEATURE_UDHCP_RFC3397 + "search" "\0" +#endif + /* MSIE's "Web Proxy Autodiscovery Protocol" support */ + "wpad" "\0" + ; + + +/* Lengths of the different option types */ +const uint8_t dhcp_option_lengths[] ALIGN1 = { + [OPTION_IP] = 4, + [OPTION_IP_PAIR] = 8, + [OPTION_BOOLEAN] = 1, + [OPTION_STRING] = 1, +#if ENABLE_FEATURE_UDHCP_RFC3397 + [OPTION_STR1035] = 1, +#endif + [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* FAST_FUNC get_option(struct dhcpMessage *packet, int code) +{ + int i, length; + uint8_t *optionptr; + int over = 0; + int curr = OPTION_FIELD; + + optionptr = packet->options; + i = 0; + length = sizeof(packet->options); + while (1) { + 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 = sizeof(packet->file); + curr = FILE_FIELD; + } else if (curr == FILE_FIELD && (over & SNAME_FIELD)) { + optionptr = packet->sname; + i = 0; + length = sizeof(packet->sname); + curr = SNAME_FIELD; + } else + return NULL; + break; + default: + i += optionptr[OPT_LEN + i] + 2; + } + } + return NULL; +} + + +/* return the position of the 'end' option (no bounds checking) */ +int FAST_FUNC 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 FAST_FUNC 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 >= DHCP_OPTIONS_BUFSIZE) { + 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 FAST_FUNC 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 = dhcp_option_lengths[dh->flags & TYPE_MASK]; + option[OPT_LEN] = len; + if (BB_BIG_ENDIAN) + data <<= 8 * (4 - len); + /* This memcpy is for 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..d18a353 --- /dev/null +++ b/networking/udhcp/options.h @@ -0,0 +1,121 @@ +/* vi: set sw=4 ts=4: */ +/* options.h */ +#ifndef _OPTIONS_H +#define _OPTIONS_H + +#if __GNUC_PREREQ(4,1) +# pragma GCC visibility push(hidden) +#endif + +#define TYPE_MASK 0x0F + +enum { + OPTION_IP = 1, + OPTION_IP_PAIR, + OPTION_STRING, +#if ENABLE_FEATURE_UDHCP_RFC3397 + OPTION_STR1035, /* RFC1035 compressed domain name list */ +#endif + 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 */ + +/*****************************************************************/ +/* Do not modify below here unless you know what you are doing!! */ +/*****************************************************************/ + +/* DHCP protocol -- see RFC 2131 */ +#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 /* client -> server */ +#define DHCPOFFER 2 /* client <- server */ +#define DHCPREQUEST 3 /* client -> server */ +#define DHCPDECLINE 4 /* client -> server */ +#define DHCPACK 5 /* client <- server */ +#define DHCPNAK 6 /* client <- server */ +#define DHCPRELEASE 7 /* client -> server */ +#define DHCPINFORM 8 /* client -> server */ + +#define OPTION_FIELD 0 +#define FILE_FIELD 1 +#define SNAME_FIELD 2 + +/* miscellaneous defines */ +#define OPT_CODE 0 +#define OPT_LEN 1 +#define OPT_DATA 2 + +struct dhcp_option { + uint8_t flags; + uint8_t code; +}; + +extern const struct dhcp_option dhcp_options[]; +extern const char dhcp_option_strings[]; +extern const uint8_t dhcp_option_lengths[]; + +uint8_t *get_option(struct dhcpMessage *packet, int code) FAST_FUNC; +int end_option(uint8_t *optionptr) FAST_FUNC; +int add_option_string(uint8_t *optionptr, uint8_t *string) FAST_FUNC; +int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data) FAST_FUNC; +#if ENABLE_FEATURE_UDHCP_RFC3397 +char *dname_dec(const uint8_t *cstr, int clen, const char *pre) FAST_FUNC; +uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen) FAST_FUNC; +#endif + +#if __GNUC_PREREQ(4,1) +# pragma GCC visibility pop +#endif + +#endif diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c new file mode 100644 index 0000000..1a6f7e6 --- /dev/null +++ b/networking/udhcp/packet.c @@ -0,0 +1,238 @@ +/* vi: set sw=4 ts=4: */ + +#include <netinet/in.h> +#if (defined(__GLIBC__) && __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 FAST_FUNC udhcp_init_header(struct dhcpMessage *packet, char type) +{ + memset(packet, 0, sizeof(struct dhcpMessage)); + packet->op = BOOTREQUEST; /* if client to a server */ + switch (type) { + case DHCPOFFER: + case DHCPACK: + case DHCPNAK: + packet->op = BOOTREPLY; /* if server to client */ + } + 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 FAST_FUNC udhcp_recv_kernel_packet(struct dhcpMessage *packet, int fd) +{ + int bytes; + unsigned char *vendor; + + memset(packet, 0, sizeof(*packet)); + bytes = safe_read(fd, packet, sizeof(*packet)); + if (bytes < 0) { + DEBUG("cannot read on listening socket, ignoring"); + return bytes; /* returns -1 */ + } + + if (packet->cookie != htonl(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); + if (vendor) { +#if 0 + static const char broken_vendors[][8] = { + "MSFT 98", + "" + }; + int i; + 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 replies", + broken_vendors[i]); + packet->flags |= htons(BROADCAST_FLAG); + } + } +#else + if (vendor[OPT_LEN - 2] == (uint8_t)(sizeof("MSFT 98")-1) + && memcmp(vendor, "MSFT 98", sizeof("MSFT 98")-1) == 0 + ) { + DEBUG("broken client (%s), forcing broadcast replies", "MSFT 98"); + packet->flags |= htons(BROADCAST_FLAG); + } +#endif + } + } + + return bytes; +} + + +uint16_t FAST_FUNC 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, send packet */ +int FAST_FUNC udhcp_send_raw_packet(struct dhcpMessage *payload, + uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port, const uint8_t *dest_arp, + int ifindex) +{ + struct sockaddr_ll dest; + struct udp_dhcp_packet packet; + int fd; + int result = -1; + const char *msg; + + enum { + IP_UPD_DHCP_SIZE = sizeof(struct udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS, + UPD_DHCP_SIZE = IP_UPD_DHCP_SIZE - offsetof(struct udp_dhcp_packet, udp), + }; + + fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + if (fd < 0) { + msg = "socket(%s)"; + goto ret_msg; + } + + memset(&dest, 0, sizeof(dest)); + memset(&packet, 0, sizeof(packet)); + packet.data = *payload; /* struct copy */ + + 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(dest)) < 0) { + msg = "bind(%s)"; + goto ret_close; + } + + 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); + /* size, excluding IP header: */ + packet.udp.len = htons(UPD_DHCP_SIZE); + /* for UDP checksumming, ip.len is set to UDP packet len */ + packet.ip.tot_len = packet.udp.len; + packet.udp.check = udhcp_checksum(&packet, IP_UPD_DHCP_SIZE); + /* but for sending, it is set to IP packet len */ + packet.ip.tot_len = htons(IP_UPD_DHCP_SIZE); + 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)); + + /* Currently we send full-sized DHCP packets (zero padded). + * If you need to change this: last byte of the packet is + * packet.data.options[end_option(packet.data.options)] + */ + result = sendto(fd, &packet, IP_UPD_DHCP_SIZE, 0, + (struct sockaddr *) &dest, sizeof(dest)); + msg = "sendto"; + ret_close: + close(fd); + if (result < 0) { + ret_msg: + bb_perror_msg(msg, "PACKET"); + } + return result; +} + + +/* Let the kernel do all the work for packet generation */ +int FAST_FUNC udhcp_send_kernel_packet(struct dhcpMessage *payload, + uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port) +{ + struct sockaddr_in client; + int fd; + int result = -1; + const char *msg; + + enum { + DHCP_SIZE = sizeof(struct dhcpMessage) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS, + }; + + fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) { + msg = "socket(%s)"; + goto ret_msg; + } + setsockopt_reuseaddr(fd); + + 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(client)) == -1) { + msg = "bind(%s)"; + goto ret_close; + } + + 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(client)) == -1) { + msg = "connect"; + goto ret_close; + } + + /* Currently we send full-sized DHCP packets (see above) */ + result = safe_write(fd, payload, DHCP_SIZE); + msg = "write"; + ret_close: + close(fd); + if (result < 0) { + ret_msg: + bb_perror_msg(msg, "UDP"); + } + return result; +} diff --git a/networking/udhcp/script.c b/networking/udhcp/script.c new file mode 100644 index 0000000..8dff9b7 --- /dev/null +++ b/networking/udhcp/script.c @@ -0,0 +1,239 @@ +/* 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 "dhcpc.h" +#include "options.h" + + +/* get a rough idea of how long an option will be (rounding up...) */ +static const uint8_t max_option_length[] = { + [OPTION_IP] = sizeof("255.255.255.255 "), + [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, + [OPTION_STRING] = 1, +#if ENABLE_FEATURE_UDHCP_RFC3397 + [OPTION_STR1035] = 1, +#endif + [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 / dhcp_option_lengths[opt_index]); +} + + +static int sprintip(char *dest, const char *pre, const 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(uint32_t mask) +{ + int i = 0; + mask = ntohl(mask); /* 111110000-like bit pattern */ + while (mask) { + i++; + mask <<= 1; + } + return i; +} + + +/* Allocate and fill with the text of option 'option'. */ +static char *alloc_fill_opts(uint8_t *option, const struct dhcp_option *type_p, const char *opt_name) +{ + int len, type, optlen; + uint16_t val_u16; + int16_t val_s16; + uint32_t val_u32; + int32_t val_s32; + char *dest, *ret; + + len = option[OPT_LEN - 2]; + type = type_p->flags & TYPE_MASK; + optlen = dhcp_option_lengths[type]; + + dest = ret = xmalloc(upper_length(len, type) + strlen(opt_name) + 2); + dest += sprintf(ret, "%s=", opt_name); + + 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 ret; /* Short circuit this case */ +#if ENABLE_FEATURE_UDHCP_RFC3397 + case OPTION_STR1035: + /* unpack option into dest; use ret for prefix (i.e., "optname=") */ + dest = dname_dec(option, len, ret); + free(ret); + return dest; +#endif + } + option += optlen; + len -= optlen; + if (len <= 0) break; + dest += sprintf(dest, " "); + } + return ret; +} + + +/* put all the parameters into an environment */ +static char **fill_envp(struct dhcpMessage *packet) +{ + int num_options = 0; + int i, j; + char **envp; + char *var; + const char *opt_name; + uint8_t *temp; + char over = 0; + + if (packet) { + 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++; + temp = get_option(packet, DHCP_OPTION_OVER); + if (temp) + 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); + var = getenv("PATH"); + if (var) + envp[j++] = xasprintf("PATH=%s", var); + var = getenv("HOME"); + if (var) + envp[j++] = xasprintf("HOME=%s", var); + + if (packet == NULL) + return envp; + + envp[j] = xmalloc(sizeof("ip=255.255.255.255")); + sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr); + + opt_name = dhcp_option_strings; + i = 0; + while (*opt_name) { + temp = get_option(packet, dhcp_options[i].code); + if (!temp) + goto next; + envp[j++] = alloc_fill_opts(temp, &dhcp_options[i], opt_name); + + /* Fill in a subnet bits option for things like /24 */ + if (dhcp_options[i].code == DHCP_SUBNET) { + uint32_t subnet; + memcpy(&subnet, temp, 4); + envp[j++] = xasprintf("mask=%d", mton(subnet)); + } + next: + opt_name += strlen(opt_name) + 1; + i++; + } + 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 FAST_FUNC 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 */ +// can we use wait4pid(spawn(...)) here? + pid = vfork(); + if (pid < 0) return; + if (pid == 0) { + /* close fd's? */ + /* exec script */ + execle(client_config.script, client_config.script, + name, NULL, envp); + bb_perror_msg_and_die("exec %s", client_config.script); + } + safe_waitpid(pid, NULL, 0); + for (curr = envp; *curr; curr++) + free(*curr); + free(envp); +} diff --git a/networking/udhcp/serverpacket.c b/networking/udhcp/serverpacket.c new file mode 100644 index 0000000..dcc234c --- /dev/null +++ b/networking/udhcp/serverpacket.c @@ -0,0 +1,266 @@ +/* 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 "dhcpc.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_send_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) +{ + const 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 (payload->flags & htons(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_send_raw_packet(payload, + /*src*/ server_config.server, SERVER_PORT, + /*dst*/ 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) +{ + if (payload->giaddr) + return send_packet_to_relay(payload); + return send_packet_to_client(payload, force_broadcast); +} + + +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 FAST_FUNC send_offer(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) >= server_config.start_ip + && ntohl(req_align) <= server_config.end_ip + && !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 FAST_FUNC send_NAK(struct dhcpMessage *oldpacket) +{ + struct dhcpMessage packet; + + init_packet(&packet, oldpacket, DHCPNAK); + + DEBUG("Sending NAK"); + return send_packet(&packet, 1); +} + + +int FAST_FUNC send_ACK(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; + + 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; + 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); + if (ENABLE_FEATURE_UDHCPD_WRITE_LEASES_EARLY) { + /* rewrite the file with leases at every new acceptance */ + write_leases(); + } + + return 0; +} + + +int FAST_FUNC 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..a025bd8 --- /dev/null +++ b/networking/udhcp/signalpipe.c @@ -0,0 +1,82 @@ +/* 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 struct fd_pair signal_pipe; + +static void signal_handler(int sig) +{ + unsigned char ch = sig; /* use char, avoid dealing with partial writes */ + if (write(signal_pipe.wr, &ch, 1) != 1) + bb_perror_msg("cannot send signal"); +} + + +/* Call this before doing anything else. Sets up the socket pair + * and installs the signal handler */ +void FAST_FUNC udhcp_sp_setup(void) +{ + /* was socketpair, but it needs AF_UNIX in kernel */ + xpiped_pair(signal_pipe); + close_on_exec_on(signal_pipe.rd); + close_on_exec_on(signal_pipe.wr); + ndelay_on(signal_pipe.wr); + bb_signals(0 + + (1 << SIGUSR1) + + (1 << SIGUSR2) + + (1 << 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 FAST_FUNC udhcp_sp_fd_set(fd_set *rfds, int extra_fd) +{ + FD_ZERO(rfds); + FD_SET(signal_pipe.rd, rfds); + if (extra_fd >= 0) { + close_on_exec_on(extra_fd); + FD_SET(extra_fd, rfds); + } + return signal_pipe.rd > extra_fd ? signal_pipe.rd : 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 FAST_FUNC udhcp_sp_read(const fd_set *rfds) +{ + unsigned char sig; + + if (!FD_ISSET(signal_pipe.rd, rfds)) + return 0; + + if (safe_read(signal_pipe.rd, &sig, 1) != 1) + return -1; + + return sig; +} diff --git a/networking/udhcp/socket.c b/networking/udhcp/socket.c new file mode 100644 index 0000000..385d5c3 --- /dev/null +++ b/networking/udhcp/socket.c @@ -0,0 +1,111 @@ +/* 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 (defined(__GLIBC__) && __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 FAST_FUNC udhcp_read_interface(const char *interface, int *ifindex, uint32_t *addr, uint8_t *arp) +{ + int fd; + struct ifreq ifr; + struct sockaddr_in *our_ip; + + memset(&ifr, 0, sizeof(ifr)); + fd = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW); + + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name)); + if (addr) { + if (ioctl_or_perror(fd, SIOCGIFADDR, &ifr, + "is interface %s up and configured?", interface) + ) { + 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_or_warn(fd, SIOCGIFINDEX, &ifr) != 0) { + close(fd); + return -1; + } + DEBUG("adapter index %d", ifr.ifr_ifindex); + *ifindex = ifr.ifr_ifindex; + } + + if (arp) { + if (ioctl_or_warn(fd, SIOCGIFHWADDR, &ifr) != 0) { + 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]); + } + + close(fd); + return 0; +} + +/* 1. None of the callers expects it to ever fail */ +/* 2. ip was always INADDR_ANY */ +int FAST_FUNC udhcp_listen_socket(/*uint32_t ip,*/ int port, const char *inf) +{ + int fd; + struct sockaddr_in addr; + + DEBUG("Opening listen socket on *:%d %s", port, inf); + fd = xsocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + + setsockopt_reuseaddr(fd); + if (setsockopt_broadcast(fd) == -1) + bb_perror_msg_and_die("SO_BROADCAST"); + + /* NB: bug 1032 says this doesn't work on ethernet aliases (ethN:M) */ + if (setsockopt_bindtodevice(fd, inf)) + xfunc_die(); /* warning is already printed */ + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + /* addr.sin_addr.s_addr = ip; - all-zeros is INADDR_ANY */ + xbind(fd, (struct sockaddr *)&addr, sizeof(addr)); + + return fd; +} diff --git a/networking/udhcp/static_leases.c b/networking/udhcp/static_leases.c new file mode 100644 index 0000000..43f1c98 --- /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 FAST_FUNC 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 FAST_FUNC 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 FAST_FUNC 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_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 FAST_FUNC 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 |