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 GPLv2, see file LICENSE in this source tree.
   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//usage:#define dhcprelay_trivial_usage
  14//usage:       "CLIENT_IFACE[,CLIENT_IFACE2]... SERVER_IFACE [SERVER_IP]"
  15//usage:#define dhcprelay_full_usage "\n\n"
  16//usage:       "Relay DHCP requests between clients and server"
  17
  18#include "common.h"
  19
  20#define SERVER_PORT    67
  21
  22/* lifetime of an xid entry in sec. */
  23#define MAX_LIFETIME   2*60
  24/* select timeout in sec. */
  25#define SELECT_TIMEOUT (MAX_LIFETIME / 8)
  26
  27/* This list holds information about clients. The xid_* functions manipulate this list. */
  28struct xid_item {
  29        unsigned timestamp;
  30        int client;
  31        uint32_t xid;
  32        struct sockaddr_in ip;
  33        struct xid_item *next;
  34} FIX_ALIASING;
  35
  36#define dhcprelay_xid_list (*(struct xid_item*)&bb_common_bufsiz1)
  37
  38static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client)
  39{
  40        struct xid_item *item;
  41
  42        /* create new xid entry */
  43        item = xmalloc(sizeof(struct xid_item));
  44
  45        /* add xid entry */
  46        item->ip = *ip;
  47        item->xid = xid;
  48        item->client = client;
  49        item->timestamp = monotonic_sec();
  50        item->next = dhcprelay_xid_list.next;
  51        dhcprelay_xid_list.next = item;
  52
  53        return item;
  54}
  55
  56static void xid_expire(void)
  57{
  58        struct xid_item *item = dhcprelay_xid_list.next;
  59        struct xid_item *last = &dhcprelay_xid_list;
  60        unsigned current_time = monotonic_sec();
  61
  62        while (item != NULL) {
  63                if ((current_time - item->timestamp) > MAX_LIFETIME) {
  64                        last->next = item->next;
  65                        free(item);
  66                        item = last->next;
  67                } else {
  68                        last = item;
  69                        item = item->next;
  70                }
  71        }
  72}
  73
  74static struct xid_item *xid_find(uint32_t xid)
  75{
  76        struct xid_item *item = dhcprelay_xid_list.next;
  77        while (item != NULL) {
  78                if (item->xid == xid) {
  79                        break;
  80                }
  81                item = item->next;
  82        }
  83        return item;
  84}
  85
  86static void xid_del(uint32_t xid)
  87{
  88        struct xid_item *item = dhcprelay_xid_list.next;
  89        struct xid_item *last = &dhcprelay_xid_list;
  90        while (item != NULL) {
  91                if (item->xid == xid) {
  92                        last->next = item->next;
  93                        free(item);
  94                        item = last->next;
  95                } else {
  96                        last = item;
  97                        item = item->next;
  98                }
  99        }
 100}
 101
 102/**
 103 * get_dhcp_packet_type - gets the message type of a dhcp packet
 104 * p - pointer to the dhcp packet
 105 * returns the message type on success, -1 otherwise
 106 */
 107static int get_dhcp_packet_type(struct dhcp_packet *p)
 108{
 109        uint8_t *op;
 110
 111        /* it must be either a BOOTREQUEST or a BOOTREPLY */
 112        if (p->op != BOOTREQUEST && p->op != BOOTREPLY)
 113                return -1;
 114        /* get message type option */
 115        op = udhcp_get_option(p, DHCP_MESSAGE_TYPE);
 116        if (op != NULL)
 117                return op[0];
 118        return -1;
 119}
 120
 121/**
 122 * make_iface_list - parses client/server interface names
 123 * returns array
 124 */
 125static char **make_iface_list(char **client_and_server_ifaces, int *client_number)
 126{
 127        char *s, **iface_list;
 128        int i, cn;
 129
 130        /* get number of items */
 131        cn = 2; /* 1 server iface + at least 1 client one */
 132        s = client_and_server_ifaces[0]; /* list of client ifaces */
 133        while (*s) {
 134                if (*s == ',')
 135                        cn++;
 136                s++;
 137        }
 138        *client_number = cn;
 139
 140        /* create vector of pointers */
 141        iface_list = xzalloc(cn * sizeof(iface_list[0]));
 142
 143        iface_list[0] = client_and_server_ifaces[1]; /* server iface */
 144
 145        i = 1;
 146        s = xstrdup(client_and_server_ifaces[0]); /* list of client ifaces */
 147        goto store_client_iface_name;
 148
 149        while (i < cn) {
 150                if (*s++ == ',') {
 151                        s[-1] = '\0';
 152 store_client_iface_name:
 153                        iface_list[i++] = s;
 154                }
 155        }
 156
 157        return iface_list;
 158}
 159
 160/* Creates listen sockets (in fds) bound to client and server ifaces,
 161 * and returns numerically max fd.
 162 */
 163static int init_sockets(char **iface_list, int num_clients, int *fds)
 164{
 165        int i, n;
 166
 167        n = 0;
 168        for (i = 0; i < num_clients; i++) {
 169                fds[i] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, iface_list[i]);
 170                if (n < fds[i])
 171                        n = fds[i];
 172        }
 173        return n;
 174}
 175
 176static int sendto_ip4(int sock, const void *msg, int msg_len, struct sockaddr_in *to)
 177{
 178        int err;
 179
 180        errno = 0;
 181        err = sendto(sock, msg, msg_len, 0, (struct sockaddr*) to, sizeof(*to));
 182        err -= msg_len;
 183        if (err)
 184                bb_perror_msg("sendto");
 185        return err;
 186}
 187
 188/**
 189 * pass_to_server() - forwards dhcp packets from client to server
 190 * p - packet to send
 191 * client - number of the client
 192 */
 193static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, int *fds,
 194                        struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
 195{
 196        int type;
 197
 198        /* check packet_type */
 199        type = get_dhcp_packet_type(p);
 200        if (type != DHCPDISCOVER && type != DHCPREQUEST
 201         && type != DHCPDECLINE && type != DHCPRELEASE
 202         && type != DHCPINFORM
 203        ) {
 204                return;
 205        }
 206
 207        /* create new xid entry */
 208        xid_add(p->xid, client_addr, client);
 209
 210        /* forward request to server */
 211        /* note that we send from fds[0] which is bound to SERVER_PORT (67).
 212         * IOW: we send _from_ SERVER_PORT! Although this may look strange,
 213         * RFC 1542 not only allows, but prescribes this for BOOTP relays.
 214         */
 215        sendto_ip4(fds[0], p, packet_len, server_addr);
 216}
 217
 218/**
 219 * pass_to_client() - forwards dhcp packets from server to client
 220 * p - packet to send
 221 */
 222static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds)
 223{
 224        int type;
 225        struct xid_item *item;
 226
 227        /* check xid */
 228        item = xid_find(p->xid);
 229        if (!item) {
 230                return;
 231        }
 232
 233        /* check packet type */
 234        type = get_dhcp_packet_type(p);
 235        if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
 236                return;
 237        }
 238
 239//TODO: also do it if (p->flags & htons(BROADCAST_FLAG)) is set!
 240        if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
 241                item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
 242
 243        if (sendto_ip4(fds[item->client], p, packet_len, &item->ip) != 0) {
 244                return; /* send error occurred */
 245        }
 246
 247        /* remove xid entry */
 248        xid_del(p->xid);
 249}
 250
 251int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 252int dhcprelay_main(int argc, char **argv)
 253{
 254        struct sockaddr_in server_addr;
 255        char **iface_list;
 256        int *fds;
 257        int num_sockets, max_socket;
 258        uint32_t our_nip;
 259
 260        server_addr.sin_family = AF_INET;
 261        server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
 262        server_addr.sin_port = htons(SERVER_PORT);
 263
 264        /* dhcprelay CLIENT_IFACE1[,CLIENT_IFACE2...] SERVER_IFACE [SERVER_IP] */
 265        if (argc == 4) {
 266                if (!inet_aton(argv[3], &server_addr.sin_addr))
 267                        bb_perror_msg_and_die("bad server IP");
 268        } else if (argc != 3) {
 269                bb_show_usage();
 270        }
 271
 272        iface_list = make_iface_list(argv + 1, &num_sockets);
 273
 274        fds = xmalloc(num_sockets * sizeof(fds[0]));
 275
 276        /* Create sockets and bind one to every iface */
 277        max_socket = init_sockets(iface_list, num_sockets, fds);
 278
 279        /* Get our IP on server_iface */
 280        if (udhcp_read_interface(argv[2], NULL, &our_nip, NULL))
 281                return 1;
 282
 283        /* Main loop */
 284        while (1) {
 285// reinit stuff from time to time? go back to make_iface_list
 286// every N minutes?
 287                fd_set rfds;
 288                struct timeval tv;
 289                int i;
 290
 291                FD_ZERO(&rfds);
 292                for (i = 0; i < num_sockets; i++)
 293                        FD_SET(fds[i], &rfds);
 294                tv.tv_sec = SELECT_TIMEOUT;
 295                tv.tv_usec = 0;
 296                if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
 297                        int packlen;
 298                        struct dhcp_packet dhcp_msg;
 299
 300                        /* server */
 301                        if (FD_ISSET(fds[0], &rfds)) {
 302                                packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]);
 303                                if (packlen > 0) {
 304                                        pass_to_client(&dhcp_msg, packlen, fds);
 305                                }
 306                        }
 307
 308                        /* clients */
 309                        for (i = 1; i < num_sockets; i++) {
 310                                struct sockaddr_in client_addr;
 311                                socklen_t addr_size;
 312
 313                                if (!FD_ISSET(fds[i], &rfds))
 314                                        continue;
 315
 316                                addr_size = sizeof(client_addr);
 317                                packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
 318                                                (struct sockaddr *)(&client_addr), &addr_size);
 319                                if (packlen <= 0)
 320                                        continue;
 321
 322                                /* Get our IP on corresponding client_iface */
 323// RFC 1542
 324// 4.1 General BOOTP Processing for Relay Agents
 325// 4.1.1 BOOTREQUEST Messages
 326//   If the relay agent does decide to relay the request, it MUST examine
 327//   the 'giaddr' ("gateway" IP address) field.  If this field is zero,
 328//   the relay agent MUST fill this field with the IP address of the
 329//   interface on which the request was received.  If the interface has
 330//   more than one IP address logically associated with it, the relay
 331//   agent SHOULD choose one IP address associated with that interface and
 332//   use it consistently for all BOOTP messages it relays.  If the
 333//   'giaddr' field contains some non-zero value, the 'giaddr' field MUST
 334//   NOT be modified.  The relay agent MUST NOT, under any circumstances,
 335//   fill the 'giaddr' field with a broadcast address as is suggested in
 336//   [1] (Section 8, sixth paragraph).
 337
 338// but why? what if server can't route such IP? Client ifaces may be, say, NATed!
 339
 340// 4.1.2 BOOTREPLY Messages
 341//   BOOTP relay agents relay BOOTREPLY messages only to BOOTP clients.
 342//   It is the responsibility of BOOTP servers to send BOOTREPLY messages
 343//   directly to the relay agent identified in the 'giaddr' field.
 344// (yeah right, unless it is impossible... see comment above)
 345//   Therefore, a relay agent may assume that all BOOTREPLY messages it
 346//   receives are intended for BOOTP clients on its directly-connected
 347//   networks.
 348//
 349//   When a relay agent receives a BOOTREPLY message, it should examine
 350//   the BOOTP 'giaddr', 'yiaddr', 'chaddr', 'htype', and 'hlen' fields.
 351//   These fields should provide adequate information for the relay agent
 352//   to deliver the BOOTREPLY message to the client.
 353//
 354//   The 'giaddr' field can be used to identify the logical interface from
 355//   which the reply must be sent (i.e., the host or router interface
 356//   connected to the same network as the BOOTP client).  If the content
 357//   of the 'giaddr' field does not match one of the relay agent's
 358//   directly-connected logical interfaces, the BOOTREPLY messsage MUST be
 359//   silently discarded.
 360                                if (udhcp_read_interface(iface_list[i], NULL, &dhcp_msg.gateway_nip, NULL)) {
 361                                        /* Fall back to our IP on server iface */
 362// this makes more sense!
 363                                        dhcp_msg.gateway_nip = our_nip;
 364                                }
 365// maybe dhcp_msg.hops++? drop packets with too many hops (RFC 1542 says 4 or 16)?
 366                                pass_to_server(&dhcp_msg, packlen, i, fds, &client_addr, &server_addr);
 367                        }
 368                }
 369                xid_expire();
 370        } /* while (1) */
 371
 372        /* return 0; - not reached */
 373}
 374