diff options
Diffstat (limited to 'networking/udhcp')
-rw-r--r-- | networking/udhcp/Config.in | 9 | ||||
-rw-r--r-- | networking/udhcp/Kbuild | 1 | ||||
-rw-r--r-- | networking/udhcp/dhcprelay.c | 339 | ||||
-rw-r--r-- | networking/udhcp/socket.c | 30 |
4 files changed, 366 insertions, 13 deletions
diff --git a/networking/udhcp/Config.in b/networking/udhcp/Config.in index 13dbcee9c..f633473eb 100644 --- a/networking/udhcp/Config.in +++ b/networking/udhcp/Config.in @@ -12,6 +12,15 @@ config APP_UDHCPD See http://udhcp.busybox.net for further details. +config APP_DHCPRELAY + bool "dhcprelay" + default n + depends on APP_UDHCPD + help + dhcprelay listens for dhcp requests on one or more interfaces + and forwards these requests to a different interface or dhcp + server. + config APP_DUMPLEASES bool "Lease display utility (dumpleases)" default n diff --git a/networking/udhcp/Kbuild b/networking/udhcp/Kbuild index 90047c174..dc2c01f61 100644 --- a/networking/udhcp/Kbuild +++ b/networking/udhcp/Kbuild @@ -15,3 +15,4 @@ lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \ lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \ serverpacket.o static_leases.o lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o +lib-$(CONFIG_APP_DHCPRELAY) += dhcprelay.o diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c new file mode 100644 index 000000000..97bdcb084 --- /dev/null +++ b/networking/udhcp/dhcprelay.c @@ -0,0 +1,339 @@ +/* vi: set sw=4 ts=4: */ +/* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com> + * + * Licensed under GPL v2, see file LICENSE in this tarball for details. + * + * DHCP Relay for 'DHCPv4 Configuration of IPSec Tunnel Mode' support + * Copyright (C) 2002 Mario Strasser <mast@gmx.net>, + * Zuercher Hochschule Winterthur, + * Netbeat AG + * Upstream has GPL v2 or later + */ + +#include "common.h" +#include "dhcpd.h" +#include "options.h" + +/* constants */ +#define SELECT_TIMEOUT 5 /* select timeout in sec. */ +#define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */ +#define MAX_INTERFACES 9 + + +/* This list holds information about clients. The xid_* functions manipulate this list. */ +struct xid_item { + u_int32_t xid; + struct sockaddr_in ip; + int client; + time_t timestamp; + struct xid_item *next; +} dhcprelay_xid_list = {0, {0}, 0, 0, NULL}; + + +static struct xid_item * xid_add(u_int32_t xid, struct sockaddr_in *ip, int client) +{ + struct xid_item *item; + + /* create new xid entry */ + item = xmalloc(sizeof(struct xid_item)); + + /* add xid entry */ + item->ip = *ip; + item->xid = xid; + item->client = client; + item->timestamp = time(NULL); + item->next = dhcprelay_xid_list.next; + dhcprelay_xid_list.next = item; + + return item; +} + + +static void xid_expire(void) +{ + struct xid_item *item = dhcprelay_xid_list.next; + struct xid_item *last = &dhcprelay_xid_list; + time_t current_time = time(NULL); + + while (item != NULL) { + if ((current_time-item->timestamp) > MAX_LIFETIME) { + last->next = item->next; + free(item); + item = last->next; + } else { + last = item; + item = item->next; + } + } +} + +static struct xid_item * xid_find(u_int32_t xid) +{ + struct xid_item *item = dhcprelay_xid_list.next; + while (item != NULL) { + if (item->xid == xid) { + return item; + } + item = item->next; + } + return NULL; +} + +static void xid_del(u_int32_t xid) +{ + struct xid_item *item = dhcprelay_xid_list.next; + struct xid_item *last = &dhcprelay_xid_list; + while (item != NULL) { + if (item->xid == xid) { + last->next = item->next; + free(item); + item = last->next; + } else { + last = item; + item = item->next; + } + } +} + + +/** + * get_dhcp_packet_type - gets the message type of a dhcp packet + * p - pointer to the dhcp packet + * returns the message type on success, -1 otherwise + */ +static int get_dhcp_packet_type(struct dhcpMessage *p) +{ + u_char *op; + + /* it must be either a BOOTREQUEST or a BOOTREPLY */ + if (p->op != BOOTREQUEST && p->op != BOOTREPLY) + return -1; + /* get message type option */ + op = get_option(p, DHCP_MESSAGE_TYPE); + if (op != NULL) + return op[0]; + return -1; +} + +/** + * signal_handler - handles signals ;-) + * sig - sent signal + */ +static int dhcprelay_stopflag; +static void dhcprelay_signal_handler(int sig) +{ + dhcprelay_stopflag = 1; +} + +/** + * get_client_devices - parses the devices list + * dev_list - comma separated list of devices + * returns array + */ +static char ** get_client_devices(char *dev_list, int *client_number) +{ + char *s, *list, **client_dev; + int i, cn=1; + + /* copy list */ + list = xstrdup(dev_list); + if (list == NULL) return NULL; + + /* get number of items */ + for (s = dev_list; *s; s++) if (*s == ',') + cn++; + + client_dev = xzalloc(cn * sizeof(*client_dev)); + + /* parse list */ + s = strtok(list, ","); + i = 0; + while (s != NULL) { + client_dev[i++] = xstrdup(s); + s = strtok(NULL, ","); + } + + /* free copy and exit */ + free(list); + *client_number = cn; + return client_dev; +} + + +/* Creates listen sockets (in fds) and returns the number allocated. */ +static int init_sockets(char **client, int num_clients, + char *server, int *fds, int *max_socket) +{ + int i; + + // talk to real server on bootps + fds[0] = listen_socket(htonl(INADDR_ANY), 67, server); + if (fds[0] < 0) return -1; + *max_socket = fds[0]; + + // array starts at 1 since server is 0 + num_clients++; + + for (i=1; i < num_clients; i++) { + // listen for clients on bootps + fds[i] = listen_socket(htonl(INADDR_ANY), 67, client[i-1]); + if (fds[i] < 0) return -1; + if (fds[i] > *max_socket) *max_socket = fds[i]; + } + + return i; +} + + +/** + * pass_on() - forwards dhcp packets from client to server + * p - packet to send + * client - number of the client + */ +static void pass_on(struct dhcpMessage *p, int packet_len, int client, int *fds, + struct sockaddr_in *client_addr, struct sockaddr_in *server_addr) +{ + int res, type; + struct xid_item *item; + + /* check packet_type */ + type = get_dhcp_packet_type(p); + if (type != DHCPDISCOVER && type != DHCPREQUEST + && type != DHCPDECLINE && type != DHCPRELEASE + && type != DHCPINFORM + ) { + return; + } + + /* create new xid entry */ + item = xid_add(p->xid, client_addr, client); + + /* forward request to LAN (server) */ + res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr, + sizeof(struct sockaddr_in)); + if (res != packet_len) { + bb_perror_msg("pass_on"); + return; + } +} + +/** + * pass_back() - forwards dhcp packets from server to client + * p - packet to send + */ +static void pass_back(struct dhcpMessage *p, int packet_len, int *fds) +{ + int res, type; + struct xid_item *item; + + /* check xid */ + item = xid_find(p->xid); + if (!item) { + return; + } + + /* check packet type */ + type = get_dhcp_packet_type(p); + if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) { + return; + } + + if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY)) + item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST); + if (item->client > MAX_INTERFACES) + return; + res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*)(&item->ip), + sizeof(item->ip)); + if (res != packet_len) { + bb_perror_msg("pass_back"); + return; + } + + /* remove xid entry */ + xid_del(p->xid); +} + +static void dhcprelay_loop(int *fds, int num_sockets, int max_socket, char **clients, + struct sockaddr_in *server_addr, uint32_t gw_ip) +{ + struct dhcpMessage dhcp_msg; + fd_set rfds; + size_t packlen, addr_size; + struct sockaddr_in client_addr; + struct timeval tv; + int i; + + while (!dhcprelay_stopflag) { + FD_ZERO(&rfds); + for (i = 0; i < num_sockets; i++) + FD_SET(fds[i], &rfds); + tv.tv_sec = SELECT_TIMEOUT; + tv.tv_usec = 0; + if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) { + /* server */ + if (FD_ISSET(fds[0], &rfds)) { + packlen = udhcp_get_packet(&dhcp_msg, fds[0]); + if (packlen > 0) { + pass_back(&dhcp_msg, packlen, fds); + } + } + for (i = 1; i < num_sockets; i++) { + /* clients */ + if (!FD_ISSET(fds[i], &rfds)) + continue; + addr_size = sizeof(struct sockaddr_in); + packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0, + (struct sockaddr *)(&client_addr), &addr_size); + if (packlen <= 0) + continue; + if (read_interface(clients[i-1], NULL, &dhcp_msg.giaddr, NULL) < 0) + dhcp_msg.giaddr = gw_ip; + pass_on(&dhcp_msg, packlen, i, fds, &client_addr, server_addr); + } + } + xid_expire(); + } +} + +int dhcprelay_main(int argc, char **argv) +{ + int i, num_sockets, max_socket, fds[MAX_INTERFACES]; + uint32_t gw_ip; + char **clients; + struct sockaddr_in server_addr; + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(67); + if (argc == 4) { + if (!inet_aton(argv[3], &server_addr.sin_addr)) + bb_perror_msg_and_die("didn't grok server"); + } else if (argc == 3) { + server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + } else { + bb_show_usage(); + } + clients = get_client_devices(argv[1], &num_sockets); + if (!clients) return 0; + + signal(SIGTERM, dhcprelay_signal_handler); + signal(SIGQUIT, dhcprelay_signal_handler); + signal(SIGINT, dhcprelay_signal_handler); + + num_sockets = init_sockets(clients, num_sockets, argv[2], fds, &max_socket); + if (num_sockets == -1) + bb_perror_msg_and_die("init_sockets() failed"); + + if (read_interface(argv[2], NULL, &gw_ip, NULL) == -1) + return 1; + + dhcprelay_loop(fds, num_sockets, max_socket, clients, &server_addr, gw_ip); + + if (ENABLE_FEATURE_CLEAN_UP) { + for (i = 0; i < num_sockets; i++) { + close(fds[i]); + free(clients[i]); + } + } + + return 0; +} diff --git a/networking/udhcp/socket.c b/networking/udhcp/socket.c index ea2913172..c19131d65 100644 --- a/networking/udhcp/socket.c +++ b/networking/udhcp/socket.c @@ -63,23 +63,27 @@ int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp) DEBUG("%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr)); } - if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) { - bb_perror_msg("SIOCGIFINDEX failed"); - close(fd); - return -1; + if (ifindex) { + if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) { + bb_perror_msg("SIOCGIFINDEX failed"); + close(fd); + return -1; + } + DEBUG("adapter index %d", ifr.ifr_ifindex); + *ifindex = ifr.ifr_ifindex; } - DEBUG("adapter index %d", ifr.ifr_ifindex); - *ifindex = ifr.ifr_ifindex; - if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) { - bb_perror_msg("SIOCGIFHWADDR failed"); - close(fd); - return -1; + if (arp) { + if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) { + bb_perror_msg("SIOCGIFHWADDR failed"); + close(fd); + return -1; + } + memcpy(arp, ifr.ifr_hwaddr.sa_data, 6); + DEBUG("adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x", + arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]); } - memcpy(arp, ifr.ifr_hwaddr.sa_data, 6); - DEBUG("adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x", - arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]); return 0; } |