Home | History | Annotate | Line # | Download | only in relay
      1  1.5  christos /*	$NetBSD: dhcrelay.c,v 1.6 2022/04/03 01:10:59 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /* dhcrelay.c
      4  1.1  christos 
      5  1.1  christos    DHCP/BOOTP Relay Agent. */
      6  1.1  christos 
      7  1.1  christos /*
      8  1.6  christos  * Copyright(c) 2004-2022 by Internet Systems Consortium, Inc.("ISC")
      9  1.1  christos  * Copyright(c) 1997-2003 by Internet Software Consortium
     10  1.1  christos  *
     11  1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
     12  1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
     13  1.1  christos  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     14  1.1  christos  *
     15  1.1  christos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
     16  1.1  christos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     17  1.1  christos  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
     18  1.1  christos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     19  1.1  christos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     20  1.1  christos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     21  1.1  christos  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     22  1.1  christos  *
     23  1.1  christos  *   Internet Systems Consortium, Inc.
     24  1.6  christos  *   PO Box 360
     25  1.6  christos  *   Newmarket, NH 03857 USA
     26  1.1  christos  *   <info (at) isc.org>
     27  1.1  christos  *   https://www.isc.org/
     28  1.1  christos  *
     29  1.1  christos  */
     30  1.1  christos 
     31  1.1  christos #include <sys/cdefs.h>
     32  1.5  christos __RCSID("$NetBSD: dhcrelay.c,v 1.6 2022/04/03 01:10:59 christos Exp $");
     33  1.1  christos 
     34  1.1  christos #include "dhcpd.h"
     35  1.1  christos #include <syslog.h>
     36  1.1  christos #include <signal.h>
     37  1.1  christos #include <sys/time.h>
     38  1.1  christos #include <isc/file.h>
     39  1.1  christos 
     40  1.1  christos TIME default_lease_time = 43200; /* 12 hours... */
     41  1.1  christos TIME max_lease_time = 86400; /* 24 hours... */
     42  1.1  christos struct tree_cache *global_options[256];
     43  1.1  christos 
     44  1.1  christos struct option *requested_opts[2];
     45  1.1  christos 
     46  1.1  christos /* Needed to prevent linking against conflex.c. */
     47  1.1  christos int lexline;
     48  1.1  christos int lexchar;
     49  1.1  christos char *token_line;
     50  1.1  christos char *tlname;
     51  1.1  christos 
     52  1.1  christos const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID;
     53  1.1  christos isc_boolean_t no_dhcrelay_pid = ISC_FALSE;
     54  1.1  christos /* False (default) => we write and use a pid file */
     55  1.1  christos isc_boolean_t no_pid_file = ISC_FALSE;
     56  1.1  christos 
     57  1.1  christos int bogus_agent_drops = 0;	/* Packets dropped because agent option
     58  1.1  christos 				   field was specified and we're not relaying
     59  1.1  christos 				   packets that already have an agent option
     60  1.1  christos 				   specified. */
     61  1.1  christos int bogus_giaddr_drops = 0;	/* Packets sent to us to relay back to a
     62  1.1  christos 				   client, but with a bogus giaddr. */
     63  1.1  christos int client_packets_relayed = 0;	/* Packets relayed from client to server. */
     64  1.1  christos int server_packet_errors = 0;	/* Errors sending packets to servers. */
     65  1.1  christos int server_packets_relayed = 0;	/* Packets relayed from server to client. */
     66  1.1  christos int client_packet_errors = 0;	/* Errors sending packets to clients. */
     67  1.1  christos 
     68  1.1  christos int add_agent_options = 0;	/* If nonzero, add relay agent options. */
     69  1.1  christos int add_rfc3527_suboption = 0;	/* If nonzero, add RFC3527 link selection sub-option. */
     70  1.1  christos 
     71  1.1  christos int agent_option_errors = 0;    /* Number of packets forwarded without
     72  1.1  christos 				   agent options because there was no room. */
     73  1.1  christos int drop_agent_mismatches = 0;	/* If nonzero, drop server replies that
     74  1.1  christos 				   don't have matching circuit-id's. */
     75  1.1  christos int corrupt_agent_options = 0;	/* Number of packets dropped because
     76  1.1  christos 				   relay agent information option was bad. */
     77  1.1  christos int missing_agent_option = 0;	/* Number of packets dropped because no
     78  1.1  christos 				   RAI option matching our ID was found. */
     79  1.1  christos int bad_circuit_id = 0;		/* Circuit ID option in matching RAI option
     80  1.1  christos 				   did not match any known circuit ID. */
     81  1.1  christos int missing_circuit_id = 0;	/* Circuit ID option in matching RAI option
     82  1.1  christos 				   was missing. */
     83  1.1  christos int max_hop_count = 10;		/* Maximum hop count */
     84  1.1  christos 
     85  1.1  christos int no_daemon = 0;
     86  1.1  christos int dfd[2] = { -1, -1 };
     87  1.1  christos 
     88  1.1  christos #ifdef DHCPv6
     89  1.1  christos 	/* Force use of DHCPv6 interface-id option. */
     90  1.1  christos isc_boolean_t use_if_id = ISC_FALSE;
     91  1.1  christos #endif
     92  1.1  christos 
     93  1.1  christos 	/* Maximum size of a packet with agent options added. */
     94  1.1  christos int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
     95  1.1  christos 
     96  1.1  christos 	/* What to do about packets we're asked to relay that
     97  1.1  christos 	   already have a relay option: */
     98  1.1  christos enum { forward_and_append,	/* Forward and append our own relay option. */
     99  1.1  christos        forward_and_replace,	/* Forward, but replace theirs with ours. */
    100  1.1  christos        forward_untouched,	/* Forward without changes. */
    101  1.1  christos        discard } agent_relay_mode = forward_and_replace;
    102  1.1  christos 
    103  1.6  christos extern u_int16_t local_port;
    104  1.6  christos extern u_int16_t remote_port;
    105  1.1  christos 
    106  1.1  christos /* Relay agent server list. */
    107  1.1  christos struct server_list {
    108  1.1  christos 	struct server_list *next;
    109  1.1  christos 	struct sockaddr_in to;
    110  1.1  christos } *servers;
    111  1.1  christos 
    112  1.1  christos struct interface_info *uplink = NULL;
    113  1.6  christos isc_boolean_t use_fake_gw = ISC_FALSE;
    114  1.6  christos struct in_addr gw = {0};
    115  1.1  christos 
    116  1.1  christos #ifdef DHCPv6
    117  1.1  christos struct stream_list {
    118  1.1  christos 	struct stream_list *next;
    119  1.1  christos 	struct interface_info *ifp;
    120  1.1  christos 	struct sockaddr_in6 link;
    121  1.1  christos 	int id;
    122  1.1  christos } *downstreams, *upstreams;
    123  1.1  christos 
    124  1.3  christos #ifndef UNIT_TEST
    125  1.1  christos static struct stream_list *parse_downstream(char *);
    126  1.1  christos static struct stream_list *parse_upstream(char *);
    127  1.1  christos static void setup_streams(void);
    128  1.3  christos #endif /* UNIT_TEST */
    129  1.1  christos 
    130  1.1  christos /*
    131  1.1  christos  * A pointer to a subscriber id to add to the message we forward.
    132  1.1  christos  * This is primarily for testing purposes as we only have one id
    133  1.1  christos  * for the entire relay and don't determine one per client which
    134  1.1  christos  * would be more useful.
    135  1.1  christos  */
    136  1.1  christos char *dhcrelay_sub_id = NULL;
    137  1.1  christos #endif
    138  1.1  christos 
    139  1.2  christos libdhcp_callbacks_t dhcrelay_callbacks = {
    140  1.2  christos 	&local_port,
    141  1.2  christos 	&remote_port,
    142  1.2  christos 	classify,
    143  1.2  christos 	check_collection,
    144  1.2  christos 	dhcp,
    145  1.2  christos #ifdef DHCPv6
    146  1.2  christos 	dhcpv6,
    147  1.2  christos #endif /* DHCPv6 */
    148  1.2  christos 	bootp,
    149  1.2  christos 	find_class,
    150  1.2  christos 	parse_allow_deny,
    151  1.2  christos 	dhcp_set_control_state,
    152  1.2  christos };
    153  1.2  christos 
    154  1.3  christos #ifndef UNIT_TEST
    155  1.1  christos static void do_relay4(struct interface_info *, struct dhcp_packet *,
    156  1.1  christos 	              unsigned int, unsigned int, struct iaddr,
    157  1.1  christos 		      struct hardware *);
    158  1.3  christos #endif /* UNIT_TEST */
    159  1.1  christos 
    160  1.3  christos extern int add_relay_agent_options(struct interface_info *,
    161  1.3  christos 				            struct dhcp_packet *, unsigned,
    162  1.3  christos 				            struct in_addr);
    163  1.3  christos extern int find_interface_by_agent_option(struct dhcp_packet *,
    164  1.3  christos 			                       struct interface_info **, u_int8_t *, int);
    165  1.3  christos 
    166  1.3  christos extern int strip_relay_agent_options(struct interface_info *,
    167  1.3  christos 				              struct interface_info **,
    168  1.3  christos 				              struct dhcp_packet *, unsigned);
    169  1.3  christos 
    170  1.3  christos #ifndef UNIT_TEST
    171  1.1  christos static void request_v4_interface(const char* name, int flags);
    172  1.1  christos 
    173  1.1  christos static const char copyright[] =
    174  1.6  christos "Copyright 2004-2022 Internet Systems Consortium.";
    175  1.1  christos static const char arr[] = "All rights reserved.";
    176  1.1  christos static const char message[] =
    177  1.1  christos "Internet Systems Consortium DHCP Relay Agent";
    178  1.1  christos static const char url[] =
    179  1.1  christos "For info, please visit https://www.isc.org/software/dhcp/";
    180  1.1  christos 
    181  1.1  christos char *progname;
    182  1.1  christos 
    183  1.1  christos #ifdef DHCPv6
    184  1.1  christos #ifdef RELAY_PORT
    185  1.1  christos #define DHCRELAY_USAGE \
    186  1.1  christos "Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \
    187  1.1  christos "                     [-A <length>] [-c <hops>]\n" \
    188  1.1  christos "                     [-p <port> | -rp <relay-port>]\n" \
    189  1.1  christos "                     [-pf <pid-file>] [--no-pid]\n"\
    190  1.1  christos "                     [-m append|replace|forward|discard]\n" \
    191  1.1  christos "                     [-i interface0 [ ... -i interfaceN]\n" \
    192  1.1  christos "                     [-iu interface0 [ ... -iu interfaceN]\n" \
    193  1.1  christos "                     [-id interface0 [ ... -id interfaceN]\n" \
    194  1.6  christos "                     [-U interface] [-g <ip-address>]\n" \
    195  1.1  christos "                     server0 [ ... serverN]\n\n" \
    196  1.1  christos "       %s -6   [-d] [-q] [-I] [-c <hops>]\n" \
    197  1.1  christos "                     [-p <port> | -rp <relay-port>]\n" \
    198  1.1  christos "                     [-pf <pid-file>] [--no-pid]\n" \
    199  1.1  christos "                     [-s <subscriber-id>]\n" \
    200  1.1  christos "                     -l lower0 [ ... -l lowerN]\n" \
    201  1.1  christos "                     -u upper0 [ ... -u upperN]\n" \
    202  1.1  christos "           lower (client link): [address%%]interface[#index]\n" \
    203  1.1  christos "           upper (server link): [address%%]interface\n\n" \
    204  1.1  christos "       %s {--version|--help|-h}"
    205  1.1  christos #else
    206  1.1  christos #define DHCRELAY_USAGE \
    207  1.1  christos "Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \
    208  1.1  christos "                     [-A <length>] [-c <hops>] [-p <port>]\n" \
    209  1.1  christos "                     [-pf <pid-file>] [--no-pid]\n"\
    210  1.1  christos "                     [-m append|replace|forward|discard]\n" \
    211  1.1  christos "                     [-i interface0 [ ... -i interfaceN]\n" \
    212  1.1  christos "                     [-iu interface0 [ ... -iu interfaceN]\n" \
    213  1.1  christos "                     [-id interface0 [ ... -id interfaceN]\n" \
    214  1.6  christos "                     [-U interface] [-g <ip-address>]\n" \
    215  1.1  christos "                     server0 [ ... serverN]\n\n" \
    216  1.1  christos "       %s -6   [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
    217  1.1  christos "                     [-pf <pid-file>] [--no-pid]\n" \
    218  1.1  christos "                     [-s <subscriber-id>]\n" \
    219  1.1  christos "                     -l lower0 [ ... -l lowerN]\n" \
    220  1.1  christos "                     -u upper0 [ ... -u upperN]\n" \
    221  1.1  christos "           lower (client link): [address%%]interface[#index]\n" \
    222  1.1  christos "           upper (server link): [address%%]interface\n\n" \
    223  1.1  christos "       %s {--version|--help|-h}"
    224  1.1  christos #endif
    225  1.1  christos #else /* !DHCPv6 */
    226  1.1  christos #ifdef RELAY_PORT
    227  1.1  christos #define DHCRELAY_USAGE \
    228  1.1  christos "Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>]\n" \
    229  1.1  christos "                [-p <port> | -rp <relay-port>]\n" \
    230  1.1  christos "                [-pf <pid-file>] [--no-pid]\n" \
    231  1.1  christos "                [-m append|replace|forward|discard]\n" \
    232  1.1  christos "                [-i interface0 [ ... -i interfaceN]\n" \
    233  1.1  christos "                [-iu interface0 [ ... -iu interfaceN]\n" \
    234  1.1  christos "                [-id interface0 [ ... -id interfaceN]\n" \
    235  1.6  christos "                [-U interface] [-g <ip-address>]\n" \
    236  1.1  christos "                server0 [ ... serverN]\n\n" \
    237  1.1  christos "       %s {--version|--help|-h}"
    238  1.1  christos #else
    239  1.1  christos #define DHCRELAY_USAGE \
    240  1.1  christos "Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
    241  1.1  christos "                [-pf <pid-file>] [--no-pid]\n" \
    242  1.1  christos "                [-m append|replace|forward|discard]\n" \
    243  1.1  christos "                [-i interface0 [ ... -i interfaceN]\n" \
    244  1.1  christos "                [-iu interface0 [ ... -iu interfaceN]\n" \
    245  1.1  christos "                [-id interface0 [ ... -id interfaceN]\n" \
    246  1.6  christos "                [-U interface] [-g <ip-address>]\n" \
    247  1.1  christos "                server0 [ ... serverN]\n\n" \
    248  1.1  christos "       %s {--version|--help|-h}"
    249  1.1  christos #endif
    250  1.1  christos #endif
    251  1.1  christos 
    252  1.1  christos /*!
    253  1.1  christos  *
    254  1.1  christos  * \brief Print the generic usage message
    255  1.1  christos  *
    256  1.1  christos  * If the user has provided an incorrect command line print out
    257  1.1  christos  * the description of the command line.  The arguments provide
    258  1.1  christos  * a way for the caller to request more specific information about
    259  1.1  christos  * the error be printed as well.  Mostly this will be that some
    260  1.6  christos  * command doesn't include its argument.
    261  1.1  christos  *
    262  1.1  christos  * \param sfmt - The basic string and format for the specific error
    263  1.6  christos  * \param sarg - Generally the offending argument from the command line.
    264  1.1  christos  *
    265  1.1  christos  * \return Nothing
    266  1.1  christos  */
    267  1.1  christos 
    268  1.1  christos #include <sys/cdefs.h>
    269  1.5  christos __RCSID("$NetBSD: dhcrelay.c,v 1.6 2022/04/03 01:10:59 christos Exp $");
    270  1.1  christos static const char use_noarg[] = "No argument for command: %s";
    271  1.1  christos #ifdef RELAY_PORT
    272  1.1  christos static const char use_port_defined[] = "Port already set, %s inappropriate";
    273  1.1  christos #if !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE)
    274  1.1  christos static const char bpf_sock_support[] = "Only LPF and BPF are supported: %s";
    275  1.1  christos #endif
    276  1.1  christos #endif
    277  1.1  christos #ifdef DHCPv6
    278  1.1  christos static const char use_badproto[] = "Protocol already set, %s inappropriate";
    279  1.1  christos static const char use_v4command[] = "Command not used for DHCPv6: %s";
    280  1.1  christos static const char use_v6command[] = "Command not used for DHCPv4: %s";
    281  1.1  christos #endif
    282  1.1  christos 
    283  1.1  christos static void
    284  1.1  christos usage(const char *sfmt, const char *sarg) {
    285  1.1  christos 	log_info("%s %s", message, PACKAGE_VERSION);
    286  1.1  christos 	log_info(copyright);
    287  1.1  christos 	log_info(arr);
    288  1.1  christos 	log_info(url);
    289  1.1  christos 
    290  1.1  christos 	/* If desired print out the specific error message */
    291  1.1  christos #ifdef PRINT_SPECIFIC_CL_ERRORS
    292  1.1  christos 	if (sfmt != NULL)
    293  1.1  christos 		log_error(sfmt, sarg);
    294  1.1  christos #endif
    295  1.1  christos 
    296  1.1  christos 	log_fatal(DHCRELAY_USAGE,
    297  1.1  christos #ifdef DHCPv6
    298  1.1  christos 		  isc_file_basename(progname),
    299  1.1  christos #endif
    300  1.1  christos 		  isc_file_basename(progname),
    301  1.1  christos 		  isc_file_basename(progname));
    302  1.1  christos }
    303  1.1  christos 
    304  1.3  christos int
    305  1.1  christos main(int argc, char **argv) {
    306  1.1  christos 	isc_result_t status;
    307  1.1  christos 	struct servent *ent;
    308  1.1  christos 	struct server_list *sp = NULL;
    309  1.1  christos 	char *service_local = NULL, *service_remote = NULL;
    310  1.1  christos 	u_int16_t port_local = 0, port_remote = 0;
    311  1.1  christos 	int quiet = 0;
    312  1.1  christos 	int fd;
    313  1.1  christos 	int i;
    314  1.1  christos #ifdef RELAY_PORT
    315  1.1  christos 	int port_defined = 0;
    316  1.1  christos #endif
    317  1.1  christos #ifdef DHCPv6
    318  1.1  christos 	struct stream_list *sl = NULL;
    319  1.1  christos 	int local_family_set = 0;
    320  1.1  christos #endif
    321  1.1  christos 
    322  1.4  christos 	libdhcp_callbacks_register(&dhcrelay_callbacks);
    323  1.4  christos 
    324  1.1  christos #ifdef OLD_LOG_NAME
    325  1.1  christos 	progname = "dhcrelay";
    326  1.1  christos #else
    327  1.1  christos 	progname = argv[0];
    328  1.1  christos #endif
    329  1.1  christos 
    330  1.1  christos 	/* Make sure that file descriptors 0(stdin), 1,(stdout), and
    331  1.1  christos 	   2(stderr) are open. To do this, we assume that when we
    332  1.1  christos 	   open a file the lowest available file descriptor is used. */
    333  1.1  christos 	fd = open("/dev/null", O_RDWR);
    334  1.1  christos 	if (fd == 0)
    335  1.1  christos 		fd = open("/dev/null", O_RDWR);
    336  1.1  christos 	if (fd == 1)
    337  1.1  christos 		fd = open("/dev/null", O_RDWR);
    338  1.1  christos 	if (fd == 2)
    339  1.1  christos 		log_perror = 0; /* No sense logging to /dev/null. */
    340  1.1  christos 	else if (fd != -1)
    341  1.1  christos 		close(fd);
    342  1.1  christos 
    343  1.1  christos 	openlog(isc_file_basename(progname), DHCP_LOG_OPTIONS, LOG_DAEMON);
    344  1.1  christos 
    345  1.1  christos #if !defined(DEBUG)
    346  1.1  christos 	setlogmask(LOG_UPTO(LOG_INFO));
    347  1.3  christos #endif
    348  1.1  christos 
    349  1.1  christos 	/* Parse arguments changing no_daemon */
    350  1.1  christos 	for (i = 1; i < argc; i++) {
    351  1.1  christos 		if (!strcmp(argv[i], "-d")) {
    352  1.1  christos 			no_daemon = 1;
    353  1.1  christos 		} else if (!strcmp(argv[i], "--version")) {
    354  1.1  christos 			log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
    355  1.1  christos 			exit(0);
    356  1.1  christos 		} else if (!strcmp(argv[i], "--help") ||
    357  1.1  christos 			   !strcmp(argv[i], "-h")) {
    358  1.1  christos 			log_info(DHCRELAY_USAGE,
    359  1.1  christos #ifdef DHCPv6
    360  1.1  christos 				 isc_file_basename(progname),
    361  1.1  christos #endif
    362  1.1  christos 				 isc_file_basename(progname),
    363  1.1  christos 				 isc_file_basename(progname));
    364  1.1  christos 			exit(0);
    365  1.1  christos 		}
    366  1.1  christos 	}
    367  1.1  christos 	/* When not forbidden prepare to become a daemon */
    368  1.1  christos 	if (!no_daemon) {
    369  1.1  christos 		int pid;
    370  1.1  christos 
    371  1.1  christos 		if (pipe(dfd) == -1)
    372  1.1  christos 			log_fatal("Can't get pipe: %m");
    373  1.1  christos 		if ((pid = fork ()) < 0)
    374  1.1  christos 			log_fatal("Can't fork daemon: %m");
    375  1.1  christos 		if (pid != 0) {
    376  1.1  christos 			/* Parent: wait for the child to start */
    377  1.1  christos 			int n;
    378  1.1  christos 
    379  1.1  christos 			(void) close(dfd[1]);
    380  1.1  christos 			do {
    381  1.1  christos 				char buf;
    382  1.1  christos 
    383  1.1  christos 				n = read(dfd[0], &buf, 1);
    384  1.1  christos 				if (n == 1)
    385  1.1  christos 					_exit(0);
    386  1.1  christos 			} while (n == -1 && errno == EINTR);
    387  1.1  christos 			_exit(1);
    388  1.1  christos 		}
    389  1.1  christos 		/* Child */
    390  1.1  christos 		(void) close(dfd[0]);
    391  1.1  christos 	}
    392  1.1  christos 
    393  1.1  christos 
    394  1.1  christos 	/* Set up the isc and dns library managers */
    395  1.1  christos 	status = dhcp_context_create(DHCP_CONTEXT_PRE_DB, NULL, NULL);
    396  1.1  christos 	if (status != ISC_R_SUCCESS)
    397  1.1  christos 		log_fatal("Can't initialize context: %s",
    398  1.1  christos 			  isc_result_totext(status));
    399  1.1  christos 
    400  1.1  christos 	/* Set up the OMAPI. */
    401  1.1  christos 	status = omapi_init();
    402  1.1  christos 	if (status != ISC_R_SUCCESS)
    403  1.1  christos 		log_fatal("Can't initialize OMAPI: %s",
    404  1.1  christos 			   isc_result_totext(status));
    405  1.1  christos 
    406  1.1  christos 	/* Set up the OMAPI wrappers for the interface object. */
    407  1.1  christos 	interface_setup();
    408  1.1  christos 
    409  1.1  christos 	for (i = 1; i < argc; i++) {
    410  1.1  christos 		if (!strcmp(argv[i], "-4")) {
    411  1.1  christos #ifdef DHCPv6
    412  1.1  christos 			if (local_family_set && (local_family == AF_INET6)) {
    413  1.1  christos 				usage(use_badproto, "-4");
    414  1.1  christos 			}
    415  1.1  christos 			local_family_set = 1;
    416  1.1  christos 			local_family = AF_INET;
    417  1.1  christos 		} else if (!strcmp(argv[i], "-6")) {
    418  1.1  christos 			if (local_family_set && (local_family == AF_INET)) {
    419  1.1  christos 				usage(use_badproto, "-6");
    420  1.1  christos 			}
    421  1.1  christos 			local_family_set = 1;
    422  1.1  christos 			local_family = AF_INET6;
    423  1.1  christos #endif
    424  1.1  christos 		} else if (!strcmp(argv[i], "-d")) {
    425  1.1  christos 			/* no_daemon = 1; */
    426  1.1  christos 		} else if (!strcmp(argv[i], "-q")) {
    427  1.1  christos 			quiet = 1;
    428  1.1  christos 			quiet_interface_discovery = 1;
    429  1.1  christos 		} else if (!strcmp(argv[i], "-p")) {
    430  1.1  christos 			if (++i == argc)
    431  1.1  christos 				usage(use_noarg, argv[i-1]);
    432  1.1  christos #ifdef RELAY_PORT
    433  1.1  christos 			if (port_defined)
    434  1.1  christos 				usage(use_port_defined, argv[i-1]);
    435  1.1  christos 			port_defined = 1;
    436  1.1  christos #endif
    437  1.1  christos 			local_port = validate_port(argv[i]);
    438  1.1  christos 			log_debug("binding to user-specified port %d",
    439  1.1  christos 				  ntohs(local_port));
    440  1.1  christos #ifdef RELAY_PORT
    441  1.1  christos 		} else if (!strcmp(argv[i], "-rp")) {
    442  1.1  christos 			if (++i == argc)
    443  1.1  christos 				usage(use_noarg, argv[i-1]);
    444  1.1  christos 			if (port_defined)
    445  1.1  christos 				usage(use_port_defined, argv[i-1]);
    446  1.1  christos 			port_defined = 1;
    447  1.1  christos 			relay_port = validate_port(argv[i]);
    448  1.1  christos 			log_debug("binding to user-specified relay port %d",
    449  1.1  christos 				  ntohs(relay_port));
    450  1.1  christos 			add_agent_options = 1;
    451  1.1  christos #endif
    452  1.1  christos 		} else if (!strcmp(argv[i], "-c")) {
    453  1.1  christos 			int hcount;
    454  1.1  christos 			if (++i == argc)
    455  1.1  christos 				usage(use_noarg, argv[i-1]);
    456  1.1  christos 			hcount = atoi(argv[i]);
    457  1.1  christos 			if (hcount <= 255)
    458  1.1  christos 				max_hop_count= hcount;
    459  1.1  christos 			else
    460  1.1  christos 				usage("Bad hop count to -c: %s", argv[i]);
    461  1.1  christos  		} else if (!strcmp(argv[i], "-i")) {
    462  1.1  christos #ifdef DHCPv6
    463  1.1  christos 			if (local_family_set && (local_family == AF_INET6)) {
    464  1.1  christos 				usage(use_v4command, argv[i]);
    465  1.1  christos 			}
    466  1.1  christos 			local_family_set = 1;
    467  1.1  christos 			local_family = AF_INET;
    468  1.1  christos #endif
    469  1.1  christos 			if (++i == argc) {
    470  1.1  christos 				usage(use_noarg, argv[i-1]);
    471  1.1  christos 			}
    472  1.1  christos 
    473  1.1  christos 			request_v4_interface(argv[i], INTERFACE_STREAMS);
    474  1.1  christos 		} else if (!strcmp(argv[i], "-iu")) {
    475  1.1  christos #ifdef DHCPv6
    476  1.1  christos 			if (local_family_set && (local_family == AF_INET6)) {
    477  1.1  christos 				usage(use_v4command, argv[i]);
    478  1.1  christos 			}
    479  1.1  christos 			local_family_set = 1;
    480  1.1  christos 			local_family = AF_INET;
    481  1.1  christos #endif
    482  1.1  christos 			if (++i == argc) {
    483  1.1  christos 				usage(use_noarg, argv[i-1]);
    484  1.1  christos 			}
    485  1.1  christos 
    486  1.1  christos 			request_v4_interface(argv[i], INTERFACE_UPSTREAM);
    487  1.1  christos 		} else if (!strcmp(argv[i], "-id")) {
    488  1.1  christos #ifdef DHCPv6
    489  1.1  christos 			if (local_family_set && (local_family == AF_INET6)) {
    490  1.1  christos 				usage(use_v4command, argv[i]);
    491  1.1  christos 			}
    492  1.1  christos 			local_family_set = 1;
    493  1.1  christos 			local_family = AF_INET;
    494  1.1  christos #endif
    495  1.1  christos 			if (++i == argc) {
    496  1.1  christos 				usage(use_noarg, argv[i-1]);
    497  1.1  christos 			}
    498  1.1  christos 
    499  1.1  christos 			request_v4_interface(argv[i], INTERFACE_DOWNSTREAM);
    500  1.1  christos 		} else if (!strcmp(argv[i], "-a")) {
    501  1.1  christos #ifdef DHCPv6
    502  1.1  christos 			if (local_family_set && (local_family == AF_INET6)) {
    503  1.1  christos 				usage(use_v4command, argv[i]);
    504  1.1  christos 			}
    505  1.1  christos 			local_family_set = 1;
    506  1.1  christos 			local_family = AF_INET;
    507  1.1  christos #endif
    508  1.1  christos 			add_agent_options = 1;
    509  1.1  christos 		} else if (!strcmp(argv[i], "-A")) {
    510  1.1  christos #ifdef DHCPv6
    511  1.1  christos 			if (local_family_set && (local_family == AF_INET6)) {
    512  1.1  christos 				usage(use_v4command, argv[i]);
    513  1.1  christos 			}
    514  1.1  christos 			local_family_set = 1;
    515  1.1  christos 			local_family = AF_INET;
    516  1.1  christos #endif
    517  1.1  christos 			if (++i == argc)
    518  1.1  christos 				usage(use_noarg, argv[i-1]);
    519  1.1  christos 
    520  1.1  christos 			dhcp_max_agent_option_packet_length = atoi(argv[i]);
    521  1.1  christos 
    522  1.1  christos 			if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
    523  1.1  christos 				log_fatal("%s: packet length exceeds "
    524  1.1  christos 					  "longest possible MTU\n",
    525  1.1  christos 					  argv[i]);
    526  1.1  christos 		} else if (!strcmp(argv[i], "-m")) {
    527  1.1  christos #ifdef DHCPv6
    528  1.1  christos 			if (local_family_set && (local_family == AF_INET6)) {
    529  1.1  christos 				usage(use_v4command, argv[i]);
    530  1.1  christos 			}
    531  1.1  christos 			local_family_set = 1;
    532  1.1  christos 			local_family = AF_INET;
    533  1.1  christos #endif
    534  1.1  christos 			if (++i == argc)
    535  1.1  christos 				usage(use_noarg, argv[i-1]);
    536  1.1  christos 			if (!strcasecmp(argv[i], "append")) {
    537  1.1  christos 				agent_relay_mode = forward_and_append;
    538  1.1  christos 			} else if (!strcasecmp(argv[i], "replace")) {
    539  1.1  christos 				agent_relay_mode = forward_and_replace;
    540  1.1  christos 			} else if (!strcasecmp(argv[i], "forward")) {
    541  1.1  christos 				agent_relay_mode = forward_untouched;
    542  1.1  christos 			} else if (!strcasecmp(argv[i], "discard")) {
    543  1.1  christos 				agent_relay_mode = discard;
    544  1.1  christos 			} else
    545  1.1  christos 				usage("Unknown argument to -m: %s", argv[i]);
    546  1.1  christos 		} else if (!strcmp(argv [i], "-U")) {
    547  1.1  christos 			if (++i == argc)
    548  1.1  christos 				usage(use_noarg, argv[i-1]);
    549  1.1  christos 
    550  1.1  christos 			if (uplink) {
    551  1.1  christos 				usage("more than one uplink (-U) specified: %s"
    552  1.1  christos 				      ,argv[i]);
    553  1.1  christos 			}
    554  1.1  christos 
    555  1.1  christos 			/* Allocate the uplink interface */
    556  1.1  christos 			status = interface_allocate(&uplink, MDL);
    557  1.1  christos 			if (status != ISC_R_SUCCESS) {
    558  1.1  christos 				log_fatal("%s: uplink interface_allocate: %s",
    559  1.1  christos 					 argv[i], isc_result_totext(status));
    560  1.1  christos 			}
    561  1.3  christos 
    562  1.1  christos 			if (strlen(argv[i]) >= sizeof(uplink->name)) {
    563  1.1  christos 				log_fatal("%s: uplink name too long,"
    564  1.1  christos 					  " it cannot exceed: %ld characters",
    565  1.1  christos 					  argv[i], (long)(sizeof(uplink->name) - 1));
    566  1.1  christos 			}
    567  1.1  christos 
    568  1.1  christos 			uplink->name[sizeof(uplink->name) - 1] = 0x00;
    569  1.1  christos 			strncpy(uplink->name, argv[i],
    570  1.1  christos 				sizeof(uplink->name) - 1);
    571  1.1  christos 			interface_snorf(uplink, (INTERFACE_REQUESTED |
    572  1.1  christos 						INTERFACE_STREAMS));
    573  1.1  christos 
    574  1.1  christos 			/* Turn on -a, in case they don't do so explicitly */
    575  1.1  christos 			add_agent_options = 1;
    576  1.1  christos 			add_rfc3527_suboption = 1;
    577  1.6  christos 		} else if (!strcmp(argv[i], "-g")) {
    578  1.6  christos 			if (++i == argc)
    579  1.6  christos 				usage(use_noarg, argv[i-1]);
    580  1.6  christos #ifdef DHCPv6
    581  1.6  christos 			if (local_family_set && (local_family == AF_INET6)) {
    582  1.6  christos 				usage(use_v4command, argv[i]);
    583  1.6  christos 			}
    584  1.6  christos 			local_family_set = 1;
    585  1.6  christos 			local_family = AF_INET;
    586  1.6  christos #endif
    587  1.6  christos 			if (inet_pton(AF_INET, argv[i], &gw) <= 0) {
    588  1.6  christos 				usage("Invalid gateway address '%s'", argv[i]);
    589  1.6  christos 			} else {
    590  1.6  christos 				use_fake_gw = ISC_TRUE;
    591  1.6  christos 			}
    592  1.1  christos 		} else if (!strcmp(argv[i], "-D")) {
    593  1.1  christos #ifdef DHCPv6
    594  1.1  christos 			if (local_family_set && (local_family == AF_INET6)) {
    595  1.1  christos 				usage(use_v4command, argv[i]);
    596  1.1  christos 			}
    597  1.1  christos 			local_family_set = 1;
    598  1.1  christos 			local_family = AF_INET;
    599  1.1  christos #endif
    600  1.1  christos 			drop_agent_mismatches = 1;
    601  1.1  christos #ifdef DHCPv6
    602  1.1  christos 		} else if (!strcmp(argv[i], "-I")) {
    603  1.1  christos 			if (local_family_set && (local_family == AF_INET)) {
    604  1.1  christos 				usage(use_v6command, argv[i]);
    605  1.1  christos 			}
    606  1.1  christos 			local_family_set = 1;
    607  1.1  christos 			local_family = AF_INET6;
    608  1.1  christos 			use_if_id = ISC_TRUE;
    609  1.1  christos 		} else if (!strcmp(argv[i], "-l")) {
    610  1.1  christos 			if (local_family_set && (local_family == AF_INET)) {
    611  1.1  christos 				usage(use_v6command, argv[i]);
    612  1.1  christos 			}
    613  1.1  christos 			local_family_set = 1;
    614  1.1  christos 			local_family = AF_INET6;
    615  1.1  christos 			if (downstreams != NULL)
    616  1.1  christos 				use_if_id = ISC_TRUE;
    617  1.1  christos 			if (++i == argc)
    618  1.1  christos 				usage(use_noarg, argv[i-1]);
    619  1.1  christos 			sl = parse_downstream(argv[i]);
    620  1.1  christos 			sl->next = downstreams;
    621  1.1  christos 			downstreams = sl;
    622  1.1  christos 		} else if (!strcmp(argv[i], "-u")) {
    623  1.1  christos 			if (local_family_set && (local_family == AF_INET)) {
    624  1.1  christos 				usage(use_v6command, argv[i]);
    625  1.1  christos 			}
    626  1.1  christos 			local_family_set = 1;
    627  1.1  christos 			local_family = AF_INET6;
    628  1.1  christos 			if (++i == argc)
    629  1.1  christos 				usage(use_noarg, argv[i-1]);
    630  1.1  christos 			sl = parse_upstream(argv[i]);
    631  1.1  christos 			sl->next = upstreams;
    632  1.1  christos 			upstreams = sl;
    633  1.1  christos 		} else if (!strcmp(argv[i], "-s")) {
    634  1.1  christos 			if (local_family_set && (local_family == AF_INET)) {
    635  1.1  christos 				usage(use_v6command, argv[i]);
    636  1.1  christos 			}
    637  1.1  christos 			local_family_set = 1;
    638  1.1  christos 			local_family = AF_INET6;
    639  1.1  christos 			if (++i == argc)
    640  1.1  christos 				usage(use_noarg, argv[i-1]);
    641  1.1  christos 			dhcrelay_sub_id = argv[i];
    642  1.1  christos #endif
    643  1.1  christos 		} else if (!strcmp(argv[i], "-pf")) {
    644  1.1  christos 			if (++i == argc)
    645  1.1  christos 				usage(use_noarg, argv[i-1]);
    646  1.1  christos 			path_dhcrelay_pid = argv[i];
    647  1.1  christos 			no_dhcrelay_pid = ISC_TRUE;
    648  1.1  christos 		} else if (!strcmp(argv[i], "--no-pid")) {
    649  1.1  christos 			no_pid_file = ISC_TRUE;
    650  1.1  christos  		} else if (argv[i][0] == '-') {
    651  1.1  christos 			usage("Unknown command: %s", argv[i]);
    652  1.1  christos  		} else {
    653  1.1  christos 			struct hostent *he;
    654  1.1  christos 			struct in_addr ia, *iap = NULL;
    655  1.1  christos 
    656  1.1  christos #ifdef DHCPv6
    657  1.1  christos 			if (local_family_set && (local_family == AF_INET6)) {
    658  1.1  christos 				usage(use_v4command, argv[i]);
    659  1.1  christos 			}
    660  1.1  christos 			local_family_set = 1;
    661  1.1  christos 			local_family = AF_INET;
    662  1.1  christos #endif
    663  1.1  christos 			if (inet_aton(argv[i], &ia)) {
    664  1.1  christos 				iap = &ia;
    665  1.1  christos 			} else {
    666  1.1  christos 				he = gethostbyname(argv[i]);
    667  1.1  christos 				if (!he) {
    668  1.1  christos 					log_error("%s: host unknown", argv[i]);
    669  1.1  christos 				} else {
    670  1.1  christos 					iap = ((struct in_addr *)
    671  1.1  christos 					       he->h_addr_list[0]);
    672  1.1  christos 				}
    673  1.1  christos 			}
    674  1.1  christos 
    675  1.1  christos 			if (iap) {
    676  1.1  christos 				sp = ((struct server_list *)
    677  1.1  christos 				      dmalloc(sizeof *sp, MDL));
    678  1.1  christos 				if (!sp)
    679  1.1  christos 					log_fatal("no memory for server.\n");
    680  1.1  christos 				sp->next = servers;
    681  1.1  christos 				servers = sp;
    682  1.1  christos 				memcpy(&sp->to.sin_addr, iap, sizeof *iap);
    683  1.1  christos 			}
    684  1.1  christos  		}
    685  1.1  christos 	}
    686  1.1  christos 
    687  1.1  christos #if defined(RELAY_PORT) && \
    688  1.1  christos     !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE)
    689  1.1  christos 	if (relay_port && (local_family == AF_INET))
    690  1.1  christos 		usage(bpf_sock_support, "-rp");
    691  1.1  christos #endif
    692  1.1  christos 
    693  1.1  christos 	/*
    694  1.1  christos 	 * If the user didn't specify a pid file directly
    695  1.1  christos 	 * find one from environment variables or defaults
    696  1.1  christos 	 */
    697  1.1  christos 	if (no_dhcrelay_pid == ISC_FALSE) {
    698  1.1  christos 		if (local_family == AF_INET) {
    699  1.1  christos 			path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
    700  1.1  christos 			if (path_dhcrelay_pid == NULL)
    701  1.1  christos 				path_dhcrelay_pid = _PATH_DHCRELAY_PID;
    702  1.1  christos 		}
    703  1.1  christos #ifdef DHCPv6
    704  1.1  christos 		else {
    705  1.1  christos 			path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
    706  1.1  christos 			if (path_dhcrelay_pid == NULL)
    707  1.1  christos 				path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
    708  1.1  christos 		}
    709  1.1  christos #endif
    710  1.1  christos 	}
    711  1.1  christos 
    712  1.1  christos 	if (!quiet) {
    713  1.1  christos 		log_info("%s %s", message, PACKAGE_VERSION);
    714  1.1  christos 		log_info(copyright);
    715  1.1  christos 		log_info(arr);
    716  1.1  christos 		log_info(url);
    717  1.3  christos 	} else
    718  1.1  christos 		log_perror = 0;
    719  1.1  christos 
    720  1.1  christos 	/* Set default port */
    721  1.1  christos 	if (local_family == AF_INET) {
    722  1.1  christos  		service_local = "bootps";
    723  1.1  christos  		service_remote = "bootpc";
    724  1.1  christos 		port_local = htons(67);
    725  1.1  christos  		port_remote = htons(68);
    726  1.1  christos 	}
    727  1.1  christos #ifdef DHCPv6
    728  1.1  christos 	else {
    729  1.1  christos 		service_local = "dhcpv6-server";
    730  1.1  christos 		service_remote = "dhcpv6-client";
    731  1.1  christos 		port_local = htons(547);
    732  1.1  christos 		port_remote = htons(546);
    733  1.1  christos 	}
    734  1.1  christos #endif
    735  1.1  christos 
    736  1.1  christos 	if (!local_port) {
    737  1.1  christos 		ent = getservbyname(service_local, "udp");
    738  1.1  christos 		if (ent)
    739  1.1  christos 			local_port = ent->s_port;
    740  1.1  christos 		else
    741  1.1  christos 			local_port = port_local;
    742  1.1  christos 
    743  1.1  christos 		ent = getservbyname(service_remote, "udp");
    744  1.1  christos 		if (ent)
    745  1.1  christos 			remote_port = ent->s_port;
    746  1.1  christos 		else
    747  1.1  christos 			remote_port = port_remote;
    748  1.1  christos 
    749  1.1  christos 		endservent();
    750  1.1  christos 	}
    751  1.1  christos 
    752  1.1  christos 	if (local_family == AF_INET) {
    753  1.1  christos 		/* We need at least one server */
    754  1.1  christos 		if (servers == NULL) {
    755  1.1  christos 			log_fatal("No servers specified.");
    756  1.1  christos 		}
    757  1.1  christos 
    758  1.1  christos 
    759  1.1  christos 		/* Set up the server sockaddrs. */
    760  1.1  christos 		for (sp = servers; sp; sp = sp->next) {
    761  1.1  christos 			sp->to.sin_port = local_port;
    762  1.1  christos 			sp->to.sin_family = AF_INET;
    763  1.1  christos #ifdef HAVE_SA_LEN
    764  1.1  christos 			sp->to.sin_len = sizeof sp->to;
    765  1.1  christos #endif
    766  1.1  christos 		}
    767  1.1  christos 	}
    768  1.1  christos #ifdef DHCPv6
    769  1.1  christos 	else {
    770  1.1  christos 		unsigned code;
    771  1.1  christos 
    772  1.1  christos 		/* We need at least one upstream and one downstream interface */
    773  1.1  christos 		if (upstreams == NULL || downstreams == NULL) {
    774  1.1  christos 			log_info("Must specify at least one lower "
    775  1.1  christos 				 "and one upper interface.\n");
    776  1.1  christos 			usage(NULL, NULL);
    777  1.1  christos 		}
    778  1.1  christos 
    779  1.1  christos 		/* Set up the initial dhcp option universe. */
    780  1.1  christos 		initialize_common_option_spaces();
    781  1.1  christos 
    782  1.1  christos 		/* Check requested options. */
    783  1.1  christos 		code = D6O_RELAY_MSG;
    784  1.1  christos 		if (!option_code_hash_lookup(&requested_opts[0],
    785  1.1  christos 					     dhcpv6_universe.code_hash,
    786  1.1  christos 					     &code, 0, MDL))
    787  1.1  christos 			log_fatal("Unable to find the RELAY_MSG "
    788  1.1  christos 				  "option definition.");
    789  1.1  christos 		code = D6O_INTERFACE_ID;
    790  1.1  christos 		if (!option_code_hash_lookup(&requested_opts[1],
    791  1.1  christos 					     dhcpv6_universe.code_hash,
    792  1.1  christos 					     &code, 0, MDL))
    793  1.1  christos 			log_fatal("Unable to find the INTERFACE_ID "
    794  1.1  christos 				  "option definition.");
    795  1.1  christos 	}
    796  1.1  christos #endif
    797  1.1  christos 
    798  1.1  christos 	/* Become a daemon... */
    799  1.1  christos 	if (!no_daemon) {
    800  1.1  christos 		char buf = 0;
    801  1.1  christos 		FILE *pf;
    802  1.1  christos 		int pfdesc;
    803  1.1  christos 
    804  1.1  christos 		log_perror = 0;
    805  1.1  christos 
    806  1.1  christos 		/* Signal parent we started successfully. */
    807  1.1  christos 		if (dfd[0] != -1 && dfd[1] != -1) {
    808  1.1  christos 			if (write(dfd[1], &buf, 1) != 1)
    809  1.1  christos 				log_fatal("write to parent: %m");
    810  1.1  christos 			(void) close(dfd[1]);
    811  1.1  christos 			dfd[0] = dfd[1] = -1;
    812  1.1  christos 		}
    813  1.1  christos 
    814  1.1  christos 		/* Create the pid file. */
    815  1.1  christos 		if (no_pid_file == ISC_FALSE) {
    816  1.1  christos 			pfdesc = open(path_dhcrelay_pid,
    817  1.1  christos 				      O_CREAT | O_TRUNC | O_WRONLY, 0644);
    818  1.1  christos 
    819  1.1  christos 			if (pfdesc < 0) {
    820  1.1  christos 				log_error("Can't create %s: %m",
    821  1.1  christos 					  path_dhcrelay_pid);
    822  1.1  christos 			} else {
    823  1.1  christos 				pf = fdopen(pfdesc, "w");
    824  1.1  christos 				if (!pf)
    825  1.1  christos 					log_error("Can't fdopen %s: %m",
    826  1.1  christos 						  path_dhcrelay_pid);
    827  1.1  christos 				else {
    828  1.1  christos 					fprintf(pf, "%ld\n",(long)getpid());
    829  1.1  christos 					fclose(pf);
    830  1.3  christos 				}
    831  1.1  christos 			}
    832  1.1  christos 		}
    833  1.1  christos 
    834  1.1  christos 		(void) close(0);
    835  1.1  christos 		(void) close(1);
    836  1.1  christos 		(void) close(2);
    837  1.1  christos 		(void) setsid();
    838  1.1  christos 
    839  1.1  christos 		IGNORE_RET (chdir("/"));
    840  1.1  christos 	}
    841  1.1  christos 
    842  1.2  christos 	/* Set up the isc and dns library managers */
    843  1.2  christos 	status = dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB,
    844  1.2  christos 				     NULL, NULL);
    845  1.2  christos 	if (status != ISC_R_SUCCESS)
    846  1.2  christos 		log_fatal("Can't initialize context: %s",
    847  1.2  christos 			  isc_result_totext(status));
    848  1.2  christos 
    849  1.2  christos 	/* Get the current time... */
    850  1.2  christos 	gettimeofday(&cur_tv, NULL);
    851  1.2  christos 
    852  1.2  christos 	/* Discover all the network interfaces. */
    853  1.2  christos 	discover_interfaces(DISCOVER_RELAY);
    854  1.2  christos 
    855  1.2  christos #ifdef DHCPv6
    856  1.2  christos 	if (local_family == AF_INET6)
    857  1.2  christos 		setup_streams();
    858  1.2  christos #endif
    859  1.2  christos 
    860  1.1  christos 	/* Set up the packet handler... */
    861  1.1  christos 	if (local_family == AF_INET)
    862  1.1  christos 		bootp_packet_handler = do_relay4;
    863  1.1  christos #ifdef DHCPv6
    864  1.1  christos 	else
    865  1.1  christos 		dhcpv6_packet_handler = do_packet6;
    866  1.1  christos #endif
    867  1.1  christos 
    868  1.1  christos #if defined(ENABLE_GENTLE_SHUTDOWN)
    869  1.1  christos 	/* no signal handlers until we deal with the side effects */
    870  1.1  christos         /* install signal handlers */
    871  1.1  christos 	signal(SIGINT, dhcp_signal_handler);   /* control-c */
    872  1.1  christos 	signal(SIGTERM, dhcp_signal_handler);  /* kill */
    873  1.1  christos #endif
    874  1.1  christos 
    875  1.1  christos 	/* Start dispatching packets and timeouts... */
    876  1.1  christos 	dispatch();
    877  1.1  christos 
    878  1.1  christos 	/* In fact dispatch() never returns. */
    879  1.1  christos 	return (0);
    880  1.1  christos }
    881  1.1  christos 
    882  1.1  christos static void
    883  1.1  christos do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
    884  1.1  christos 	  unsigned int length, unsigned int from_port, struct iaddr from,
    885  1.1  christos 	  struct hardware *hfrom) {
    886  1.1  christos 	struct server_list *sp;
    887  1.1  christos 	struct sockaddr_in to;
    888  1.1  christos 	struct interface_info *out;
    889  1.1  christos 	struct hardware hto, *htop;
    890  1.1  christos 
    891  1.1  christos 	if (packet->hlen > sizeof packet->chaddr) {
    892  1.1  christos 		log_info("Discarding packet with invalid hlen, received on "
    893  1.1  christos 			 "%s interface.", ip->name);
    894  1.1  christos 		return;
    895  1.1  christos 	}
    896  1.1  christos 	if (ip->address_count < 1 || ip->addresses == NULL) {
    897  1.1  christos 		log_info("Discarding packet received on %s interface that "
    898  1.1  christos 			 "has no IPv4 address assigned.", ip->name);
    899  1.1  christos 		return;
    900  1.1  christos 	}
    901  1.1  christos 
    902  1.1  christos 	/* Find the interface that corresponds to the giaddr
    903  1.1  christos 	   in the packet. */
    904  1.1  christos 	if (packet->giaddr.s_addr) {
    905  1.1  christos 		for (out = interfaces; out; out = out->next) {
    906  1.1  christos 			int i;
    907  1.1  christos 
    908  1.1  christos 			for (i = 0 ; i < out->address_count ; i++ ) {
    909  1.1  christos 				if (out->addresses[i].s_addr ==
    910  1.1  christos 				    packet->giaddr.s_addr) {
    911  1.1  christos 					i = -1;
    912  1.1  christos 					break;
    913  1.1  christos 				}
    914  1.1  christos 			}
    915  1.1  christos 
    916  1.1  christos 			if (i == -1)
    917  1.1  christos 				break;
    918  1.1  christos 		}
    919  1.1  christos 	} else {
    920  1.1  christos 		out = NULL;
    921  1.1  christos 	}
    922  1.1  christos 
    923  1.1  christos 	/* If it's a bootreply, forward it to the client. */
    924  1.1  christos 	if (packet->op == BOOTREPLY) {
    925  1.1  christos 		if (!(ip->flags & INTERFACE_UPSTREAM)) {
    926  1.1  christos 			log_debug("Dropping reply received on %s", ip->name);
    927  1.1  christos 			return;
    928  1.1  christos 		}
    929  1.1  christos 
    930  1.6  christos 		log_debug("BOOTREPLY giaddr: %s\n", inet_ntoa(packet->giaddr));
    931  1.1  christos 		if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
    932  1.1  christos 			can_unicast_without_arp(out)) {
    933  1.1  christos 			to.sin_addr = packet->yiaddr;
    934  1.1  christos 			to.sin_port = remote_port;
    935  1.1  christos 
    936  1.1  christos 			/* and hardware address is not broadcast */
    937  1.1  christos 			htop = &hto;
    938  1.1  christos 		} else {
    939  1.1  christos 			to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
    940  1.1  christos 			to.sin_port = remote_port;
    941  1.1  christos 
    942  1.1  christos 			/* hardware address is broadcast */
    943  1.1  christos 			htop = NULL;
    944  1.1  christos 		}
    945  1.1  christos 		to.sin_family = AF_INET;
    946  1.1  christos #ifdef HAVE_SA_LEN
    947  1.1  christos 		to.sin_len = sizeof to;
    948  1.1  christos #endif
    949  1.1  christos 
    950  1.1  christos 		memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
    951  1.1  christos 		hto.hbuf[0] = packet->htype;
    952  1.1  christos 		hto.hlen = packet->hlen + 1;
    953  1.1  christos 
    954  1.1  christos 		/* Wipe out the agent relay options and, if possible, figure
    955  1.1  christos 		   out which interface to use based on the contents of the
    956  1.1  christos 		   option that we put on the request to which the server is
    957  1.1  christos 		   replying. */
    958  1.1  christos 		if (!(length =
    959  1.1  christos 		      strip_relay_agent_options(ip, &out, packet, length)))
    960  1.1  christos 			return;
    961  1.1  christos 
    962  1.1  christos 		if (!out) {
    963  1.1  christos 			log_error("Packet to bogus giaddr %s.\n",
    964  1.1  christos 			      inet_ntoa(packet->giaddr));
    965  1.1  christos 			++bogus_giaddr_drops;
    966  1.1  christos 			return;
    967  1.1  christos 		}
    968  1.1  christos 
    969  1.6  christos 		if (use_fake_gw) {
    970  1.6  christos 			packet->giaddr = gw;
    971  1.6  christos 		}
    972  1.6  christos 
    973  1.1  christos 		if (send_packet(out, NULL, packet, length, out->addresses[0],
    974  1.1  christos 				&to, htop) < 0) {
    975  1.1  christos 			++server_packet_errors;
    976  1.1  christos 		} else {
    977  1.1  christos 			log_debug("Forwarded BOOTREPLY for %s to %s",
    978  1.1  christos 			       print_hw_addr(packet->htype, packet->hlen,
    979  1.1  christos 					      packet->chaddr),
    980  1.1  christos 			       inet_ntoa(to.sin_addr));
    981  1.1  christos 
    982  1.1  christos 			++server_packets_relayed;
    983  1.1  christos 		}
    984  1.1  christos 		return;
    985  1.1  christos 	}
    986  1.1  christos 
    987  1.1  christos 	/* If giaddr matches one of our addresses, ignore the packet -
    988  1.1  christos 	   we just sent it. */
    989  1.1  christos 	if (out)
    990  1.1  christos 		return;
    991  1.1  christos 
    992  1.1  christos 	if (!(ip->flags & INTERFACE_DOWNSTREAM)) {
    993  1.1  christos 		log_debug("Dropping request received on %s", ip->name);
    994  1.1  christos 		return;
    995  1.1  christos 	}
    996  1.1  christos 
    997  1.1  christos 	/* Add relay agent options if indicated.   If something goes wrong,
    998  1.1  christos 	 * drop the packet.  Note this may set packet->giaddr if RFC3527
    999  1.1  christos 	 * is enabled. */
   1000  1.1  christos 	if (!(length = add_relay_agent_options(ip, packet, length,
   1001  1.1  christos 					       ip->addresses[0])))
   1002  1.1  christos 		return;
   1003  1.1  christos 
   1004  1.1  christos 	/* If giaddr is not already set, Set it so the server can
   1005  1.1  christos 	   figure out what net it's from and so that we can later
   1006  1.1  christos 	   forward the response to the correct net.    If it's already
   1007  1.1  christos 	   set, the response will be sent directly to the relay agent
   1008  1.1  christos 	   that set giaddr, so we won't see it. */
   1009  1.1  christos 	if (!packet->giaddr.s_addr)
   1010  1.1  christos 		packet->giaddr = ip->addresses[0];
   1011  1.1  christos 	if (packet->hops < max_hop_count)
   1012  1.1  christos 		packet->hops = packet->hops + 1;
   1013  1.1  christos 	else
   1014  1.1  christos 		return;
   1015  1.1  christos 
   1016  1.1  christos 	/* Otherwise, it's a BOOTREQUEST, so forward it to all the
   1017  1.1  christos 	   servers. */
   1018  1.1  christos 	for (sp = servers; sp; sp = sp->next) {
   1019  1.1  christos 		if (send_packet((fallback_interface
   1020  1.1  christos 				 ? fallback_interface : interfaces),
   1021  1.1  christos 				 NULL, packet, length, ip->addresses[0],
   1022  1.1  christos 				 &sp->to, NULL) < 0) {
   1023  1.1  christos 			++client_packet_errors;
   1024  1.1  christos 		} else {
   1025  1.1  christos 			log_debug("Forwarded BOOTREQUEST for %s to %s",
   1026  1.1  christos 			       print_hw_addr(packet->htype, packet->hlen,
   1027  1.1  christos 					      packet->chaddr),
   1028  1.1  christos 			       inet_ntoa(sp->to.sin_addr));
   1029  1.1  christos 			++client_packets_relayed;
   1030  1.1  christos 		}
   1031  1.1  christos 	}
   1032  1.3  christos 
   1033  1.1  christos }
   1034  1.1  christos 
   1035  1.3  christos #endif /* UNIT_TEST */
   1036  1.3  christos 
   1037  1.1  christos /* Strip any Relay Agent Information options from the DHCP packet
   1038  1.1  christos    option buffer.   If there is a circuit ID suboption, look up the
   1039  1.1  christos    outgoing interface based upon it. */
   1040  1.1  christos 
   1041  1.3  christos int
   1042  1.1  christos strip_relay_agent_options(struct interface_info *in,
   1043  1.1  christos 			  struct interface_info **out,
   1044  1.1  christos 			  struct dhcp_packet *packet,
   1045  1.1  christos 			  unsigned length) {
   1046  1.1  christos 	int is_dhcp = 0;
   1047  1.1  christos 	u_int8_t *op, *nextop, *sp, *max;
   1048  1.1  christos 	int good_agent_option = 0;
   1049  1.1  christos 	int status;
   1050  1.1  christos 
   1051  1.1  christos 	/* If we're not adding agent options to packets, we're not taking
   1052  1.1  christos 	   them out either. */
   1053  1.1  christos 	if (!add_agent_options)
   1054  1.1  christos 		return (length);
   1055  1.1  christos 
   1056  1.1  christos 	/* If there's no cookie, it's a bootp packet, so we should just
   1057  1.1  christos 	   forward it unchanged. */
   1058  1.1  christos 	if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
   1059  1.1  christos 		return (length);
   1060  1.1  christos 
   1061  1.1  christos 	max = ((u_int8_t *)packet) + length;
   1062  1.1  christos 	sp = op = &packet->options[4];
   1063  1.1  christos 
   1064  1.1  christos 	while (op < max) {
   1065  1.1  christos 		switch(*op) {
   1066  1.1  christos 			/* Skip padding... */
   1067  1.1  christos 		      case DHO_PAD:
   1068  1.1  christos 			if (sp != op)
   1069  1.1  christos 				*sp = *op;
   1070  1.1  christos 			++op;
   1071  1.1  christos 			++sp;
   1072  1.1  christos 			continue;
   1073  1.1  christos 
   1074  1.1  christos 			/* If we see a message type, it's a DHCP packet. */
   1075  1.1  christos 		      case DHO_DHCP_MESSAGE_TYPE:
   1076  1.1  christos 			is_dhcp = 1;
   1077  1.1  christos 			goto skip;
   1078  1.1  christos 			break;
   1079  1.1  christos 
   1080  1.1  christos 			/* Quit immediately if we hit an End option. */
   1081  1.1  christos 		      case DHO_END:
   1082  1.1  christos 			if (sp != op)
   1083  1.1  christos 				*sp++ = *op++;
   1084  1.1  christos 			goto out;
   1085  1.1  christos 
   1086  1.1  christos 		      case DHO_DHCP_AGENT_OPTIONS:
   1087  1.1  christos 			/* We shouldn't see a relay agent option in a
   1088  1.1  christos 			   packet before we've seen the DHCP packet type,
   1089  1.1  christos 			   but if we do, we have to leave it alone. */
   1090  1.1  christos 			if (!is_dhcp)
   1091  1.1  christos 				goto skip;
   1092  1.1  christos 
   1093  1.1  christos 			/* Do not process an agent option if it exceeds the
   1094  1.1  christos 			 * buffer.  Fail this packet.
   1095  1.1  christos 			 */
   1096  1.1  christos 			nextop = op + op[1] + 2;
   1097  1.1  christos 			if (nextop > max)
   1098  1.1  christos 				return (0);
   1099  1.1  christos 
   1100  1.1  christos 			status = find_interface_by_agent_option(packet,
   1101  1.1  christos 								out, op + 2,
   1102  1.1  christos 								op[1]);
   1103  1.1  christos 			if (status == -1 && drop_agent_mismatches)
   1104  1.1  christos 				return (0);
   1105  1.1  christos 			if (status)
   1106  1.1  christos 				good_agent_option = 1;
   1107  1.1  christos 			op = nextop;
   1108  1.1  christos 			break;
   1109  1.1  christos 
   1110  1.1  christos 		      skip:
   1111  1.1  christos 			/* Skip over other options. */
   1112  1.1  christos 		      default:
   1113  1.1  christos 			/* Fail if processing this option will exceed the
   1114  1.1  christos 			 * buffer(op[1] is malformed).
   1115  1.1  christos 			 */
   1116  1.1  christos 			nextop = op + op[1] + 2;
   1117  1.1  christos 			if (nextop > max)
   1118  1.1  christos 				return (0);
   1119  1.1  christos 
   1120  1.1  christos 			if (sp != op) {
   1121  1.3  christos 				size_t mlen = op[1] + 2;
   1122  1.3  christos 				memmove(sp, op, mlen);
   1123  1.3  christos 				sp += mlen;
   1124  1.3  christos 				if (sp > max) {
   1125  1.3  christos 					return (0);
   1126  1.3  christos 				}
   1127  1.3  christos 
   1128  1.1  christos 				op = nextop;
   1129  1.1  christos 			} else
   1130  1.1  christos 				op = sp = nextop;
   1131  1.1  christos 
   1132  1.1  christos 			break;
   1133  1.1  christos 		}
   1134  1.1  christos 	}
   1135  1.1  christos       out:
   1136  1.1  christos 
   1137  1.1  christos 	/* If it's not a DHCP packet, we're not supposed to touch it. */
   1138  1.1  christos 	if (!is_dhcp)
   1139  1.1  christos 		return (length);
   1140  1.1  christos 
   1141  1.1  christos 	/* If none of the agent options we found matched, or if we didn't
   1142  1.1  christos 	   find any agent options, count this packet as not having any
   1143  1.1  christos 	   matching agent options, and if we're relying on agent options
   1144  1.1  christos 	   to determine the outgoing interface, drop the packet. */
   1145  1.1  christos 
   1146  1.1  christos 	if (!good_agent_option) {
   1147  1.1  christos 		++missing_agent_option;
   1148  1.1  christos 		if (drop_agent_mismatches)
   1149  1.1  christos 			return (0);
   1150  1.1  christos 	}
   1151  1.1  christos 
   1152  1.1  christos 	/* Adjust the length... */
   1153  1.1  christos 	if (sp != op) {
   1154  1.1  christos 		length = sp -((u_int8_t *)packet);
   1155  1.1  christos 
   1156  1.1  christos 		/* Make sure the packet isn't short(this is unlikely,
   1157  1.1  christos 		   but WTH) */
   1158  1.1  christos 		if (length < BOOTP_MIN_LEN) {
   1159  1.1  christos 			memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
   1160  1.1  christos 			length = BOOTP_MIN_LEN;
   1161  1.1  christos 		}
   1162  1.1  christos 	}
   1163  1.1  christos 	return (length);
   1164  1.1  christos }
   1165  1.1  christos 
   1166  1.1  christos 
   1167  1.1  christos /* Find an interface that matches the circuit ID specified in the
   1168  1.1  christos    Relay Agent Information option.   If one is found, store it through
   1169  1.1  christos    the pointer given; otherwise, leave the existing pointer alone.
   1170  1.1  christos 
   1171  1.1  christos    We actually deviate somewhat from the current specification here:
   1172  1.1  christos    if the option buffer is corrupt, we suggest that the caller not
   1173  1.1  christos    respond to this packet.  If the circuit ID doesn't match any known
   1174  1.1  christos    interface, we suggest that the caller to drop the packet.  Only if
   1175  1.1  christos    we find a circuit ID that matches an existing interface do we tell
   1176  1.1  christos    the caller to go ahead and process the packet. */
   1177  1.1  christos 
   1178  1.3  christos int
   1179  1.1  christos find_interface_by_agent_option(struct dhcp_packet *packet,
   1180  1.1  christos 			       struct interface_info **out,
   1181  1.1  christos 			       u_int8_t *buf, int len) {
   1182  1.1  christos 	int i = 0;
   1183  1.1  christos 	u_int8_t *circuit_id = 0;
   1184  1.1  christos 	unsigned circuit_id_len = 0;
   1185  1.1  christos 	struct interface_info *ip;
   1186  1.1  christos 
   1187  1.1  christos 	while (i < len) {
   1188  1.1  christos 		/* If the next agent option overflows the end of the
   1189  1.1  christos 		   packet, the agent option buffer is corrupt. */
   1190  1.1  christos 		if (i + 1 == len ||
   1191  1.1  christos 		    i + buf[i + 1] + 2 > len) {
   1192  1.1  christos 			++corrupt_agent_options;
   1193  1.1  christos 			return (-1);
   1194  1.1  christos 		}
   1195  1.1  christos 		switch(buf[i]) {
   1196  1.1  christos 			/* Remember where the circuit ID is... */
   1197  1.1  christos 		      case RAI_CIRCUIT_ID:
   1198  1.1  christos 			circuit_id = &buf[i + 2];
   1199  1.1  christos 			circuit_id_len = buf[i + 1];
   1200  1.1  christos 			i += circuit_id_len + 2;
   1201  1.1  christos 			continue;
   1202  1.1  christos 
   1203  1.1  christos 		      default:
   1204  1.1  christos 			i += buf[i + 1] + 2;
   1205  1.1  christos 			break;
   1206  1.1  christos 		}
   1207  1.1  christos 	}
   1208  1.1  christos 
   1209  1.1  christos 	/* If there's no circuit ID, it's not really ours, tell the caller
   1210  1.1  christos 	   it's no good. */
   1211  1.1  christos 	if (!circuit_id) {
   1212  1.1  christos 		++missing_circuit_id;
   1213  1.1  christos 		return (-1);
   1214  1.1  christos 	}
   1215  1.1  christos 
   1216  1.1  christos 	/* Scan the interface list looking for an interface whose
   1217  1.1  christos 	   name matches the one specified in circuit_id. */
   1218  1.1  christos 
   1219  1.1  christos 	for (ip = interfaces; ip; ip = ip->next) {
   1220  1.1  christos 		if (ip->circuit_id &&
   1221  1.1  christos 		    ip->circuit_id_len == circuit_id_len &&
   1222  1.1  christos 		    !memcmp(ip->circuit_id, circuit_id, circuit_id_len))
   1223  1.1  christos 			break;
   1224  1.1  christos 	}
   1225  1.1  christos 
   1226  1.1  christos 	/* If we got a match, use it. */
   1227  1.1  christos 	if (ip) {
   1228  1.1  christos 		*out = ip;
   1229  1.1  christos 		return (1);
   1230  1.1  christos 	}
   1231  1.1  christos 
   1232  1.1  christos 	/* If we didn't get a match, the circuit ID was bogus. */
   1233  1.1  christos 	++bad_circuit_id;
   1234  1.1  christos 	return (-1);
   1235  1.1  christos }
   1236  1.1  christos 
   1237  1.1  christos /*
   1238  1.1  christos  * Examine a packet to see if it's a candidate to have a Relay
   1239  1.1  christos  * Agent Information option tacked onto its tail.   If it is, tack
   1240  1.1  christos  * the option on.
   1241  1.1  christos  */
   1242  1.3  christos int
   1243  1.1  christos add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
   1244  1.1  christos 			unsigned length, struct in_addr giaddr) {
   1245  1.1  christos 	int is_dhcp = 0, mms;
   1246  1.1  christos 	unsigned optlen;
   1247  1.1  christos 	u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
   1248  1.1  christos 	int adding_link_select;
   1249  1.1  christos 
   1250  1.1  christos 	/* If we're not adding agent options to packets, we can skip
   1251  1.1  christos 	   this. */
   1252  1.1  christos 	if (!add_agent_options)
   1253  1.1  christos 		return (length);
   1254  1.1  christos 
   1255  1.1  christos 	/* If there's no cookie, it's a bootp packet, so we should just
   1256  1.1  christos 	   forward it unchanged. */
   1257  1.1  christos 	if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
   1258  1.1  christos 		return (length);
   1259  1.1  christos 
   1260  1.1  christos 	max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
   1261  1.1  christos 
   1262  1.1  christos 	/* Add link selection suboption if enabled and we're the first relay */
   1263  1.1  christos 	adding_link_select = (add_rfc3527_suboption
   1264  1.1  christos 			      && (packet->giaddr.s_addr == 0));
   1265  1.1  christos 
   1266  1.1  christos 	/* Commence processing after the cookie. */
   1267  1.1  christos 	sp = op = &packet->options[4];
   1268  1.1  christos 
   1269  1.1  christos 	while (op < max) {
   1270  1.1  christos 		switch(*op) {
   1271  1.1  christos 			/* Skip padding... */
   1272  1.1  christos 		      case DHO_PAD:
   1273  1.1  christos 			/* Remember the first pad byte so we can commandeer
   1274  1.1  christos 			 * padded space.
   1275  1.1  christos 			 *
   1276  1.1  christos 			 * XXX: Is this really a good idea?  Sure, we can
   1277  1.1  christos 			 * seemingly reduce the packet while we're looking,
   1278  1.1  christos 			 * but if the packet was signed by the client then
   1279  1.1  christos 			 * this padding is part of the checksum(RFC3118),
   1280  1.1  christos 			 * and its nonpresence would break authentication.
   1281  1.1  christos 			 */
   1282  1.1  christos 			if (end_pad == NULL)
   1283  1.1  christos 				end_pad = sp;
   1284  1.1  christos 
   1285  1.1  christos 			if (sp != op)
   1286  1.1  christos 				*sp++ = *op++;
   1287  1.1  christos 			else
   1288  1.1  christos 				sp = ++op;
   1289  1.1  christos 
   1290  1.1  christos 			continue;
   1291  1.1  christos 
   1292  1.1  christos 			/* If we see a message type, it's a DHCP packet. */
   1293  1.1  christos 		      case DHO_DHCP_MESSAGE_TYPE:
   1294  1.1  christos 			is_dhcp = 1;
   1295  1.1  christos 			goto skip;
   1296  1.1  christos 
   1297  1.1  christos 			/*
   1298  1.1  christos 			 * If there's a maximum message size option, we
   1299  1.1  christos 			 * should pay attention to it
   1300  1.1  christos 			 */
   1301  1.1  christos 		      case DHO_DHCP_MAX_MESSAGE_SIZE:
   1302  1.1  christos 			mms = ntohs(*(op + 2));
   1303  1.1  christos 			if (mms < dhcp_max_agent_option_packet_length &&
   1304  1.1  christos 			    mms >= DHCP_MTU_MIN)
   1305  1.1  christos 				max = ((u_int8_t *)packet) + mms;
   1306  1.1  christos 			goto skip;
   1307  1.1  christos 
   1308  1.1  christos 			/* Quit immediately if we hit an End option. */
   1309  1.1  christos 		      case DHO_END:
   1310  1.1  christos 			goto out;
   1311  1.1  christos 
   1312  1.1  christos 		      case DHO_DHCP_AGENT_OPTIONS:
   1313  1.1  christos 			/* We shouldn't see a relay agent option in a
   1314  1.1  christos 			   packet before we've seen the DHCP packet type,
   1315  1.1  christos 			   but if we do, we have to leave it alone. */
   1316  1.1  christos 			if (!is_dhcp)
   1317  1.1  christos 				goto skip;
   1318  1.1  christos 
   1319  1.1  christos 			end_pad = NULL;
   1320  1.1  christos 
   1321  1.1  christos 			/* There's already a Relay Agent Information option
   1322  1.1  christos 			   in this packet.   How embarrassing.   Decide what
   1323  1.1  christos 			   to do based on the mode the user specified. */
   1324  1.1  christos 
   1325  1.1  christos 			switch(agent_relay_mode) {
   1326  1.1  christos 			      case forward_and_append:
   1327  1.1  christos 				goto skip;
   1328  1.1  christos 			      case forward_untouched:
   1329  1.1  christos 				return (length);
   1330  1.1  christos 			      case discard:
   1331  1.1  christos 				return (0);
   1332  1.1  christos 			      case forward_and_replace:
   1333  1.1  christos 			      default:
   1334  1.1  christos 				break;
   1335  1.1  christos 			}
   1336  1.1  christos 
   1337  1.1  christos 			/* Skip over the agent option and start copying
   1338  1.1  christos 			   if we aren't copying already. */
   1339  1.1  christos 			op += op[1] + 2;
   1340  1.1  christos 			break;
   1341  1.1  christos 
   1342  1.1  christos 		      skip:
   1343  1.1  christos 			/* Skip over other options. */
   1344  1.1  christos 		      default:
   1345  1.1  christos 			/* Fail if processing this option will exceed the
   1346  1.1  christos 			 * buffer(op[1] is malformed).
   1347  1.1  christos 			 */
   1348  1.1  christos 			nextop = op + op[1] + 2;
   1349  1.1  christos 			if (nextop > max)
   1350  1.1  christos 				return (0);
   1351  1.1  christos 
   1352  1.1  christos 			end_pad = NULL;
   1353  1.1  christos 
   1354  1.1  christos 			if (sp != op) {
   1355  1.3  christos 				size_t mlen = op[1] + 2;
   1356  1.3  christos 				memmove(sp, op, mlen);
   1357  1.3  christos 				sp += mlen;
   1358  1.3  christos 				if (sp > max) {
   1359  1.3  christos 					return (0);
   1360  1.3  christos 				}
   1361  1.3  christos 
   1362  1.1  christos 				op = nextop;
   1363  1.1  christos 			} else
   1364  1.1  christos 				op = sp = nextop;
   1365  1.1  christos 
   1366  1.1  christos 			break;
   1367  1.1  christos 		}
   1368  1.1  christos 	}
   1369  1.1  christos       out:
   1370  1.1  christos 
   1371  1.1  christos 	/* If it's not a DHCP packet, we're not supposed to touch it. */
   1372  1.1  christos 	if (!is_dhcp)
   1373  1.1  christos 		return (length);
   1374  1.1  christos 
   1375  1.1  christos 	/* If the packet was padded out, we can store the agent option
   1376  1.1  christos 	   at the beginning of the padding. */
   1377  1.1  christos 
   1378  1.1  christos 	if (end_pad != NULL)
   1379  1.1  christos 		sp = end_pad;
   1380  1.1  christos 
   1381  1.1  christos #if 0
   1382  1.1  christos 	/* Remember where the end of the packet was after parsing
   1383  1.1  christos 	   it. */
   1384  1.1  christos 	op = sp;
   1385  1.1  christos #endif
   1386  1.1  christos 
   1387  1.1  christos 	/* Sanity check.  Had better not ever happen. */
   1388  1.1  christos 	if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
   1389  1.1  christos 		log_fatal("Circuit ID length %d out of range [1-255] on "
   1390  1.1  christos 			  "%s\n", ip->circuit_id_len, ip->name);
   1391  1.1  christos 	optlen = ip->circuit_id_len + 2;            /* RAI_CIRCUIT_ID + len */
   1392  1.1  christos 
   1393  1.1  christos 	if (ip->remote_id) {
   1394  1.1  christos 		if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
   1395  1.1  christos 			log_fatal("Remote ID length %d out of range [1-255] "
   1396  1.1  christos 				  "on %s\n", ip->remote_id_len, ip->name);
   1397  1.1  christos 		optlen += ip->remote_id_len + 2;    /* RAI_REMOTE_ID + len */
   1398  1.1  christos 	}
   1399  1.1  christos 
   1400  1.1  christos 	if (adding_link_select) {
   1401  1.1  christos 		optlen += 6;
   1402  1.1  christos 	}
   1403  1.1  christos 
   1404  1.1  christos #ifdef RELAY_PORT
   1405  1.1  christos 	if (relay_port) {
   1406  1.1  christos 		optlen += 2;
   1407  1.1  christos 	}
   1408  1.1  christos #endif
   1409  1.1  christos 
   1410  1.1  christos 	/* We do not support relay option fragmenting(multiple options to
   1411  1.1  christos 	 * support an option data exceeding 255 bytes).
   1412  1.1  christos 	 */
   1413  1.1  christos 	if ((optlen < 3) ||(optlen > 255))
   1414  1.1  christos 		log_fatal("Total agent option length(%u) out of range "
   1415  1.1  christos 			   "[3 - 255] on %s\n", optlen, ip->name);
   1416  1.1  christos 
   1417  1.1  christos 	/*
   1418  1.1  christos 	 * Is there room for the option, its code+len, and DHO_END?
   1419  1.1  christos 	 * If not, forward without adding the option.
   1420  1.1  christos 	 */
   1421  1.1  christos 	if (max - sp >= optlen + 3) {
   1422  1.1  christos 		log_debug("Adding %d-byte relay agent option", optlen + 3);
   1423  1.1  christos 
   1424  1.1  christos 		/* Okay, cons up *our* Relay Agent Information option. */
   1425  1.1  christos 		*sp++ = DHO_DHCP_AGENT_OPTIONS;
   1426  1.1  christos 		*sp++ = optlen;
   1427  1.1  christos 
   1428  1.1  christos 		/* Copy in the circuit id... */
   1429  1.1  christos 		*sp++ = RAI_CIRCUIT_ID;
   1430  1.1  christos 		*sp++ = ip->circuit_id_len;
   1431  1.1  christos 		memcpy(sp, ip->circuit_id, ip->circuit_id_len);
   1432  1.1  christos 		sp += ip->circuit_id_len;
   1433  1.1  christos 
   1434  1.1  christos 		/* Copy in remote ID... */
   1435  1.1  christos 		if (ip->remote_id) {
   1436  1.1  christos 			*sp++ = RAI_REMOTE_ID;
   1437  1.1  christos 			*sp++ = ip->remote_id_len;
   1438  1.1  christos 			memcpy(sp, ip->remote_id, ip->remote_id_len);
   1439  1.1  christos 			sp += ip->remote_id_len;
   1440  1.1  christos 		}
   1441  1.1  christos 
   1442  1.1  christos 		/* RFC3527: Use the inbound packet's interface address in
   1443  1.1  christos 		 * the link selection suboption and set the outbound giaddr
   1444  1.1  christos 		 * to the uplink address. */
   1445  1.1  christos 		if (adding_link_select) {
   1446  1.1  christos 			*sp++ = RAI_LINK_SELECT;
   1447  1.1  christos 			*sp++ = 4u;
   1448  1.1  christos 			memcpy(sp, &giaddr.s_addr, 4);
   1449  1.1  christos 			sp += 4;
   1450  1.1  christos 			packet->giaddr = uplink->addresses[0];
   1451  1.1  christos 			log_debug ("Adding link selection suboption"
   1452  1.1  christos 				   " with addr: %s", inet_ntoa(giaddr));
   1453  1.1  christos 		}
   1454  1.1  christos 
   1455  1.1  christos #ifdef RELAY_PORT
   1456  1.1  christos 		/* draft-ietf-dhc-relay-port-10.txt section 5.1 */
   1457  1.1  christos 		if (relay_port) {
   1458  1.1  christos 			*sp++ = RAI_RELAY_PORT;
   1459  1.1  christos 			*sp++ = 0u;
   1460  1.1  christos 		}
   1461  1.1  christos #endif
   1462  1.1  christos 	} else {
   1463  1.1  christos 		++agent_option_errors;
   1464  1.1  christos 		log_error("No room in packet (used %d of %d) "
   1465  1.1  christos 			  "for %d-byte relay agent option: omitted",
   1466  1.1  christos 			   (int) (sp - ((u_int8_t *) packet)),
   1467  1.1  christos 			   (int) (max - ((u_int8_t *) packet)),
   1468  1.1  christos 			   optlen + 3);
   1469  1.1  christos 	}
   1470  1.1  christos 
   1471  1.1  christos 	/*
   1472  1.1  christos 	 * Deposit an END option unless the packet is full (shouldn't
   1473  1.1  christos 	 * be possible).
   1474  1.1  christos 	 */
   1475  1.1  christos 	if (sp < max)
   1476  1.1  christos 		*sp++ = DHO_END;
   1477  1.1  christos 
   1478  1.1  christos 	/* Recalculate total packet length. */
   1479  1.1  christos 	length = sp -((u_int8_t *)packet);
   1480  1.1  christos 
   1481  1.1  christos 	/* Make sure the packet isn't short(this is unlikely, but WTH) */
   1482  1.1  christos 	if (length < BOOTP_MIN_LEN) {
   1483  1.1  christos 		memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
   1484  1.1  christos 		return (BOOTP_MIN_LEN);
   1485  1.1  christos 	}
   1486  1.1  christos 
   1487  1.1  christos 	return (length);
   1488  1.1  christos }
   1489  1.1  christos 
   1490  1.6  christos #ifdef DHCPv6
   1491  1.5  christos #ifndef UNIT_TEST
   1492  1.1  christos /*
   1493  1.1  christos  * Parse a downstream argument: [address%]interface[#index].
   1494  1.1  christos  */
   1495  1.1  christos static struct stream_list *
   1496  1.1  christos parse_downstream(char *arg) {
   1497  1.1  christos 	struct stream_list *dp, *up;
   1498  1.1  christos 	struct interface_info *ifp = NULL;
   1499  1.1  christos 	char *ifname, *addr, *iid;
   1500  1.1  christos 	isc_result_t status;
   1501  1.1  christos 
   1502  1.1  christos 	if (!supports_multiple_interfaces(ifp) &&
   1503  1.1  christos 	    (downstreams != NULL))
   1504  1.1  christos 		log_fatal("No support for multiple interfaces.");
   1505  1.1  christos 
   1506  1.1  christos 	/* Decode the argument. */
   1507  1.1  christos 	ifname = strchr(arg, '%');
   1508  1.1  christos 	if (ifname == NULL) {
   1509  1.1  christos 		ifname = arg;
   1510  1.1  christos 		addr = NULL;
   1511  1.1  christos 	} else {
   1512  1.1  christos 		*ifname++ = '\0';
   1513  1.1  christos 		addr = arg;
   1514  1.1  christos 	}
   1515  1.1  christos 	iid = strchr(ifname, '#');
   1516  1.1  christos 	if (iid != NULL) {
   1517  1.1  christos 		*iid++ = '\0';
   1518  1.1  christos 	}
   1519  1.1  christos 	if (strlen(ifname) >= sizeof(ifp->name)) {
   1520  1.1  christos 		usage("Interface name '%s' too long", ifname);
   1521  1.1  christos 	}
   1522  1.1  christos 
   1523  1.1  christos 	/* Don't declare twice. */
   1524  1.1  christos 	for (dp = downstreams; dp; dp = dp->next) {
   1525  1.1  christos 		if (strcmp(ifname, dp->ifp->name) == 0)
   1526  1.1  christos 			log_fatal("Down interface '%s' declared twice.",
   1527  1.1  christos 				  ifname);
   1528  1.1  christos 	}
   1529  1.1  christos 
   1530  1.1  christos 	/* Share with up side? */
   1531  1.1  christos 	for (up = upstreams; up; up = up->next) {
   1532  1.1  christos 		if (strcmp(ifname, up->ifp->name) == 0) {
   1533  1.1  christos 			log_info("parse_downstream: Interface '%s' is "
   1534  1.1  christos 				 "both down and up.", ifname);
   1535  1.1  christos 			ifp = up->ifp;
   1536  1.1  christos 			break;
   1537  1.1  christos 		}
   1538  1.1  christos 	}
   1539  1.1  christos 
   1540  1.1  christos 	/* New interface. */
   1541  1.1  christos 	if (ifp == NULL) {
   1542  1.1  christos 		status = interface_allocate(&ifp, MDL);
   1543  1.1  christos 		if (status != ISC_R_SUCCESS)
   1544  1.1  christos 			log_fatal("%s: interface_allocate: %s",
   1545  1.1  christos 				  arg, isc_result_totext(status));
   1546  1.1  christos 		strcpy(ifp->name, ifname);
   1547  1.1  christos 		if (interfaces) {
   1548  1.1  christos 			interface_reference(&ifp->next, interfaces, MDL);
   1549  1.1  christos 			interface_dereference(&interfaces, MDL);
   1550  1.1  christos 		}
   1551  1.1  christos 		interface_reference(&interfaces, ifp, MDL);
   1552  1.1  christos 	}
   1553  1.1  christos 	ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
   1554  1.1  christos 
   1555  1.1  christos 	/* New downstream. */
   1556  1.1  christos 	dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
   1557  1.1  christos 	if (!dp)
   1558  1.1  christos 		log_fatal("No memory for downstream.");
   1559  1.1  christos 	dp->ifp = ifp;
   1560  1.1  christos 	if (iid != NULL) {
   1561  1.1  christos 		dp->id = atoi(iid);
   1562  1.1  christos 	} else {
   1563  1.1  christos 		dp->id = -1;
   1564  1.1  christos 	}
   1565  1.1  christos 	/* !addr case handled by setup. */
   1566  1.1  christos 	if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
   1567  1.1  christos 		log_fatal("Bad link address '%s'", addr);
   1568  1.1  christos 
   1569  1.1  christos 	return dp;
   1570  1.1  christos }
   1571  1.1  christos 
   1572  1.1  christos /*
   1573  1.1  christos  * Parse an upstream argument: [address]%interface.
   1574  1.1  christos  */
   1575  1.1  christos static struct stream_list *
   1576  1.1  christos parse_upstream(char *arg) {
   1577  1.1  christos 	struct stream_list *up, *dp;
   1578  1.1  christos 	struct interface_info *ifp = NULL;
   1579  1.1  christos 	char *ifname, *addr;
   1580  1.1  christos 	isc_result_t status;
   1581  1.1  christos 
   1582  1.1  christos 	/* Decode the argument. */
   1583  1.1  christos 	ifname = strchr(arg, '%');
   1584  1.1  christos 	if (ifname == NULL) {
   1585  1.1  christos 		ifname = arg;
   1586  1.1  christos 		addr = All_DHCP_Servers;
   1587  1.1  christos 	} else {
   1588  1.1  christos 		*ifname++ = '\0';
   1589  1.1  christos 		addr = arg;
   1590  1.1  christos 	}
   1591  1.1  christos 	if (strlen(ifname) >= sizeof(ifp->name)) {
   1592  1.1  christos 		log_fatal("Interface name '%s' too long", ifname);
   1593  1.1  christos 	}
   1594  1.1  christos 
   1595  1.1  christos 	/* Shared up interface? */
   1596  1.1  christos 	for (up = upstreams; up; up = up->next) {
   1597  1.1  christos 		if (strcmp(ifname, up->ifp->name) == 0) {
   1598  1.1  christos 			ifp = up->ifp;
   1599  1.1  christos 			break;
   1600  1.1  christos 		}
   1601  1.1  christos 	}
   1602  1.1  christos 	for (dp = downstreams; dp; dp = dp->next) {
   1603  1.1  christos 		if (strcmp(ifname, dp->ifp->name) == 0) {
   1604  1.1  christos 			log_info("parse_upstream: Interface '%s' is "
   1605  1.1  christos 				 "both down and up.", ifname);
   1606  1.1  christos 			ifp = dp->ifp;
   1607  1.1  christos 			break;
   1608  1.1  christos 		}
   1609  1.1  christos 	}
   1610  1.1  christos 
   1611  1.1  christos 	/* New interface. */
   1612  1.1  christos 	if (ifp == NULL) {
   1613  1.1  christos 		status = interface_allocate(&ifp, MDL);
   1614  1.1  christos 		if (status != ISC_R_SUCCESS)
   1615  1.1  christos 			log_fatal("%s: interface_allocate: %s",
   1616  1.1  christos 				  arg, isc_result_totext(status));
   1617  1.1  christos 		strcpy(ifp->name, ifname);
   1618  1.1  christos 		if (interfaces) {
   1619  1.1  christos 			interface_reference(&ifp->next, interfaces, MDL);
   1620  1.1  christos 			interface_dereference(&interfaces, MDL);
   1621  1.1  christos 		}
   1622  1.1  christos 		interface_reference(&interfaces, ifp, MDL);
   1623  1.1  christos 	}
   1624  1.1  christos 	ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
   1625  1.1  christos 
   1626  1.1  christos 	/* New upstream. */
   1627  1.1  christos 	up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
   1628  1.1  christos 	if (up == NULL)
   1629  1.1  christos 		log_fatal("No memory for upstream.");
   1630  1.1  christos 
   1631  1.1  christos 	up->ifp = ifp;
   1632  1.1  christos 
   1633  1.1  christos 	if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
   1634  1.1  christos 		log_fatal("Bad address %s", addr);
   1635  1.1  christos 
   1636  1.1  christos 	return up;
   1637  1.1  christos }
   1638  1.1  christos 
   1639  1.1  christos /*
   1640  1.1  christos  * Setup downstream interfaces.
   1641  1.1  christos  */
   1642  1.1  christos static void
   1643  1.1  christos setup_streams(void) {
   1644  1.1  christos 	struct stream_list *dp, *up;
   1645  1.1  christos 	int i;
   1646  1.1  christos 	isc_boolean_t link_is_set;
   1647  1.1  christos 
   1648  1.1  christos 	for (dp = downstreams; dp; dp = dp->next) {
   1649  1.1  christos 		/* Check interface */
   1650  1.1  christos 		if (dp->ifp->v6address_count == 0)
   1651  1.1  christos 			log_fatal("Interface '%s' has no IPv6 addresses.",
   1652  1.1  christos 				  dp->ifp->name);
   1653  1.1  christos 
   1654  1.1  christos 		/* Check/set link. */
   1655  1.1  christos 		if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
   1656  1.1  christos 			link_is_set = ISC_FALSE;
   1657  1.1  christos 		else
   1658  1.1  christos 			link_is_set = ISC_TRUE;
   1659  1.1  christos 		for (i = 0; i < dp->ifp->v6address_count; i++) {
   1660  1.1  christos 			if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
   1661  1.1  christos 				continue;
   1662  1.1  christos 			if (!link_is_set)
   1663  1.1  christos 				break;
   1664  1.1  christos 			if (!memcmp(&dp->ifp->v6addresses[i],
   1665  1.1  christos 				    &dp->link.sin6_addr,
   1666  1.1  christos 				    sizeof(dp->link.sin6_addr)))
   1667  1.1  christos 				break;
   1668  1.1  christos 		}
   1669  1.1  christos 		if (i == dp->ifp->v6address_count)
   1670  1.1  christos 			log_fatal("Interface %s does not have global IPv6 "
   1671  1.1  christos 				  "address assigned.", dp->ifp->name);
   1672  1.1  christos 		if (!link_is_set)
   1673  1.1  christos 			memcpy(&dp->link.sin6_addr,
   1674  1.1  christos 			       &dp->ifp->v6addresses[i],
   1675  1.1  christos 			       sizeof(dp->link.sin6_addr));
   1676  1.1  christos 
   1677  1.1  christos 		/* Set interface-id. */
   1678  1.1  christos 		if (dp->id == -1)
   1679  1.1  christos 			dp->id = dp->ifp->index;
   1680  1.1  christos 	}
   1681  1.1  christos 
   1682  1.1  christos 	for (up = upstreams; up; up = up->next) {
   1683  1.1  christos 		up->link.sin6_port = local_port;
   1684  1.1  christos 		up->link.sin6_family = AF_INET6;
   1685  1.1  christos #ifdef HAVE_SA_LEN
   1686  1.1  christos 		up->link.sin6_len = sizeof(up->link);
   1687  1.1  christos #endif
   1688  1.1  christos 
   1689  1.1  christos 		if (up->ifp->v6address_count == 0)
   1690  1.1  christos 			log_fatal("Interface '%s' has no IPv6 addresses.",
   1691  1.1  christos 				  up->ifp->name);
   1692  1.1  christos 
   1693  1.1  christos 		/* RFC 3315 Sec 20 - "If the relay agent relays messages to
   1694  1.1  christos 		 * the All_DHCP_Servers address or other multicast addresses,
   1695  1.1  christos 		 * it sets the Hop Limit field to 32." */
   1696  1.1  christos 		if (IN6_IS_ADDR_MULTICAST(&up->link.sin6_addr)) {
   1697  1.1  christos 			set_multicast_hop_limit(up->ifp, HOP_COUNT_LIMIT);
   1698  1.1  christos 		}
   1699  1.1  christos 	}
   1700  1.1  christos }
   1701  1.1  christos 
   1702  1.1  christos /*
   1703  1.1  christos  * Add DHCPv6 agent options here.
   1704  1.1  christos  */
   1705  1.1  christos static const int required_forw_opts[] = {
   1706  1.1  christos 	D6O_INTERFACE_ID,
   1707  1.1  christos 	D6O_SUBSCRIBER_ID,
   1708  1.1  christos #if defined(RELAY_PORT)
   1709  1.1  christos 	D6O_RELAY_SOURCE_PORT,
   1710  1.1  christos #endif
   1711  1.1  christos 	D6O_RELAY_MSG,
   1712  1.1  christos 	0
   1713  1.1  christos };
   1714  1.1  christos 
   1715  1.1  christos /*
   1716  1.1  christos  * Process a packet upwards, i.e., from client to server.
   1717  1.1  christos  */
   1718  1.1  christos static void
   1719  1.1  christos process_up6(struct packet *packet, struct stream_list *dp) {
   1720  1.1  christos 	char forw_data[65535];
   1721  1.1  christos 	unsigned cursor;
   1722  1.1  christos 	struct dhcpv6_relay_packet *relay;
   1723  1.1  christos 	struct option_state *opts;
   1724  1.1  christos 	struct stream_list *up;
   1725  1.1  christos 	u_int16_t relay_client_port = 0;
   1726  1.1  christos 
   1727  1.1  christos 	/* Check if the message should be relayed to the server. */
   1728  1.1  christos 	switch (packet->dhcpv6_msg_type) {
   1729  1.1  christos 	      case DHCPV6_SOLICIT:
   1730  1.1  christos 	      case DHCPV6_REQUEST:
   1731  1.1  christos 	      case DHCPV6_CONFIRM:
   1732  1.1  christos 	      case DHCPV6_RENEW:
   1733  1.1  christos 	      case DHCPV6_REBIND:
   1734  1.1  christos 	      case DHCPV6_RELEASE:
   1735  1.1  christos 	      case DHCPV6_DECLINE:
   1736  1.1  christos 	      case DHCPV6_INFORMATION_REQUEST:
   1737  1.1  christos 	      case DHCPV6_RELAY_FORW:
   1738  1.1  christos 	      case DHCPV6_LEASEQUERY:
   1739  1.1  christos 	      case DHCPV6_DHCPV4_QUERY:
   1740  1.1  christos 		log_info("Relaying %s from %s port %d going up.",
   1741  1.1  christos 			 dhcpv6_type_names[packet->dhcpv6_msg_type],
   1742  1.1  christos 			 piaddr(packet->client_addr),
   1743  1.1  christos 			 ntohs(packet->client_port));
   1744  1.1  christos 		break;
   1745  1.1  christos 
   1746  1.1  christos 	      case DHCPV6_ADVERTISE:
   1747  1.1  christos 	      case DHCPV6_REPLY:
   1748  1.1  christos 	      case DHCPV6_RECONFIGURE:
   1749  1.1  christos 	      case DHCPV6_RELAY_REPL:
   1750  1.1  christos 	      case DHCPV6_LEASEQUERY_REPLY:
   1751  1.1  christos 	      case DHCPV6_DHCPV4_RESPONSE:
   1752  1.1  christos 		log_info("Discarding %s from %s port %d going up.",
   1753  1.1  christos 			 dhcpv6_type_names[packet->dhcpv6_msg_type],
   1754  1.1  christos 			 piaddr(packet->client_addr),
   1755  1.1  christos 			 ntohs(packet->client_port));
   1756  1.1  christos 		return;
   1757  1.1  christos 
   1758  1.1  christos 	      default:
   1759  1.1  christos 		log_info("Unknown %d type from %s port %d going up.",
   1760  1.1  christos 			 packet->dhcpv6_msg_type,
   1761  1.1  christos 			 piaddr(packet->client_addr),
   1762  1.1  christos 			 ntohs(packet->client_port));
   1763  1.1  christos 		return;
   1764  1.1  christos 	}
   1765  1.1  christos 
   1766  1.1  christos 	/* Build the relay-forward header. */
   1767  1.1  christos 	relay = (struct dhcpv6_relay_packet *) forw_data;
   1768  1.1  christos 	cursor = offsetof(struct dhcpv6_relay_packet, options);
   1769  1.1  christos 	relay->msg_type = DHCPV6_RELAY_FORW;
   1770  1.1  christos 	if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
   1771  1.1  christos 		if (packet->dhcpv6_hop_count >= max_hop_count) {
   1772  1.1  christos 			log_info("Hop count exceeded,");
   1773  1.1  christos 			return;
   1774  1.1  christos 		}
   1775  1.1  christos 		relay->hop_count = packet->dhcpv6_hop_count + 1;
   1776  1.1  christos 		if (dp) {
   1777  1.1  christos 			memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
   1778  1.1  christos 		} else {
   1779  1.1  christos 			/* On smart relay add: && !global. */
   1780  1.1  christos 			if (!use_if_id && downstreams->next) {
   1781  1.1  christos 				log_info("Shan't get back the interface.");
   1782  1.1  christos 				return;
   1783  1.1  christos 			}
   1784  1.1  christos 			memset(&relay->link_address, 0, 16);
   1785  1.1  christos 		}
   1786  1.1  christos 
   1787  1.1  christos 		if (packet->client_port != htons(547)) {
   1788  1.1  christos 			relay_client_port = packet->client_port;
   1789  1.1  christos 		}
   1790  1.1  christos 	} else {
   1791  1.1  christos 		relay->hop_count = 0;
   1792  1.1  christos 		if (!dp)
   1793  1.1  christos 			return;
   1794  1.1  christos 		memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
   1795  1.1  christos 	}
   1796  1.1  christos 	memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
   1797  1.1  christos 
   1798  1.1  christos 	/* Get an option state. */
   1799  1.1  christos 	opts = NULL;
   1800  1.1  christos 	if (!option_state_allocate(&opts, MDL)) {
   1801  1.1  christos 		log_fatal("No memory for upwards options.");
   1802  1.1  christos 	}
   1803  1.3  christos 
   1804  1.1  christos 	/* Add an interface-id (if used). */
   1805  1.1  christos 	if (use_if_id) {
   1806  1.1  christos 		int if_id;
   1807  1.1  christos 
   1808  1.1  christos 		if (dp) {
   1809  1.1  christos 			if_id = dp->id;
   1810  1.1  christos 		} else if (!downstreams->next) {
   1811  1.1  christos 			if_id = downstreams->id;
   1812  1.1  christos 		} else {
   1813  1.1  christos 			log_info("Don't know the interface.");
   1814  1.1  christos 			option_state_dereference(&opts, MDL);
   1815  1.1  christos 			return;
   1816  1.1  christos 		}
   1817  1.1  christos 
   1818  1.1  christos 		if (!save_option_buffer(&dhcpv6_universe, opts,
   1819  1.1  christos 					NULL, (unsigned char *) &if_id,
   1820  1.1  christos 					sizeof(int),
   1821  1.1  christos 					D6O_INTERFACE_ID, 0)) {
   1822  1.1  christos 			log_error("Can't save interface-id.");
   1823  1.1  christos 			option_state_dereference(&opts, MDL);
   1824  1.1  christos 			return;
   1825  1.1  christos 		}
   1826  1.1  christos 	}
   1827  1.1  christos 
   1828  1.1  christos 	/* Add a subscriber-id if desired. */
   1829  1.1  christos 	/* This is for testing rather than general use */
   1830  1.1  christos 	if (dhcrelay_sub_id != NULL) {
   1831  1.1  christos 		if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
   1832  1.1  christos 					(unsigned char *) dhcrelay_sub_id,
   1833  1.1  christos 					strlen(dhcrelay_sub_id),
   1834  1.1  christos 					D6O_SUBSCRIBER_ID, 0)) {
   1835  1.1  christos 			log_error("Can't save subsriber-id.");
   1836  1.1  christos 			option_state_dereference(&opts, MDL);
   1837  1.1  christos 			return;
   1838  1.1  christos 		}
   1839  1.1  christos 	}
   1840  1.3  christos 
   1841  1.1  christos 
   1842  1.1  christos #if defined(RELAY_PORT)
   1843  1.1  christos 	/*
   1844  1.1  christos 	 * If we use a non-547 UDP source port or if we have received
   1845  1.1  christos 	 * from a downstream relay agent uses a non-547 port, we need
   1846  1.1  christos 	 * to include the RELAY-SOURCE-PORT option. The "Downstream
   1847  1.1  christos 	 * UDP Port" field value in the option allow us to send
   1848  1.1  christos 	 * relay-reply message back to the downstream relay agent
   1849  1.1  christos 	 * with the correct UDP source port.
   1850  1.1  christos         */
   1851  1.1  christos 	if (relay_port || relay_client_port) {
   1852  1.1  christos 		if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
   1853  1.1  christos 					(unsigned char *) &relay_client_port,
   1854  1.1  christos 					sizeof(u_int16_t),
   1855  1.1  christos 					D6O_RELAY_SOURCE_PORT, 0)) {
   1856  1.1  christos 			log_error("Can't save relay-source-port.");
   1857  1.1  christos 			option_state_dereference(&opts, MDL);
   1858  1.1  christos 			return;
   1859  1.1  christos 		}
   1860  1.1  christos 	}
   1861  1.1  christos #else
   1862  1.1  christos 	/* Avoid unused but set warning, */
   1863  1.1  christos 	(void)(relay_client_port);
   1864  1.1  christos #endif
   1865  1.1  christos 
   1866  1.1  christos 	/* Add the relay-msg carrying the packet. */
   1867  1.1  christos 	if (!save_option_buffer(&dhcpv6_universe, opts,
   1868  1.1  christos 				NULL, (unsigned char *) packet->raw,
   1869  1.1  christos 				packet->packet_length,
   1870  1.1  christos 				D6O_RELAY_MSG, 0)) {
   1871  1.1  christos 		log_error("Can't save relay-msg.");
   1872  1.1  christos 		option_state_dereference(&opts, MDL);
   1873  1.1  christos 		return;
   1874  1.1  christos 	}
   1875  1.1  christos 
   1876  1.1  christos 	/* Finish the relay-forward message. */
   1877  1.1  christos 	cursor += store_options6(forw_data + cursor,
   1878  1.1  christos 				 sizeof(forw_data) - cursor,
   1879  1.3  christos 				 opts, packet,
   1880  1.1  christos 				 required_forw_opts, NULL);
   1881  1.1  christos 	option_state_dereference(&opts, MDL);
   1882  1.1  christos 
   1883  1.1  christos 	/* Send it to all upstreams. */
   1884  1.1  christos 	for (up = upstreams; up; up = up->next) {
   1885  1.1  christos 		send_packet6(up->ifp, (unsigned char *) forw_data,
   1886  1.1  christos 			     (size_t) cursor, &up->link);
   1887  1.1  christos 	}
   1888  1.1  christos }
   1889  1.3  christos 
   1890  1.1  christos /*
   1891  1.1  christos  * Process a packet downwards, i.e., from server to client.
   1892  1.1  christos  */
   1893  1.1  christos static void
   1894  1.1  christos process_down6(struct packet *packet) {
   1895  1.1  christos 	struct stream_list *dp;
   1896  1.1  christos 	struct option_cache *oc;
   1897  1.1  christos 	struct data_string relay_msg;
   1898  1.1  christos 	const struct dhcpv6_packet *msg;
   1899  1.1  christos 	struct data_string if_id;
   1900  1.1  christos #if defined(RELAY_PORT)
   1901  1.1  christos 	struct data_string down_port;
   1902  1.1  christos #endif
   1903  1.1  christos 	struct sockaddr_in6 to;
   1904  1.1  christos 	struct iaddr peer;
   1905  1.1  christos 
   1906  1.1  christos 	/* The packet must be a relay-reply message. */
   1907  1.1  christos 	if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
   1908  1.1  christos 		if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
   1909  1.1  christos 			log_info("Discarding %s from %s port %d going down.",
   1910  1.1  christos 				 dhcpv6_type_names[packet->dhcpv6_msg_type],
   1911  1.1  christos 				 piaddr(packet->client_addr),
   1912  1.1  christos 				 ntohs(packet->client_port));
   1913  1.1  christos 		else
   1914  1.1  christos 			log_info("Unknown %d type from %s port %d going down.",
   1915  1.1  christos 				 packet->dhcpv6_msg_type,
   1916  1.1  christos 				 piaddr(packet->client_addr),
   1917  1.1  christos 				 ntohs(packet->client_port));
   1918  1.1  christos 		return;
   1919  1.1  christos 	}
   1920  1.1  christos 
   1921  1.1  christos 	/* Inits. */
   1922  1.1  christos 	memset(&relay_msg, 0, sizeof(relay_msg));
   1923  1.1  christos 	memset(&if_id, 0, sizeof(if_id));
   1924  1.1  christos #if defined(RELAY_PORT)
   1925  1.1  christos 	memset(&down_port, 0, sizeof(down_port));
   1926  1.1  christos #endif
   1927  1.1  christos 	memset(&to, 0, sizeof(to));
   1928  1.1  christos 	to.sin6_family = AF_INET6;
   1929  1.1  christos #ifdef HAVE_SA_LEN
   1930  1.1  christos 	to.sin6_len = sizeof(to);
   1931  1.1  christos #endif
   1932  1.1  christos 	to.sin6_port = remote_port;
   1933  1.1  christos 	peer.len = 16;
   1934  1.1  christos 
   1935  1.1  christos 	/* Get the relay-msg option (carrying the message to relay). */
   1936  1.1  christos 	oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
   1937  1.1  christos 	if (oc == NULL) {
   1938  1.1  christos 		log_info("No relay-msg.");
   1939  1.1  christos 		return;
   1940  1.1  christos 	}
   1941  1.1  christos 	if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
   1942  1.1  christos 				   packet->options, NULL,
   1943  1.1  christos 				   &global_scope, oc, MDL) ||
   1944  1.1  christos 	    (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
   1945  1.1  christos 		log_error("Can't evaluate relay-msg.");
   1946  1.3  christos 		goto cleanup;
   1947  1.1  christos 	}
   1948  1.1  christos 	msg = (const struct dhcpv6_packet *) relay_msg.data;
   1949  1.1  christos 
   1950  1.1  christos 	/* Get the interface-id (if exists) and the downstream. */
   1951  1.1  christos 	oc = lookup_option(&dhcpv6_universe, packet->options,
   1952  1.1  christos 			   D6O_INTERFACE_ID);
   1953  1.1  christos 	if (oc != NULL) {
   1954  1.1  christos 		int if_index;
   1955  1.1  christos 
   1956  1.1  christos 		if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
   1957  1.1  christos 					   packet->options, NULL,
   1958  1.1  christos 					   &global_scope, oc, MDL) ||
   1959  1.1  christos 		    (if_id.len != sizeof(int))) {
   1960  1.1  christos 			log_info("Can't evaluate interface-id.");
   1961  1.1  christos 			goto cleanup;
   1962  1.1  christos 		}
   1963  1.1  christos 		memcpy(&if_index, if_id.data, sizeof(int));
   1964  1.1  christos 		for (dp = downstreams; dp; dp = dp->next) {
   1965  1.1  christos 			if (dp->id == if_index)
   1966  1.1  christos 				break;
   1967  1.1  christos 		}
   1968  1.1  christos 	} else {
   1969  1.1  christos 		if (use_if_id) {
   1970  1.1  christos 			/* Require an interface-id. */
   1971  1.1  christos 			log_info("No interface-id.");
   1972  1.1  christos 			goto cleanup;
   1973  1.1  christos 		}
   1974  1.1  christos 		for (dp = downstreams; dp; dp = dp->next) {
   1975  1.1  christos 			/* Get the first matching one. */
   1976  1.1  christos 			if (!memcmp(&dp->link.sin6_addr,
   1977  1.1  christos 				    &packet->dhcpv6_link_address,
   1978  1.1  christos 				    sizeof(struct in6_addr)))
   1979  1.1  christos 				break;
   1980  1.1  christos 		}
   1981  1.1  christos 	}
   1982  1.1  christos 	/* Why bother when there is no choice. */
   1983  1.1  christos 	if (!dp && downstreams && !downstreams->next)
   1984  1.1  christos 		dp = downstreams;
   1985  1.1  christos 	if (!dp) {
   1986  1.1  christos 		log_info("Can't find the down interface.");
   1987  1.1  christos 		goto cleanup;
   1988  1.1  christos 	}
   1989  1.1  christos 	memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
   1990  1.1  christos 	to.sin6_addr = packet->dhcpv6_peer_address;
   1991  1.1  christos 
   1992  1.1  christos 	/* Check if we should relay the carried message. */
   1993  1.1  christos 	switch (msg->msg_type) {
   1994  1.1  christos 		/* Relay-Reply of for another relay, not a client. */
   1995  1.1  christos 	      case DHCPV6_RELAY_REPL:
   1996  1.1  christos 		to.sin6_port = local_port;
   1997  1.1  christos 
   1998  1.1  christos #if defined(RELAY_PORT)
   1999  1.1  christos 		oc = lookup_option(&dhcpv6_universe, packet->options,
   2000  1.1  christos 				   D6O_RELAY_SOURCE_PORT);
   2001  1.1  christos 		if (oc != NULL) {
   2002  1.1  christos 			u_int16_t down_relay_port;
   2003  1.1  christos 
   2004  1.1  christos 			memset(&down_port, 0, sizeof(down_port));
   2005  1.1  christos 			if (!evaluate_option_cache(&down_port, packet, NULL,
   2006  1.1  christos 						   NULL, packet->options, NULL,
   2007  1.1  christos 						   &global_scope, oc, MDL) ||
   2008  1.1  christos 			    (down_port.len != sizeof(u_int16_t))) {
   2009  1.1  christos 				log_info("Can't evaluate down "
   2010  1.1  christos 					 "relay-source-port.");
   2011  1.1  christos 				goto cleanup;
   2012  1.1  christos 			}
   2013  1.1  christos 			memcpy(&down_relay_port, down_port.data,
   2014  1.1  christos 			       sizeof(u_int16_t));
   2015  1.1  christos 			/*
   2016  1.1  christos 			 * If the down_relay_port value is non-zero,
   2017  1.1  christos 			 * that means our downstream relay agent uses
   2018  1.1  christos 			 * a non-547 UDP source port sending
   2019  1.1  christos 			 * relay-forw message to us. We need to use
   2020  1.1  christos 			 * the same UDP port sending reply back.
   2021  1.1  christos 			 */
   2022  1.1  christos 			if (down_relay_port) {
   2023  1.1  christos 				to.sin6_port = down_relay_port;
   2024  1.1  christos 			}
   2025  1.1  christos 		}
   2026  1.1  christos #endif
   2027  1.1  christos 
   2028  1.1  christos 		/* Fall into: */
   2029  1.1  christos 
   2030  1.1  christos 	      case DHCPV6_ADVERTISE:
   2031  1.1  christos 	      case DHCPV6_REPLY:
   2032  1.1  christos 	      case DHCPV6_RECONFIGURE:
   2033  1.1  christos 	      case DHCPV6_RELAY_FORW:
   2034  1.1  christos 	      case DHCPV6_LEASEQUERY_REPLY:
   2035  1.1  christos 	      case DHCPV6_DHCPV4_RESPONSE:
   2036  1.1  christos 		log_info("Relaying %s to %s port %d down.",
   2037  1.1  christos 			 dhcpv6_type_names[msg->msg_type],
   2038  1.1  christos 			 piaddr(peer),
   2039  1.1  christos 			 ntohs(to.sin6_port));
   2040  1.1  christos 		break;
   2041  1.1  christos 
   2042  1.1  christos 	      case DHCPV6_SOLICIT:
   2043  1.1  christos 	      case DHCPV6_REQUEST:
   2044  1.1  christos 	      case DHCPV6_CONFIRM:
   2045  1.1  christos 	      case DHCPV6_RENEW:
   2046  1.1  christos 	      case DHCPV6_REBIND:
   2047  1.1  christos 	      case DHCPV6_RELEASE:
   2048  1.1  christos 	      case DHCPV6_DECLINE:
   2049  1.1  christos 	      case DHCPV6_INFORMATION_REQUEST:
   2050  1.1  christos 	      case DHCPV6_LEASEQUERY:
   2051  1.1  christos 	      case DHCPV6_DHCPV4_QUERY:
   2052  1.1  christos 		log_info("Discarding %s to %s port %d down.",
   2053  1.1  christos 			 dhcpv6_type_names[msg->msg_type],
   2054  1.1  christos 			 piaddr(peer),
   2055  1.1  christos 			 ntohs(to.sin6_port));
   2056  1.1  christos 		goto cleanup;
   2057  1.1  christos 
   2058  1.1  christos 	      default:
   2059  1.1  christos 		log_info("Unknown %d type to %s port %d down.",
   2060  1.1  christos 			 msg->msg_type,
   2061  1.1  christos 			 piaddr(peer),
   2062  1.1  christos 			 ntohs(to.sin6_port));
   2063  1.1  christos 		goto cleanup;
   2064  1.1  christos 	}
   2065  1.1  christos 
   2066  1.1  christos 	/* Send the message to the downstream. */
   2067  1.1  christos 	send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
   2068  1.1  christos 		     (size_t) relay_msg.len, &to);
   2069  1.1  christos 
   2070  1.1  christos       cleanup:
   2071  1.1  christos 	if (relay_msg.data != NULL)
   2072  1.1  christos 		data_string_forget(&relay_msg, MDL);
   2073  1.1  christos 	if (if_id.data != NULL)
   2074  1.1  christos 		data_string_forget(&if_id, MDL);
   2075  1.1  christos }
   2076  1.6  christos #endif /* UNIT_TEST */
   2077  1.1  christos 
   2078  1.1  christos /*
   2079  1.1  christos  * Called by the dispatch packet handler with a decoded packet.
   2080  1.1  christos  */
   2081  1.1  christos void
   2082  1.1  christos dhcpv6(struct packet *packet) {
   2083  1.6  christos #ifndef UNIT_TEST
   2084  1.1  christos 	struct stream_list *dp;
   2085  1.1  christos 
   2086  1.1  christos 	/* Try all relay-replies downwards. */
   2087  1.1  christos 	if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
   2088  1.1  christos 		process_down6(packet);
   2089  1.1  christos 		return;
   2090  1.1  christos 	}
   2091  1.1  christos 	/* Others are candidates to go up if they come from down. */
   2092  1.1  christos 	for (dp = downstreams; dp; dp = dp->next) {
   2093  1.1  christos 		if (packet->interface != dp->ifp)
   2094  1.1  christos 			continue;
   2095  1.1  christos 		process_up6(packet, dp);
   2096  1.1  christos 		return;
   2097  1.1  christos 	}
   2098  1.1  christos 	/* Relay-forward could work from an unknown interface. */
   2099  1.1  christos 	if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
   2100  1.1  christos 		process_up6(packet, NULL);
   2101  1.1  christos 		return;
   2102  1.1  christos 	}
   2103  1.1  christos 
   2104  1.1  christos 	log_info("Can't process packet from interface '%s'.",
   2105  1.1  christos 		 packet->interface->name);
   2106  1.6  christos #endif /* UNIT_TEST */
   2107  1.1  christos }
   2108  1.6  christos #endif /* DHCPv6 */
   2109  1.1  christos 
   2110  1.1  christos /* Stub routines needed for linking with DHCP libraries. */
   2111  1.1  christos void
   2112  1.1  christos bootp(struct packet *packet) {
   2113  1.1  christos 	return;
   2114  1.1  christos }
   2115  1.1  christos 
   2116  1.1  christos void
   2117  1.1  christos dhcp(struct packet *packet) {
   2118  1.1  christos 	return;
   2119  1.1  christos }
   2120  1.1  christos 
   2121  1.1  christos #if defined(DHCPv6) && defined(DHCP4o6)
   2122  1.1  christos isc_result_t dhcpv4o6_handler(omapi_object_t *h)
   2123  1.1  christos {
   2124  1.1  christos 	return ISC_R_NOTIMPLEMENTED;
   2125  1.1  christos }
   2126  1.1  christos #endif
   2127  1.1  christos 
   2128  1.1  christos void
   2129  1.1  christos classify(struct packet *p, struct class *c) {
   2130  1.1  christos 	return;
   2131  1.1  christos }
   2132  1.1  christos 
   2133  1.1  christos int
   2134  1.1  christos check_collection(struct packet *p, struct lease *l, struct collection *c) {
   2135  1.1  christos 	return 0;
   2136  1.1  christos }
   2137  1.1  christos 
   2138  1.1  christos isc_result_t
   2139  1.1  christos find_class(struct class **class, const char *c1, const char *c2, int i) {
   2140  1.1  christos 	return ISC_R_NOTFOUND;
   2141  1.1  christos }
   2142  1.1  christos 
   2143  1.1  christos int
   2144  1.1  christos parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
   2145  1.1  christos 	return 0;
   2146  1.1  christos }
   2147  1.1  christos 
   2148  1.1  christos isc_result_t
   2149  1.1  christos dhcp_set_control_state(control_object_state_t oldstate,
   2150  1.1  christos 		       control_object_state_t newstate) {
   2151  1.1  christos 	char buf = 0;
   2152  1.1  christos 
   2153  1.1  christos 	if (newstate != server_shutdown)
   2154  1.1  christos 		return ISC_R_SUCCESS;
   2155  1.1  christos 
   2156  1.3  christos 	/* Log shutdown on signal. */
   2157  1.3  christos 	log_info("Received signal %d, initiating shutdown.", shutdown_signal);
   2158  1.3  christos 
   2159  1.1  christos 	if (no_pid_file == ISC_FALSE)
   2160  1.1  christos 		(void) unlink(path_dhcrelay_pid);
   2161  1.1  christos 
   2162  1.1  christos 	if (!no_daemon && dfd[0] != -1 && dfd[1] != -1) {
   2163  1.1  christos 		IGNORE_RET(write(dfd[1], &buf, 1));
   2164  1.1  christos 		(void) close(dfd[1]);
   2165  1.1  christos 		dfd[0] = dfd[1] = -1;
   2166  1.1  christos 	}
   2167  1.1  christos 	exit(0);
   2168  1.1  christos }
   2169  1.1  christos 
   2170  1.1  christos /*!
   2171  1.1  christos  *
   2172  1.1  christos  * \brief Allocate an interface as requested with a given set of flags
   2173  1.1  christos  *
   2174  1.1  christos  * The requested interface is allocated, its flags field is set to
   2175  1.1  christos  * INTERFACE_REQUESTED OR'd with the given flags,  and then added to
   2176  1.1  christos  * the list of interfaces.
   2177  1.1  christos  *
   2178  1.1  christos  * \param name - name of the requested interface
   2179  1.1  christos  * \param flags - additional flags for the interface
   2180  1.1  christos  *
   2181  1.1  christos  * \return Nothing
   2182  1.1  christos  */
   2183  1.1  christos void request_v4_interface(const char* name, int flags) {
   2184  1.1  christos         struct interface_info *tmp = NULL;
   2185  1.1  christos         int len = strlen(name);
   2186  1.1  christos         isc_result_t status;
   2187  1.1  christos 
   2188  1.1  christos         if (len >= sizeof(tmp->name)) {
   2189  1.1  christos                 log_fatal("%s: interface name too long (is %d)", name, len);
   2190  1.1  christos         }
   2191  1.1  christos 
   2192  1.1  christos         status = interface_allocate(&tmp, MDL);
   2193  1.1  christos         if (status != ISC_R_SUCCESS) {
   2194  1.1  christos                 log_fatal("%s: interface_allocate: %s", name,
   2195  1.1  christos                           isc_result_totext(status));
   2196  1.1  christos         }
   2197  1.1  christos 
   2198  1.1  christos 	log_debug("Requesting: %s as upstream: %c downstream: %c", name,
   2199  1.1  christos 		  (flags & INTERFACE_UPSTREAM ? 'Y' : 'N'),
   2200  1.1  christos 		  (flags & INTERFACE_DOWNSTREAM ? 'Y' : 'N'));
   2201  1.1  christos 
   2202  1.3  christos         memcpy(tmp->name, name, len);
   2203  1.1  christos         interface_snorf(tmp, (INTERFACE_REQUESTED | flags));
   2204  1.1  christos         interface_dereference(&tmp, MDL);
   2205  1.1  christos }
   2206