busybox/networking/udhcp/dhcprelay.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com>
   3 *
   4 * Licensed under GPL v2, see file LICENSE in this tarball for details.
   5 *
   6 * DHCP Relay for 'DHCPv4 Configuration of IPSec Tunnel Mode' support
   7 * Copyright (C) 2002 Mario Strasser <mast@gmx.net>,
   8 *                   Zuercher Hochschule Winterthur,
   9 *                   Netbeat AG
  10 * Upstream has GPL v2 or later
  11 */
  12
  13#include "common.h"
  14#include "options.h"
  15
  16#define SERVER_PORT      67
  17#define SELECT_TIMEOUT    5 /* select timeout in sec. */
  18#define MAX_LIFETIME   2*60 /* lifetime of an xid entry in sec. */
  19
  20/* This list holds information about clients. The xid_* functions manipulate this list. */
  21struct xid_item {
  22        unsigned timestamp;
  23        int client;
  24        uint32_t xid;
  25        struct sockaddr_in ip;
  26        struct xid_item *next;
  27};
  28
  29#define dhcprelay_xid_list (*(struct xid_item*)&bb_common_bufsiz1)
  30
  31static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client)
  32{
  33        struct xid_item *item;
  34
  35        /* create new xid entry */
  36        item = xmalloc(sizeof(struct xid_item));
  37
  38        /* add xid entry */
  39        item->ip = *ip;
  40        item->xid = xid;
  41        item->client = client;
  42        item->timestamp = monotonic_sec();
  43        item->next = dhcprelay_xid_list.next;
  44        dhcprelay_xid_list.next = item;
  45
  46        return item;
  47}
  48
  49static void xid_expire(void)
  50{
  51        struct xid_item *item = dhcprelay_xid_list.next;
  52        struct xid_item *last = &dhcprelay_xid_list;
  53        unsigned current_time = monotonic_sec();
  54
  55        while (item != NULL) {
  56                if ((current_time - item->timestamp) > MAX_LIFETIME) {
  57                        last->next = item->next;
  58                        free(item);
  59                        item = last->next;
  60                } else {
  61                        last = item;
  62                        item = item->next;
  63                }
  64        }
  65}
  66
  67static struct xid_item *xid_find(uint32_t xid)
  68{
  69        struct xid_item *item = dhcprelay_xid_list.next;
  70        while (item != NULL) {
  71                if (item->xid == xid) {
  72                        return item;
  73                }
  74                item = item->next;
  75        }
  76        return NULL;
  77}
  78
  79static void xid_del(uint32_t xid)
  80{
  81        struct xid_item *item = dhcprelay_xid_list.next;
  82        struct xid_item *last = &dhcprelay_xid_list;
  83        while (item != NULL) {
  84                if (item->xid == xid) {
  85                        last->next = item->next;
  86                        free(item);
  87                        item = last->next;
  88                } else {
  89                        last = item;
  90                        item = item->next;
  91                }
  92        }
  93}
  94
  95/**
  96 * get_dhcp_packet_type - gets the message type of a dhcp packet
  97 * p - pointer to the dhcp packet
  98 * returns the message type on success, -1 otherwise
  99 */
 100static int get_dhcp_packet_type(struct dhcp_packet *p)
 101{
 102        uint8_t *op;
 103
 104        /* it must be either a BOOTREQUEST or a BOOTREPLY */
 105        if (p->op != BOOTREQUEST && p->op != BOOTREPLY)
 106                return -1;
 107        /* get message type option */
 108        op = get_option(p, DHCP_MESSAGE_TYPE);
 109        if (op != NULL)
 110                return op[0];
 111        return -1;
 112}
 113
 114/**
 115 * get_client_devices - parses the devices list
 116 * dev_list - comma separated list of devices
 117 * returns array
 118 */
 119static char **get_client_devices(char *dev_list, int *client_number)
 120{
 121        char *s, **client_dev;
 122        int i, cn;
 123
 124        /* copy list */
 125        dev_list = xstrdup(dev_list);
 126
 127        /* get number of items, replace ',' with NULs */
 128        s = dev_list;
 129        cn = 1;
 130        while (*s) {
 131                if (*s == ',') {
 132                        *s = '\0';
 133                        cn++;
 134                }
 135                s++;
 136        }
 137        *client_number = cn;
 138
 139        /* create vector of pointers */
 140        client_dev = xzalloc(cn * sizeof(*client_dev));
 141        client_dev[0] = dev_list;
 142        i = 1;
 143        while (i != cn) {
 144                client_dev[i] = client_dev[i - 1] + strlen(client_dev[i - 1]) + 1;
 145                i++;
 146        }
 147        return client_dev;
 148}
 149
 150
 151/* Creates listen sockets (in fds) bound to client and server ifaces,
 152 * and returns numerically max fd.
 153 */
 154static int init_sockets(char **client_ifaces, int num_clients,
 155                        char *server_iface, int *fds)
 156{
 157        int i, n;
 158
 159        /* talk to real server on bootps */
 160        fds[0] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, server_iface);
 161        n = fds[0];
 162
 163        for (i = 1; i < num_clients; i++) {
 164                /* listen for clients on bootps */
 165                fds[i] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, client_ifaces[i-1]);
 166                if (fds[i] > n)
 167                        n = fds[i];
 168        }
 169        return n;
 170}
 171
 172
 173/**
 174 * pass_to_server() - forwards dhcp packets from client to server
 175 * p - packet to send
 176 * client - number of the client
 177 */
 178static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, int *fds,
 179                        struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
 180{
 181        int res, type;
 182        struct xid_item *item;
 183
 184        /* check packet_type */
 185        type = get_dhcp_packet_type(p);
 186        if (type != DHCPDISCOVER && type != DHCPREQUEST
 187         && type != DHCPDECLINE && type != DHCPRELEASE
 188         && type != DHCPINFORM
 189        ) {
 190                return;
 191        }
 192
 193        /* create new xid entry */
 194        item = xid_add(p->xid, client_addr, client);
 195
 196        /* forward request to LAN (server) */
 197        errno = 0;
 198        res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr,
 199                        sizeof(struct sockaddr_in));
 200        if (res != packet_len) {
 201                bb_perror_msg("sendto");
 202        }
 203}
 204
 205/**
 206 * pass_to_client() - forwards dhcp packets from server to client
 207 * p - packet to send
 208 */
 209static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds)
 210{
 211        int res, type;
 212        struct xid_item *item;
 213
 214        /* check xid */
 215        item = xid_find(p->xid);
 216        if (!item) {
 217                return;
 218        }
 219
 220        /* check packet type */
 221        type = get_dhcp_packet_type(p);
 222        if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
 223                return;
 224        }
 225
 226        if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
 227                item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
 228        errno = 0;
 229        res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*) &(item->ip),
 230                        sizeof(item->ip));
 231        if (res != packet_len) {
 232                bb_perror_msg("sendto");
 233                return;
 234        }
 235
 236        /* remove xid entry */
 237        xid_del(p->xid);
 238}
 239
 240int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 241int dhcprelay_main(int argc, char **argv)
 242{
 243        struct dhcp_packet dhcp_msg;
 244        struct sockaddr_in server_addr;
 245        struct sockaddr_in client_addr;
 246        fd_set rfds;
 247        char **client_ifaces;
 248        int *fds;
 249        int num_sockets, max_socket;
 250        uint32_t our_nip;
 251
 252        server_addr.sin_family = AF_INET;
 253        server_addr.sin_port = htons(SERVER_PORT);
 254
 255        /* dhcprelay client_iface1,client_iface2,... server_iface [server_IP] */
 256        if (argc == 4) {
 257                if (!inet_aton(argv[3], &server_addr.sin_addr))
 258                        bb_perror_msg_and_die("bad server IP");
 259        } else if (argc == 3) {
 260                server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
 261        } else {
 262                bb_show_usage();
 263        }
 264
 265        /* Produce list of client ifaces */
 266        client_ifaces = get_client_devices(argv[1], &num_sockets);
 267
 268        num_sockets++; /* for server socket at fds[0] */
 269        fds = xmalloc(num_sockets * sizeof(fds[0]));
 270
 271        /* Create sockets and bind one to every iface */
 272        max_socket = init_sockets(client_ifaces, num_sockets, argv[2], fds);
 273
 274        /* Get our IP on server_iface */
 275        if (udhcp_read_interface(argv[2], NULL, &our_nip, NULL))
 276                return 1;
 277
 278        /* Main loop */
 279        while (1) {
 280//reinit stuff from time to time? go back to get_client_devices
 281//every N minutes?
 282                struct timeval tv;
 283                size_t packlen;
 284                socklen_t addr_size;
 285                int i;
 286
 287                FD_ZERO(&rfds);
 288                for (i = 0; i < num_sockets; i++)
 289                        FD_SET(fds[i], &rfds);
 290                tv.tv_sec = SELECT_TIMEOUT;
 291                tv.tv_usec = 0;
 292                if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
 293                        /* server */
 294                        if (FD_ISSET(fds[0], &rfds)) {
 295                                packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]);
 296                                if (packlen > 0) {
 297                                        pass_to_client(&dhcp_msg, packlen, fds);
 298                                }
 299                        }
 300                        /* clients */
 301                        for (i = 1; i < num_sockets; i++) {
 302                                if (!FD_ISSET(fds[i], &rfds))
 303                                        continue;
 304                                addr_size = sizeof(struct sockaddr_in);
 305                                packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
 306                                                (struct sockaddr *)(&client_addr), &addr_size);
 307                                if (packlen <= 0)
 308                                        continue;
 309
 310                                /* Get our IP on corresponding client_iface */
 311//why? what if server can't route such IP?
 312                                if (udhcp_read_interface(client_ifaces[i-1], NULL, &dhcp_msg.gateway_nip, NULL)) {
 313                                        /* Fall back to our server_iface's IP */
 314//this makes more sense!
 315                                        dhcp_msg.gateway_nip = our_nip;
 316                                }
 317//maybe set dhcp_msg.flags |= BROADCAST_FLAG too?
 318                                pass_to_server(&dhcp_msg, packlen, i, fds, &client_addr, &server_addr);
 319                        }
 320                }
 321                xid_expire();
 322        } /* while (1) */
 323
 324        /* return 0; - not reached */
 325}
 326