File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dhcp / relay / dhcrelay.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Oct 9 09:06:54 2012 UTC (11 years, 8 months ago) by misho
Branches: dhcp, MAIN
CVS tags: v4_1_R7p0, v4_1_R7, v4_1_R4, HEAD
dhcp 4.1 r7

    1: /* dhcrelay.c
    2: 
    3:    DHCP/BOOTP Relay Agent. */
    4: 
    5: /*
    6:  * Copyright(c) 2004-2012 by Internet Systems Consortium, Inc.("ISC")
    7:  * Copyright(c) 1997-2003 by Internet Software Consortium
    8:  *
    9:  * Permission to use, copy, modify, and distribute this software for any
   10:  * purpose with or without fee is hereby granted, provided that the above
   11:  * copyright notice and this permission notice appear in all copies.
   12:  *
   13:  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
   14:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   15:  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
   16:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   17:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   18:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
   19:  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   20:  *
   21:  *   Internet Systems Consortium, Inc.
   22:  *   950 Charter Street
   23:  *   Redwood City, CA 94063
   24:  *   <info@isc.org>
   25:  *   https://www.isc.org/
   26:  *
   27:  * This software has been written for Internet Systems Consortium
   28:  * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
   29:  * To learn more about Internet Systems Consortium, see
   30:  * ``https://www.isc.org/''.  To learn more about Vixie Enterprises,
   31:  * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
   32:  * ``http://www.nominum.com''.
   33:  */
   34: 
   35: #include "dhcpd.h"
   36: #include <syslog.h>
   37: #include <sys/time.h>
   38: #include <signal.h>
   39: 
   40: TIME default_lease_time = 43200; /* 12 hours... */
   41: TIME max_lease_time = 86400; /* 24 hours... */
   42: struct tree_cache *global_options[256];
   43: 
   44: struct option *requested_opts[2];
   45: 
   46: /* Needed to prevent linking against conflex.c. */
   47: int lexline;
   48: int lexchar;
   49: char *token_line;
   50: char *tlname;
   51: 
   52: const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID;
   53: isc_boolean_t no_dhcrelay_pid = ISC_FALSE;
   54: /* False (default) => we write and use a pid file */
   55: isc_boolean_t no_pid_file = ISC_FALSE;
   56: 
   57: int bogus_agent_drops = 0;	/* Packets dropped because agent option
   58: 				   field was specified and we're not relaying
   59: 				   packets that already have an agent option
   60: 				   specified. */
   61: int bogus_giaddr_drops = 0;	/* Packets sent to us to relay back to a
   62: 				   client, but with a bogus giaddr. */
   63: int client_packets_relayed = 0;	/* Packets relayed from client to server. */
   64: int server_packet_errors = 0;	/* Errors sending packets to servers. */
   65: int server_packets_relayed = 0;	/* Packets relayed from server to client. */
   66: int client_packet_errors = 0;	/* Errors sending packets to clients. */
   67: 
   68: int add_agent_options = 0;	/* If nonzero, add relay agent options. */
   69: 
   70: int agent_option_errors = 0;    /* Number of packets forwarded without
   71: 				   agent options because there was no room. */
   72: int drop_agent_mismatches = 0;	/* If nonzero, drop server replies that
   73: 				   don't have matching circuit-id's. */
   74: int corrupt_agent_options = 0;	/* Number of packets dropped because
   75: 				   relay agent information option was bad. */
   76: int missing_agent_option = 0;	/* Number of packets dropped because no
   77: 				   RAI option matching our ID was found. */
   78: int bad_circuit_id = 0;		/* Circuit ID option in matching RAI option
   79: 				   did not match any known circuit ID. */
   80: int missing_circuit_id = 0;	/* Circuit ID option in matching RAI option
   81: 				   was missing. */
   82: int max_hop_count = 10;		/* Maximum hop count */
   83: 
   84: #ifdef DHCPv6
   85: 	/* Force use of DHCPv6 interface-id option. */
   86: isc_boolean_t use_if_id = ISC_FALSE;
   87: #endif
   88: 
   89: 	/* Maximum size of a packet with agent options added. */
   90: int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
   91: 
   92: 	/* What to do about packets we're asked to relay that
   93: 	   already have a relay option: */
   94: enum { forward_and_append,	/* Forward and append our own relay option. */
   95:        forward_and_replace,	/* Forward, but replace theirs with ours. */
   96:        forward_untouched,	/* Forward without changes. */
   97:        discard } agent_relay_mode = forward_and_replace;
   98: 
   99: u_int16_t local_port;
  100: u_int16_t remote_port;
  101: 
  102: /* Relay agent server list. */
  103: struct server_list {
  104: 	struct server_list *next;
  105: 	struct sockaddr_in to;
  106: } *servers;
  107: 
  108: #ifdef DHCPv6
  109: struct stream_list {
  110: 	struct stream_list *next;
  111: 	struct interface_info *ifp;
  112: 	struct sockaddr_in6 link;
  113: 	int id;
  114: } *downstreams, *upstreams;
  115: 
  116: static struct stream_list *parse_downstream(char *);
  117: static struct stream_list *parse_upstream(char *);
  118: static void setup_streams(void);
  119: #endif
  120: 
  121: static void do_relay4(struct interface_info *, struct dhcp_packet *,
  122: 	              unsigned int, unsigned int, struct iaddr,
  123: 		      struct hardware *);
  124: static int add_relay_agent_options(struct interface_info *,
  125: 				   struct dhcp_packet *, unsigned,
  126: 				   struct in_addr);
  127: static int find_interface_by_agent_option(struct dhcp_packet *,
  128: 			       struct interface_info **, u_int8_t *, int);
  129: static int strip_relay_agent_options(struct interface_info *,
  130: 				     struct interface_info **,
  131: 				     struct dhcp_packet *, unsigned);
  132: 
  133: static const char copyright[] =
  134: "Copyright 2004-2012 Internet Systems Consortium.";
  135: static const char arr[] = "All rights reserved.";
  136: static const char message[] =
  137: "Internet Systems Consortium DHCP Relay Agent";
  138: static const char url[] =
  139: "For info, please visit https://www.isc.org/software/dhcp/";
  140: 
  141: #ifdef DHCPv6
  142: #define DHCRELAY_USAGE \
  143: "Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]\n"\
  144: "                     [-A <length>] [-c <hops>] [-p <port>]\n" \
  145: "                     [-pf <pid-file>] [--no-pid]\n"\
  146: "                     [-m append|replace|forward|discard]\n" \
  147: "                     [-i interface0 [ ... -i interfaceN]\n" \
  148: "                     server0 [ ... serverN]\n\n" \
  149: "       dhcrelay -6   [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
  150: "                     [-pf <pid-file>] [--no-pid]\n"\
  151: "                     -l lower0 [ ... -l lowerN]\n" \
  152: "                     -u upper0 [ ... -u upperN]\n" \
  153: "       lower (client link): [address%%]interface[#index]\n" \
  154: "       upper (server link): [address%%]interface"
  155: #else
  156: #define DHCRELAY_USAGE \
  157: "Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
  158: "                [-pf <pid-file>] [--no-pid]\n"\
  159: "                [-m append|replace|forward|discard]\n" \
  160: "                [-i interface0 [ ... -i interfaceN]\n" \
  161: "                server0 [ ... serverN]\n\n"
  162: #endif
  163: 
  164: static void usage() {
  165: 	log_fatal(DHCRELAY_USAGE);
  166: }
  167: 
  168: int 
  169: main(int argc, char **argv) {
  170: 	isc_result_t status;
  171: 	struct servent *ent;
  172: 	struct server_list *sp = NULL;
  173: 	struct interface_info *tmp = NULL;
  174: 	char *service_local = NULL, *service_remote = NULL;
  175: 	u_int16_t port_local = 0, port_remote = 0;
  176: 	int no_daemon = 0, quiet = 0;
  177: 	int fd;
  178: 	int i;
  179: #ifdef DHCPv6
  180: 	struct stream_list *sl = NULL;
  181: 	int local_family_set = 0;
  182: #endif
  183: 
  184: 	/* Make sure that file descriptors 0(stdin), 1,(stdout), and
  185: 	   2(stderr) are open. To do this, we assume that when we
  186: 	   open a file the lowest available file descriptor is used. */
  187: 	fd = open("/dev/null", O_RDWR);
  188: 	if (fd == 0)
  189: 		fd = open("/dev/null", O_RDWR);
  190: 	if (fd == 1)
  191: 		fd = open("/dev/null", O_RDWR);
  192: 	if (fd == 2)
  193: 		log_perror = 0; /* No sense logging to /dev/null. */
  194: 	else if (fd != -1)
  195: 		close(fd);
  196: 
  197: 	openlog("dhcrelay", LOG_NDELAY, LOG_DAEMON);
  198: 
  199: #if !defined(DEBUG)
  200: 	setlogmask(LOG_UPTO(LOG_INFO));
  201: #endif	
  202: 
  203: 	/*
  204: 	 * Set up the signal handlers, currently we only
  205: 	 * have one to ignore sigpipe.
  206: 	 */
  207: 	if (dhcp_handle_signal(SIGPIPE, SIG_IGN) != ISC_R_SUCCESS) {
  208: 		log_fatal("Can't set up signal handler");
  209: 	}
  210: 
  211: 	/* Set up the OMAPI. */
  212: 	status = omapi_init();
  213: 	if (status != ISC_R_SUCCESS)
  214: 		log_fatal("Can't initialize OMAPI: %s",
  215: 			   isc_result_totext(status));
  216: 
  217: 	/* Set up the OMAPI wrappers for the interface object. */
  218: 	interface_setup();
  219: 
  220: 	for (i = 1; i < argc; i++) {
  221: 		if (!strcmp(argv[i], "-4")) {
  222: #ifdef DHCPv6
  223: 			if (local_family_set && (local_family == AF_INET6)) {
  224: 				usage();
  225: 			}
  226: 			local_family_set = 1;
  227: 			local_family = AF_INET;
  228: 		} else if (!strcmp(argv[i], "-6")) {
  229: 			if (local_family_set && (local_family == AF_INET)) {
  230: 				usage();
  231: 			}
  232: 			local_family_set = 1;
  233: 			local_family = AF_INET6;
  234: #endif
  235: 		} else if (!strcmp(argv[i], "-d")) {
  236: 			no_daemon = 1;
  237: 		} else if (!strcmp(argv[i], "-q")) {
  238: 			quiet = 1;
  239: 			quiet_interface_discovery = 1;
  240: 		} else if (!strcmp(argv[i], "-p")) {
  241: 			if (++i == argc)
  242: 				usage();
  243: 			local_port = validate_port(argv[i]);
  244: 			log_debug("binding to user-specified port %d",
  245: 				  ntohs(local_port));
  246: 		} else if (!strcmp(argv[i], "-c")) {
  247: 			int hcount;
  248: 			if (++i == argc)
  249: 				usage();
  250: 			hcount = atoi(argv[i]);
  251: 			if (hcount <= 255)
  252: 				max_hop_count= hcount;
  253: 			else
  254: 				usage();
  255:  		} else if (!strcmp(argv[i], "-i")) {
  256: #ifdef DHCPv6
  257: 			if (local_family_set && (local_family == AF_INET6)) {
  258: 				usage();
  259: 			}
  260: 			local_family_set = 1;
  261: 			local_family = AF_INET;
  262: #endif
  263: 			status = interface_allocate(&tmp, MDL);
  264: 			if (status != ISC_R_SUCCESS)
  265: 				log_fatal("%s: interface_allocate: %s",
  266: 					  argv[i],
  267: 					  isc_result_totext(status));
  268: 			if (++i == argc) {
  269: 				usage();
  270: 			}
  271: 			strcpy(tmp->name, argv[i]);
  272: 			interface_snorf(tmp, INTERFACE_REQUESTED);
  273: 			interface_dereference(&tmp, MDL);
  274: 		} else if (!strcmp(argv[i], "-a")) {
  275: #ifdef DHCPv6
  276: 			if (local_family_set && (local_family == AF_INET6)) {
  277: 				usage();
  278: 			}
  279: 			local_family_set = 1;
  280: 			local_family = AF_INET;
  281: #endif
  282: 			add_agent_options = 1;
  283: 		} else if (!strcmp(argv[i], "-A")) {
  284: #ifdef DHCPv6
  285: 			if (local_family_set && (local_family == AF_INET6)) {
  286: 				usage();
  287: 			}
  288: 			local_family_set = 1;
  289: 			local_family = AF_INET;
  290: #endif
  291: 			if (++i == argc)
  292: 				usage();
  293: 
  294: 			dhcp_max_agent_option_packet_length = atoi(argv[i]);
  295: 
  296: 			if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
  297: 				log_fatal("%s: packet length exceeds "
  298: 					  "longest possible MTU\n",
  299: 					  argv[i]);
  300: 		} else if (!strcmp(argv[i], "-m")) {
  301: #ifdef DHCPv6
  302: 			if (local_family_set && (local_family == AF_INET6)) {
  303: 				usage();
  304: 			}
  305: 			local_family_set = 1;
  306: 			local_family = AF_INET;
  307: #endif
  308: 			if (++i == argc)
  309: 				usage();
  310: 			if (!strcasecmp(argv[i], "append")) {
  311: 				agent_relay_mode = forward_and_append;
  312: 			} else if (!strcasecmp(argv[i], "replace")) {
  313: 				agent_relay_mode = forward_and_replace;
  314: 			} else if (!strcasecmp(argv[i], "forward")) {
  315: 				agent_relay_mode = forward_untouched;
  316: 			} else if (!strcasecmp(argv[i], "discard")) {
  317: 				agent_relay_mode = discard;
  318: 			} else
  319: 				usage();
  320: 		} else if (!strcmp(argv[i], "-D")) {
  321: #ifdef DHCPv6
  322: 			if (local_family_set && (local_family == AF_INET6)) {
  323: 				usage();
  324: 			}
  325: 			local_family_set = 1;
  326: 			local_family = AF_INET;
  327: #endif
  328: 			drop_agent_mismatches = 1;
  329: #ifdef DHCPv6
  330: 		} else if (!strcmp(argv[i], "-I")) {
  331: 			if (local_family_set && (local_family == AF_INET)) {
  332: 				usage();
  333: 			}
  334: 			local_family_set = 1;
  335: 			local_family = AF_INET6;
  336: 			use_if_id = ISC_TRUE;
  337: 		} else if (!strcmp(argv[i], "-l")) {
  338: 			if (local_family_set && (local_family == AF_INET)) {
  339: 				usage();
  340: 			}
  341: 			local_family_set = 1;
  342: 			local_family = AF_INET6;
  343: 			if (downstreams != NULL)
  344: 				use_if_id = ISC_TRUE;
  345: 			if (++i == argc)
  346: 				usage();
  347: 			sl = parse_downstream(argv[i]);
  348: 			sl->next = downstreams;
  349: 			downstreams = sl;
  350: 		} else if (!strcmp(argv[i], "-u")) {
  351: 			if (local_family_set && (local_family == AF_INET)) {
  352: 				usage();
  353: 			}
  354: 			local_family_set = 1;
  355: 			local_family = AF_INET6;
  356: 			if (++i == argc)
  357: 				usage();
  358: 			sl = parse_upstream(argv[i]);
  359: 			sl->next = upstreams;
  360: 			upstreams = sl;
  361: #endif
  362: 		} else if (!strcmp(argv[i], "-pf")) {
  363: 			if (++i == argc)
  364: 				usage();
  365: 			path_dhcrelay_pid = argv[i];
  366: 			no_dhcrelay_pid = ISC_TRUE;
  367: 		} else if (!strcmp(argv[i], "--no-pid")) {
  368: 			no_pid_file = ISC_TRUE;
  369: 		} else if (!strcmp(argv[i], "--version")) {
  370: 			log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
  371: 			exit(0);
  372: 		} else if (!strcmp(argv[i], "--help") ||
  373: 			   !strcmp(argv[i], "-h")) {
  374: 			log_info(DHCRELAY_USAGE);
  375: 			exit(0);
  376:  		} else if (argv[i][0] == '-') {
  377: 			usage();
  378:  		} else {
  379: 			struct hostent *he;
  380: 			struct in_addr ia, *iap = NULL;
  381: 
  382: #ifdef DHCPv6
  383: 			if (local_family_set && (local_family == AF_INET6)) {
  384: 				usage();
  385: 			}
  386: 			local_family_set = 1;
  387: 			local_family = AF_INET;
  388: #endif
  389: 			if (inet_aton(argv[i], &ia)) {
  390: 				iap = &ia;
  391: 			} else {
  392: 				he = gethostbyname(argv[i]);
  393: 				if (!he) {
  394: 					log_error("%s: host unknown", argv[i]);
  395: 				} else {
  396: 					iap = ((struct in_addr *)
  397: 					       he->h_addr_list[0]);
  398: 				}
  399: 			}
  400: 
  401: 			if (iap) {
  402: 				sp = ((struct server_list *)
  403: 				      dmalloc(sizeof *sp, MDL));
  404: 				if (!sp)
  405: 					log_fatal("no memory for server.\n");
  406: 				sp->next = servers;
  407: 				servers = sp;
  408: 				memcpy(&sp->to.sin_addr, iap, sizeof *iap);
  409: 			}
  410:  		}
  411: 	}
  412: 
  413: 	/*
  414: 	 * If the user didn't specify a pid file directly
  415: 	 * find one from environment variables or defaults
  416: 	 */
  417: 	if (no_dhcrelay_pid == ISC_FALSE) {
  418: 		if (local_family == AF_INET) {
  419: 			path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
  420: 			if (path_dhcrelay_pid == NULL)
  421: 				path_dhcrelay_pid = _PATH_DHCRELAY_PID;
  422: 		}
  423: #ifdef DHCPv6
  424: 		else {
  425: 			path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
  426: 			if (path_dhcrelay_pid == NULL)
  427: 				path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
  428: 		}
  429: #endif
  430: 	}
  431: 
  432: 	if (!quiet) {
  433: 		log_info("%s %s", message, PACKAGE_VERSION);
  434: 		log_info(copyright);
  435: 		log_info(arr);
  436: 		log_info(url);
  437: 	} else {
  438: 		quiet = 0;
  439: 		log_perror = 0;
  440: 	}
  441: 
  442: 	/* Set default port */
  443: 	if (local_family == AF_INET) {
  444:  		service_local = "bootps";
  445:  		service_remote = "bootpc";
  446: 		port_local = htons(67);
  447:  		port_remote = htons(68);
  448: 	}
  449: #ifdef DHCPv6
  450: 	else {
  451: 		service_local = "dhcpv6-server";
  452: 		service_remote = "dhcpv6-client";
  453: 		port_local = htons(547);
  454: 		port_remote = htons(546);
  455: 	}
  456: #endif
  457: 
  458: 	if (!local_port) {
  459: 		ent = getservbyname(service_local, "udp");
  460: 		if (ent)
  461: 			local_port = ent->s_port;
  462: 		else
  463: 			local_port = port_local;
  464: 
  465: 		ent = getservbyname(service_remote, "udp");
  466: 		if (ent)
  467: 			remote_port = ent->s_port;
  468: 		else
  469: 			remote_port = port_remote;
  470: 
  471: 		endservent();
  472: 	}
  473: 
  474: 	if (local_family == AF_INET) {
  475: 		/* We need at least one server */
  476: 		if (servers == NULL) {
  477: 			log_fatal("No servers specified.");
  478: 		}
  479: 
  480: 
  481: 		/* Set up the server sockaddrs. */
  482: 		for (sp = servers; sp; sp = sp->next) {
  483: 			sp->to.sin_port = local_port;
  484: 			sp->to.sin_family = AF_INET;
  485: #ifdef HAVE_SA_LEN
  486: 			sp->to.sin_len = sizeof sp->to;
  487: #endif
  488: 		}
  489: 	}
  490: #ifdef DHCPv6
  491: 	else {
  492: 		unsigned code;
  493: 
  494: 		/* We need at least one upstream and one downstream interface */
  495: 		if (upstreams == NULL || downstreams == NULL) {
  496: 			log_info("Must specify at least one lower "
  497: 				 "and one upper interface.\n");
  498: 			usage();
  499: 		}
  500: 
  501: 		/* Set up the initial dhcp option universe. */
  502: 		initialize_common_option_spaces();
  503: 
  504: 		/* Check requested options. */
  505: 		code = D6O_RELAY_MSG;
  506: 		if (!option_code_hash_lookup(&requested_opts[0],
  507: 					     dhcpv6_universe.code_hash,
  508: 					     &code, 0, MDL))
  509: 			log_fatal("Unable to find the RELAY_MSG "
  510: 				  "option definition.");
  511: 		code = D6O_INTERFACE_ID;
  512: 		if (!option_code_hash_lookup(&requested_opts[1],
  513: 					     dhcpv6_universe.code_hash,
  514: 					     &code, 0, MDL))
  515: 			log_fatal("Unable to find the INTERFACE_ID "
  516: 				  "option definition.");
  517: 	}
  518: #endif
  519: 
  520: 	/* Get the current time... */
  521: 	gettimeofday(&cur_tv, NULL);
  522: 
  523: 	/* Discover all the network interfaces. */
  524: 	discover_interfaces(DISCOVER_RELAY);
  525: 
  526: #ifdef DHCPv6
  527: 	if (local_family == AF_INET6)
  528: 		setup_streams();
  529: #endif
  530: 
  531: 	/* Become a daemon... */
  532: 	if (!no_daemon) {
  533: 		int pid;
  534: 		FILE *pf;
  535: 		int pfdesc;
  536: 
  537: 		log_perror = 0;
  538: 
  539: 		if ((pid = fork()) < 0)
  540: 			log_fatal("Can't fork daemon: %m");
  541: 		else if (pid)
  542: 			exit(0);
  543: 
  544: 		if (no_pid_file == ISC_FALSE) {
  545: 			pfdesc = open(path_dhcrelay_pid,
  546: 				      O_CREAT | O_TRUNC | O_WRONLY, 0644);
  547: 
  548: 			if (pfdesc < 0) {
  549: 				log_error("Can't create %s: %m",
  550: 					  path_dhcrelay_pid);
  551: 			} else {
  552: 				pf = fdopen(pfdesc, "w");
  553: 				if (!pf)
  554: 					log_error("Can't fdopen %s: %m",
  555: 						  path_dhcrelay_pid);
  556: 				else {
  557: 					fprintf(pf, "%ld\n",(long)getpid());
  558: 					fclose(pf);
  559: 				}	
  560: 			}
  561: 		}
  562: 
  563: 		close(0);
  564: 		close(1);
  565: 		close(2);
  566: 		pid = setsid();
  567: 
  568: 		IGNORE_RET (chdir("/"));
  569: 	}
  570: 
  571: 	/* Set up the packet handler... */
  572: 	if (local_family == AF_INET)
  573: 		bootp_packet_handler = do_relay4;
  574: #ifdef DHCPv6
  575: 	else
  576: 		dhcpv6_packet_handler = do_packet6;
  577: #endif
  578: 
  579: 	/* Start dispatching packets and timeouts... */
  580: 	dispatch();
  581: 
  582: 	/* Not reached */
  583: 	return (0);
  584: }
  585: 
  586: static void
  587: do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
  588: 	  unsigned int length, unsigned int from_port, struct iaddr from,
  589: 	  struct hardware *hfrom) {
  590: 	struct server_list *sp;
  591: 	struct sockaddr_in to;
  592: 	struct interface_info *out;
  593: 	struct hardware hto, *htop;
  594: 
  595: 	if (packet->hlen > sizeof packet->chaddr) {
  596: 		log_info("Discarding packet with invalid hlen.");
  597: 		return;
  598: 	}
  599: 
  600: 	if (ip->address_count < 1 || ip->addresses == NULL) {
  601: 		log_info("Discarding packet received on %s interface that "
  602: 			 "has no IPv4 address assigned.", ip->name);
  603: 		return;
  604: 	}
  605: 
  606: 	/* Find the interface that corresponds to the giaddr
  607: 	   in the packet. */
  608: 	if (packet->giaddr.s_addr) {
  609: 		for (out = interfaces; out; out = out->next) {
  610: 			int i;
  611: 
  612: 			for (i = 0 ; i < out->address_count ; i++ ) {
  613: 				if (out->addresses[i].s_addr ==
  614: 				    packet->giaddr.s_addr)
  615: 					i = -1;
  616: 					break;
  617: 			}
  618: 
  619: 			if (i == -1)
  620: 				break;
  621: 		}
  622: 	} else {
  623: 		out = NULL;
  624: 	}
  625: 
  626: 	/* If it's a bootreply, forward it to the client. */
  627: 	if (packet->op == BOOTREPLY) {
  628: 		if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
  629: 			can_unicast_without_arp(out)) {
  630: 			to.sin_addr = packet->yiaddr;
  631: 			to.sin_port = remote_port;
  632: 
  633: 			/* and hardware address is not broadcast */
  634: 			htop = &hto;
  635: 		} else {
  636: 			to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
  637: 			to.sin_port = remote_port;
  638: 
  639: 			/* hardware address is broadcast */
  640: 			htop = NULL;
  641: 		}
  642: 		to.sin_family = AF_INET;
  643: #ifdef HAVE_SA_LEN
  644: 		to.sin_len = sizeof to;
  645: #endif
  646: 
  647: 		memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
  648: 		hto.hbuf[0] = packet->htype;
  649: 		hto.hlen = packet->hlen + 1;
  650: 
  651: 		/* Wipe out the agent relay options and, if possible, figure
  652: 		   out which interface to use based on the contents of the
  653: 		   option that we put on the request to which the server is
  654: 		   replying. */
  655: 		if (!(length =
  656: 		      strip_relay_agent_options(ip, &out, packet, length)))
  657: 			return;
  658: 
  659: 		if (!out) {
  660: 			log_error("Packet to bogus giaddr %s.\n",
  661: 			      inet_ntoa(packet->giaddr));
  662: 			++bogus_giaddr_drops;
  663: 			return;
  664: 		}
  665: 
  666: 		if (send_packet(out, NULL, packet, length, out->addresses[0],
  667: 				&to, htop) < 0) {
  668: 			++server_packet_errors;
  669: 		} else {
  670: 			log_debug("Forwarded BOOTREPLY for %s to %s",
  671: 			       print_hw_addr(packet->htype, packet->hlen,
  672: 					      packet->chaddr),
  673: 			       inet_ntoa(to.sin_addr));
  674: 
  675: 			++server_packets_relayed;
  676: 		}
  677: 		return;
  678: 	}
  679: 
  680: 	/* If giaddr matches one of our addresses, ignore the packet -
  681: 	   we just sent it. */
  682: 	if (out)
  683: 		return;
  684: 
  685: 	/* Add relay agent options if indicated.   If something goes wrong,
  686: 	   drop the packet. */
  687: 	if (!(length = add_relay_agent_options(ip, packet, length,
  688: 					       ip->addresses[0])))
  689: 		return;
  690: 
  691: 	/* If giaddr is not already set, Set it so the server can
  692: 	   figure out what net it's from and so that we can later
  693: 	   forward the response to the correct net.    If it's already
  694: 	   set, the response will be sent directly to the relay agent
  695: 	   that set giaddr, so we won't see it. */
  696: 	if (!packet->giaddr.s_addr)
  697: 		packet->giaddr = ip->addresses[0];
  698: 	if (packet->hops < max_hop_count)
  699: 		packet->hops = packet->hops + 1;
  700: 	else
  701: 		return;
  702: 
  703: 	/* Otherwise, it's a BOOTREQUEST, so forward it to all the
  704: 	   servers. */
  705: 	for (sp = servers; sp; sp = sp->next) {
  706: 		if (send_packet((fallback_interface
  707: 				 ? fallback_interface : interfaces),
  708: 				 NULL, packet, length, ip->addresses[0],
  709: 				 &sp->to, NULL) < 0) {
  710: 			++client_packet_errors;
  711: 		} else {
  712: 			log_debug("Forwarded BOOTREQUEST for %s to %s",
  713: 			       print_hw_addr(packet->htype, packet->hlen,
  714: 					      packet->chaddr),
  715: 			       inet_ntoa(sp->to.sin_addr));
  716: 			++client_packets_relayed;
  717: 		}
  718: 	}
  719: 				 
  720: }
  721: 
  722: /* Strip any Relay Agent Information options from the DHCP packet
  723:    option buffer.   If there is a circuit ID suboption, look up the
  724:    outgoing interface based upon it. */
  725: 
  726: static int
  727: strip_relay_agent_options(struct interface_info *in,
  728: 			  struct interface_info **out,
  729: 			  struct dhcp_packet *packet,
  730: 			  unsigned length) {
  731: 	int is_dhcp = 0;
  732: 	u_int8_t *op, *nextop, *sp, *max;
  733: 	int good_agent_option = 0;
  734: 	int status;
  735: 
  736: 	/* If we're not adding agent options to packets, we're not taking
  737: 	   them out either. */
  738: 	if (!add_agent_options)
  739: 		return (length);
  740: 
  741: 	/* If there's no cookie, it's a bootp packet, so we should just
  742: 	   forward it unchanged. */
  743: 	if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
  744: 		return (length);
  745: 
  746: 	max = ((u_int8_t *)packet) + length;
  747: 	sp = op = &packet->options[4];
  748: 
  749: 	while (op < max) {
  750: 		switch(*op) {
  751: 			/* Skip padding... */
  752: 		      case DHO_PAD:
  753: 			if (sp != op)
  754: 				*sp = *op;
  755: 			++op;
  756: 			++sp;
  757: 			continue;
  758: 
  759: 			/* If we see a message type, it's a DHCP packet. */
  760: 		      case DHO_DHCP_MESSAGE_TYPE:
  761: 			is_dhcp = 1;
  762: 			goto skip;
  763: 			break;
  764: 
  765: 			/* Quit immediately if we hit an End option. */
  766: 		      case DHO_END:
  767: 			if (sp != op)
  768: 				*sp++ = *op++;
  769: 			goto out;
  770: 
  771: 		      case DHO_DHCP_AGENT_OPTIONS:
  772: 			/* We shouldn't see a relay agent option in a
  773: 			   packet before we've seen the DHCP packet type,
  774: 			   but if we do, we have to leave it alone. */
  775: 			if (!is_dhcp)
  776: 				goto skip;
  777: 
  778: 			/* Do not process an agent option if it exceeds the
  779: 			 * buffer.  Fail this packet.
  780: 			 */
  781: 			nextop = op + op[1] + 2;
  782: 			if (nextop > max)
  783: 				return (0);
  784: 
  785: 			status = find_interface_by_agent_option(packet,
  786: 								out, op + 2,
  787: 								op[1]);
  788: 			if (status == -1 && drop_agent_mismatches)
  789: 				return (0);
  790: 			if (status)
  791: 				good_agent_option = 1;
  792: 			op = nextop;
  793: 			break;
  794: 
  795: 		      skip:
  796: 			/* Skip over other options. */
  797: 		      default:
  798: 			/* Fail if processing this option will exceed the
  799: 			 * buffer(op[1] is malformed).
  800: 			 */
  801: 			nextop = op + op[1] + 2;
  802: 			if (nextop > max)
  803: 				return (0);
  804: 
  805: 			if (sp != op) {
  806: 				memmove(sp, op, op[1] + 2);
  807: 				sp += op[1] + 2;
  808: 				op = nextop;
  809: 			} else
  810: 				op = sp = nextop;
  811: 
  812: 			break;
  813: 		}
  814: 	}
  815:       out:
  816: 
  817: 	/* If it's not a DHCP packet, we're not supposed to touch it. */
  818: 	if (!is_dhcp)
  819: 		return (length);
  820: 
  821: 	/* If none of the agent options we found matched, or if we didn't
  822: 	   find any agent options, count this packet as not having any
  823: 	   matching agent options, and if we're relying on agent options
  824: 	   to determine the outgoing interface, drop the packet. */
  825: 
  826: 	if (!good_agent_option) {
  827: 		++missing_agent_option;
  828: 		if (drop_agent_mismatches)
  829: 			return (0);
  830: 	}
  831: 
  832: 	/* Adjust the length... */
  833: 	if (sp != op) {
  834: 		length = sp -((u_int8_t *)packet);
  835: 
  836: 		/* Make sure the packet isn't short(this is unlikely,
  837: 		   but WTH) */
  838: 		if (length < BOOTP_MIN_LEN) {
  839: 			memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
  840: 			length = BOOTP_MIN_LEN;
  841: 		}
  842: 	}
  843: 	return (length);
  844: }
  845: 
  846: 
  847: /* Find an interface that matches the circuit ID specified in the
  848:    Relay Agent Information option.   If one is found, store it through
  849:    the pointer given; otherwise, leave the existing pointer alone.
  850: 
  851:    We actually deviate somewhat from the current specification here:
  852:    if the option buffer is corrupt, we suggest that the caller not
  853:    respond to this packet.  If the circuit ID doesn't match any known
  854:    interface, we suggest that the caller to drop the packet.  Only if
  855:    we find a circuit ID that matches an existing interface do we tell
  856:    the caller to go ahead and process the packet. */
  857: 
  858: static int
  859: find_interface_by_agent_option(struct dhcp_packet *packet,
  860: 			       struct interface_info **out,
  861: 			       u_int8_t *buf, int len) {
  862: 	int i = 0;
  863: 	u_int8_t *circuit_id = 0;
  864: 	unsigned circuit_id_len = 0;
  865: 	struct interface_info *ip;
  866: 
  867: 	while (i < len) {
  868: 		/* If the next agent option overflows the end of the
  869: 		   packet, the agent option buffer is corrupt. */
  870: 		if (i + 1 == len ||
  871: 		    i + buf[i + 1] + 2 > len) {
  872: 			++corrupt_agent_options;
  873: 			return (-1);
  874: 		}
  875: 		switch(buf[i]) {
  876: 			/* Remember where the circuit ID is... */
  877: 		      case RAI_CIRCUIT_ID:
  878: 			circuit_id = &buf[i + 2];
  879: 			circuit_id_len = buf[i + 1];
  880: 			i += circuit_id_len + 2;
  881: 			continue;
  882: 
  883: 		      default:
  884: 			i += buf[i + 1] + 2;
  885: 			break;
  886: 		}
  887: 	}
  888: 
  889: 	/* If there's no circuit ID, it's not really ours, tell the caller
  890: 	   it's no good. */
  891: 	if (!circuit_id) {
  892: 		++missing_circuit_id;
  893: 		return (-1);
  894: 	}
  895: 
  896: 	/* Scan the interface list looking for an interface whose
  897: 	   name matches the one specified in circuit_id. */
  898: 
  899: 	for (ip = interfaces; ip; ip = ip->next) {
  900: 		if (ip->circuit_id &&
  901: 		    ip->circuit_id_len == circuit_id_len &&
  902: 		    !memcmp(ip->circuit_id, circuit_id, circuit_id_len))
  903: 			break;
  904: 	}
  905: 
  906: 	/* If we got a match, use it. */
  907: 	if (ip) {
  908: 		*out = ip;
  909: 		return (1);
  910: 	}
  911: 
  912: 	/* If we didn't get a match, the circuit ID was bogus. */
  913: 	++bad_circuit_id;
  914: 	return (-1);
  915: }
  916: 
  917: /*
  918:  * Examine a packet to see if it's a candidate to have a Relay
  919:  * Agent Information option tacked onto its tail.   If it is, tack
  920:  * the option on.
  921:  */
  922: static int
  923: add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
  924: 			unsigned length, struct in_addr giaddr) {
  925: 	int is_dhcp = 0, mms;
  926: 	unsigned optlen;
  927: 	u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
  928: 
  929: 	/* If we're not adding agent options to packets, we can skip
  930: 	   this. */
  931: 	if (!add_agent_options)
  932: 		return (length);
  933: 
  934: 	/* If there's no cookie, it's a bootp packet, so we should just
  935: 	   forward it unchanged. */
  936: 	if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
  937: 		return (length);
  938: 
  939: 	max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
  940: 
  941: 	/* Commence processing after the cookie. */
  942: 	sp = op = &packet->options[4];
  943: 
  944: 	while (op < max) {
  945: 		switch(*op) {
  946: 			/* Skip padding... */
  947: 		      case DHO_PAD:
  948: 			/* Remember the first pad byte so we can commandeer
  949: 			 * padded space.
  950: 			 *
  951: 			 * XXX: Is this really a good idea?  Sure, we can
  952: 			 * seemingly reduce the packet while we're looking,
  953: 			 * but if the packet was signed by the client then
  954: 			 * this padding is part of the checksum(RFC3118),
  955: 			 * and its nonpresence would break authentication.
  956: 			 */
  957: 			if (end_pad == NULL)
  958: 				end_pad = sp;
  959: 
  960: 			if (sp != op)
  961: 				*sp++ = *op++;
  962: 			else
  963: 				sp = ++op;
  964: 
  965: 			continue;
  966: 
  967: 			/* If we see a message type, it's a DHCP packet. */
  968: 		      case DHO_DHCP_MESSAGE_TYPE:
  969: 			is_dhcp = 1;
  970: 			goto skip;
  971: 
  972: 			/*
  973: 			 * If there's a maximum message size option, we
  974: 			 * should pay attention to it
  975: 			 */
  976: 		      case DHO_DHCP_MAX_MESSAGE_SIZE:
  977: 			mms = ntohs(*(op + 2));
  978: 			if (mms < dhcp_max_agent_option_packet_length &&
  979: 			    mms >= DHCP_MTU_MIN)
  980: 				max = ((u_int8_t *)packet) + mms;
  981: 			goto skip;
  982: 
  983: 			/* Quit immediately if we hit an End option. */
  984: 		      case DHO_END:
  985: 			goto out;
  986: 
  987: 		      case DHO_DHCP_AGENT_OPTIONS:
  988: 			/* We shouldn't see a relay agent option in a
  989: 			   packet before we've seen the DHCP packet type,
  990: 			   but if we do, we have to leave it alone. */
  991: 			if (!is_dhcp)
  992: 				goto skip;
  993: 
  994: 			end_pad = NULL;
  995: 
  996: 			/* There's already a Relay Agent Information option
  997: 			   in this packet.   How embarrassing.   Decide what
  998: 			   to do based on the mode the user specified. */
  999: 
 1000: 			switch(agent_relay_mode) {
 1001: 			      case forward_and_append:
 1002: 				goto skip;
 1003: 			      case forward_untouched:
 1004: 				return (length);
 1005: 			      case discard:
 1006: 				return (0);
 1007: 			      case forward_and_replace:
 1008: 			      default:
 1009: 				break;
 1010: 			}
 1011: 
 1012: 			/* Skip over the agent option and start copying
 1013: 			   if we aren't copying already. */
 1014: 			op += op[1] + 2;
 1015: 			break;
 1016: 
 1017: 		      skip:
 1018: 			/* Skip over other options. */
 1019: 		      default:
 1020: 			/* Fail if processing this option will exceed the
 1021: 			 * buffer(op[1] is malformed).
 1022: 			 */
 1023: 			nextop = op + op[1] + 2;
 1024: 			if (nextop > max)
 1025: 				return (0);
 1026: 
 1027: 			end_pad = NULL;
 1028: 
 1029: 			if (sp != op) {
 1030: 				memmove(sp, op, op[1] + 2);
 1031: 				sp += op[1] + 2;
 1032: 				op = nextop;
 1033: 			} else
 1034: 				op = sp = nextop;
 1035: 
 1036: 			break;
 1037: 		}
 1038: 	}
 1039:       out:
 1040: 
 1041: 	/* If it's not a DHCP packet, we're not supposed to touch it. */
 1042: 	if (!is_dhcp)
 1043: 		return (length);
 1044: 
 1045: 	/* If the packet was padded out, we can store the agent option
 1046: 	   at the beginning of the padding. */
 1047: 
 1048: 	if (end_pad != NULL)
 1049: 		sp = end_pad;
 1050: 
 1051: 	/* Remember where the end of the packet was after parsing
 1052: 	   it. */
 1053: 	op = sp;
 1054: 
 1055: 	/* Sanity check.  Had better not ever happen. */
 1056: 	if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
 1057: 		log_fatal("Circuit ID length %d out of range [1-255] on "
 1058: 			  "%s\n", ip->circuit_id_len, ip->name);
 1059: 	optlen = ip->circuit_id_len + 2;            /* RAI_CIRCUIT_ID + len */
 1060: 
 1061: 	if (ip->remote_id) {
 1062: 		if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
 1063: 			log_fatal("Remote ID length %d out of range [1-255] "
 1064: 				  "on %s\n", ip->circuit_id_len, ip->name);
 1065: 		optlen += ip->remote_id_len + 2;    /* RAI_REMOTE_ID + len */
 1066: 	}
 1067: 
 1068: 	/* We do not support relay option fragmenting(multiple options to
 1069: 	 * support an option data exceeding 255 bytes).
 1070: 	 */
 1071: 	if ((optlen < 3) ||(optlen > 255))
 1072: 		log_fatal("Total agent option length(%u) out of range "
 1073: 			   "[3 - 255] on %s\n", optlen, ip->name);
 1074: 
 1075: 	/*
 1076: 	 * Is there room for the option, its code+len, and DHO_END?
 1077: 	 * If not, forward without adding the option.
 1078: 	 */
 1079: 	if (max - sp >= optlen + 3) {
 1080: 		log_debug("Adding %d-byte relay agent option", optlen + 3);
 1081: 
 1082: 		/* Okay, cons up *our* Relay Agent Information option. */
 1083: 		*sp++ = DHO_DHCP_AGENT_OPTIONS;
 1084: 		*sp++ = optlen;
 1085: 
 1086: 		/* Copy in the circuit id... */
 1087: 		*sp++ = RAI_CIRCUIT_ID;
 1088: 		*sp++ = ip->circuit_id_len;
 1089: 		memcpy(sp, ip->circuit_id, ip->circuit_id_len);
 1090: 		sp += ip->circuit_id_len;
 1091: 
 1092: 		/* Copy in remote ID... */
 1093: 		if (ip->remote_id) {
 1094: 			*sp++ = RAI_REMOTE_ID;
 1095: 			*sp++ = ip->remote_id_len;
 1096: 			memcpy(sp, ip->remote_id, ip->remote_id_len);
 1097: 			sp += ip->remote_id_len;
 1098: 		}
 1099: 	} else {
 1100: 		++agent_option_errors;
 1101: 		log_error("No room in packet (used %d of %d) "
 1102: 			  "for %d-byte relay agent option: omitted",
 1103: 			   (int) (sp - ((u_int8_t *) packet)),
 1104: 			   (int) (max - ((u_int8_t *) packet)),
 1105: 			   optlen + 3);
 1106: 	}
 1107: 
 1108: 	/*
 1109: 	 * Deposit an END option unless the packet is full (shouldn't
 1110: 	 * be possible).
 1111: 	 */
 1112: 	if (sp < max)
 1113: 		*sp++ = DHO_END;
 1114: 
 1115: 	/* Recalculate total packet length. */
 1116: 	length = sp -((u_int8_t *)packet);
 1117: 
 1118: 	/* Make sure the packet isn't short(this is unlikely, but WTH) */
 1119: 	if (length < BOOTP_MIN_LEN) {
 1120: 		memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
 1121: 		return (BOOTP_MIN_LEN);
 1122: 	}
 1123: 
 1124: 	return (length);
 1125: }
 1126: 
 1127: #ifdef DHCPv6
 1128: /*
 1129:  * Parse a downstream argument: [address%]interface[#index].
 1130:  */
 1131: static struct stream_list *
 1132: parse_downstream(char *arg) {
 1133: 	struct stream_list *dp, *up;
 1134: 	struct interface_info *ifp = NULL;
 1135: 	char *ifname, *addr, *iid;
 1136: 	isc_result_t status;
 1137: 
 1138: 	if (!supports_multiple_interfaces(ifp) &&
 1139: 	    (downstreams != NULL))
 1140: 		log_fatal("No support for multiple interfaces.");
 1141: 
 1142: 	/* Decode the argument. */
 1143: 	ifname = strchr(arg, '%');
 1144: 	if (ifname == NULL) {
 1145: 		ifname = arg;
 1146: 		addr = NULL;
 1147: 	} else {
 1148: 		*ifname++ = '\0';
 1149: 		addr = arg;
 1150: 	}
 1151: 	iid = strchr(ifname, '#');
 1152: 	if (iid != NULL) {
 1153: 		*iid++ = '\0';
 1154: 	}
 1155: 	if (strlen(ifname) >= sizeof(ifp->name)) {
 1156: 		log_error("Interface name '%s' too long", ifname);
 1157: 		usage();
 1158: 	}
 1159: 
 1160: 	/* Don't declare twice. */
 1161: 	for (dp = downstreams; dp; dp = dp->next) {
 1162: 		if (strcmp(ifname, dp->ifp->name) == 0)
 1163: 			log_fatal("Down interface '%s' declared twice.",
 1164: 				  ifname);
 1165: 	}
 1166: 
 1167: 	/* Share with up side? */
 1168: 	for (up = upstreams; up; up = up->next) {
 1169: 		if (strcmp(ifname, up->ifp->name) == 0) {
 1170: 			log_info("Interface '%s' is both down and up.",
 1171: 				 ifname);
 1172: 			ifp = up->ifp;
 1173: 			break;
 1174: 		}
 1175: 	}
 1176: 
 1177: 	/* New interface. */
 1178: 	if (ifp == NULL) {
 1179: 		status = interface_allocate(&ifp, MDL);
 1180: 		if (status != ISC_R_SUCCESS)
 1181: 			log_fatal("%s: interface_allocate: %s",
 1182: 				  arg, isc_result_totext(status));
 1183: 		strcpy(ifp->name, ifname);
 1184: 		if (interfaces) {
 1185: 			interface_reference(&ifp->next, interfaces, MDL);
 1186: 			interface_dereference(&interfaces, MDL);
 1187: 		}
 1188: 		interface_reference(&interfaces, ifp, MDL);
 1189: 		ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
 1190: 	}
 1191: 
 1192: 	/* New downstream. */
 1193: 	dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
 1194: 	if (!dp)
 1195: 		log_fatal("No memory for downstream.");
 1196: 	dp->ifp = ifp;
 1197: 	if (iid != NULL) {
 1198: 		dp->id = atoi(iid);
 1199: 	} else {
 1200: 		dp->id = -1;
 1201: 	}
 1202: 	/* !addr case handled by setup. */
 1203: 	if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
 1204: 		log_fatal("Bad link address '%s'", addr);
 1205: 
 1206: 	return dp;
 1207: }
 1208: 
 1209: /*
 1210:  * Parse an upstream argument: [address]%interface.
 1211:  */
 1212: static struct stream_list *
 1213: parse_upstream(char *arg) {
 1214: 	struct stream_list *up, *dp;
 1215: 	struct interface_info *ifp = NULL;
 1216: 	char *ifname, *addr;
 1217: 	isc_result_t status;
 1218: 
 1219: 	/* Decode the argument. */
 1220: 	ifname = strchr(arg, '%');
 1221: 	if (ifname == NULL) {
 1222: 		ifname = arg;
 1223: 		addr = All_DHCP_Servers;
 1224: 	} else {
 1225: 		*ifname++ = '\0';
 1226: 		addr = arg;
 1227: 	}
 1228: 	if (strlen(ifname) >= sizeof(ifp->name)) {
 1229: 		log_fatal("Interface name '%s' too long", ifname);
 1230: 	}
 1231: 
 1232: 	/* Shared up interface? */
 1233: 	for (up = upstreams; up; up = up->next) {
 1234: 		if (strcmp(ifname, up->ifp->name) == 0) {
 1235: 			ifp = up->ifp;
 1236: 			break;
 1237: 		}
 1238: 	}
 1239: 	for (dp = downstreams; dp; dp = dp->next) {
 1240: 		if (strcmp(ifname, dp->ifp->name) == 0) {
 1241: 			ifp = dp->ifp;
 1242: 			break;
 1243: 		}
 1244: 	}
 1245: 
 1246: 	/* New interface. */
 1247: 	if (ifp == NULL) {
 1248: 		status = interface_allocate(&ifp, MDL);
 1249: 		if (status != ISC_R_SUCCESS)
 1250: 			log_fatal("%s: interface_allocate: %s",
 1251: 				  arg, isc_result_totext(status));
 1252: 		strcpy(ifp->name, ifname);
 1253: 		if (interfaces) {
 1254: 			interface_reference(&ifp->next, interfaces, MDL);
 1255: 			interface_dereference(&interfaces, MDL);
 1256: 		}
 1257: 		interface_reference(&interfaces, ifp, MDL);
 1258: 		ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
 1259: 	}
 1260: 
 1261: 	/* New upstream. */
 1262: 	up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
 1263: 	if (up == NULL)
 1264: 		log_fatal("No memory for upstream.");
 1265: 
 1266: 	up->ifp = ifp;
 1267: 
 1268: 	if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
 1269: 		log_fatal("Bad address %s", addr);
 1270: 
 1271: 	return up;
 1272: }
 1273: 
 1274: /*
 1275:  * Setup downstream interfaces.
 1276:  */
 1277: static void
 1278: setup_streams(void) {
 1279: 	struct stream_list *dp, *up;
 1280: 	int i;
 1281: 	isc_boolean_t link_is_set;
 1282: 
 1283: 	for (dp = downstreams; dp; dp = dp->next) {
 1284: 		/* Check interface */
 1285: 		if (dp->ifp->v6address_count == 0)
 1286: 			log_fatal("Interface '%s' has no IPv6 addresses.",
 1287: 				  dp->ifp->name);
 1288: 
 1289: 		/* Check/set link. */
 1290: 		if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
 1291: 			link_is_set = ISC_FALSE;
 1292: 		else
 1293: 			link_is_set = ISC_TRUE;
 1294: 		for (i = 0; i < dp->ifp->v6address_count; i++) {
 1295: 			if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
 1296: 				continue;
 1297: 			if (!link_is_set)
 1298: 				break;
 1299: 			if (!memcmp(&dp->ifp->v6addresses[i],
 1300: 				    &dp->link.sin6_addr,
 1301: 				    sizeof(dp->link.sin6_addr)))
 1302: 				break;
 1303: 		}
 1304: 		if (i == dp->ifp->v6address_count)
 1305: 			log_fatal("Can't find link address for interface '%s'.",
 1306: 				  dp->ifp->name);
 1307: 		if (!link_is_set)
 1308: 			memcpy(&dp->link.sin6_addr,
 1309: 			       &dp->ifp->v6addresses[i],
 1310: 			       sizeof(dp->link.sin6_addr));
 1311: 
 1312: 		/* Set interface-id. */
 1313: 		if (dp->id == -1)
 1314: 			dp->id = dp->ifp->index;
 1315: 	}
 1316: 
 1317: 	for (up = upstreams; up; up = up->next) {
 1318: 		up->link.sin6_port = local_port;
 1319: 		up->link.sin6_family = AF_INET6;
 1320: #ifdef HAVE_SA_LEN
 1321: 		up->link.sin6_len = sizeof(up->link);
 1322: #endif
 1323: 
 1324: 		if (up->ifp->v6address_count == 0)
 1325: 			log_fatal("Interface '%s' has no IPv6 addresses.",
 1326: 				  up->ifp->name);
 1327: 	}
 1328: }
 1329: 
 1330: /*
 1331:  * Add DHCPv6 agent options here.
 1332:  */
 1333: static const int required_forw_opts[] = {
 1334: 	D6O_INTERFACE_ID,
 1335: 	D6O_RELAY_MSG,
 1336: 	0
 1337: };
 1338: 
 1339: /*
 1340:  * Process a packet upwards, i.e., from client to server.
 1341:  */
 1342: static void
 1343: process_up6(struct packet *packet, struct stream_list *dp) {
 1344: 	char forw_data[65535];
 1345: 	unsigned cursor;
 1346: 	struct dhcpv6_relay_packet *relay;
 1347: 	struct option_state *opts;
 1348: 	struct stream_list *up;
 1349: 
 1350: 	/* Check if the message should be relayed to the server. */
 1351: 	switch (packet->dhcpv6_msg_type) {
 1352: 	      case DHCPV6_SOLICIT:
 1353: 	      case DHCPV6_REQUEST:
 1354: 	      case DHCPV6_CONFIRM:
 1355: 	      case DHCPV6_RENEW:
 1356: 	      case DHCPV6_REBIND:
 1357: 	      case DHCPV6_RELEASE:
 1358: 	      case DHCPV6_DECLINE:
 1359: 	      case DHCPV6_INFORMATION_REQUEST:
 1360: 	      case DHCPV6_RELAY_FORW:
 1361: 	      case DHCPV6_LEASEQUERY:
 1362: 		log_info("Relaying %s from %s port %d going up.",
 1363: 			 dhcpv6_type_names[packet->dhcpv6_msg_type],
 1364: 			 piaddr(packet->client_addr),
 1365: 			 ntohs(packet->client_port));
 1366: 		break;
 1367: 
 1368: 	      case DHCPV6_ADVERTISE:
 1369: 	      case DHCPV6_REPLY:
 1370: 	      case DHCPV6_RECONFIGURE:
 1371: 	      case DHCPV6_RELAY_REPL:
 1372: 	      case DHCPV6_LEASEQUERY_REPLY:
 1373: 		log_info("Discarding %s from %s port %d going up.",
 1374: 			 dhcpv6_type_names[packet->dhcpv6_msg_type],
 1375: 			 piaddr(packet->client_addr),
 1376: 			 ntohs(packet->client_port));
 1377: 		return;
 1378: 
 1379: 	      default:
 1380: 		log_info("Unknown %d type from %s port %d going up.",
 1381: 			 packet->dhcpv6_msg_type,
 1382: 			 piaddr(packet->client_addr),
 1383: 			 ntohs(packet->client_port));
 1384: 		return;
 1385: 	}
 1386: 
 1387: 	/* Build the relay-forward header. */
 1388: 	relay = (struct dhcpv6_relay_packet *) forw_data;
 1389: 	cursor = offsetof(struct dhcpv6_relay_packet, options);
 1390: 	relay->msg_type = DHCPV6_RELAY_FORW;
 1391: 	if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
 1392: 		if (packet->dhcpv6_hop_count >= max_hop_count) {
 1393: 			log_info("Hop count exceeded,");
 1394: 			return;
 1395: 		}
 1396: 		relay->hop_count = packet->dhcpv6_hop_count + 1;
 1397: 		if (dp) {
 1398: 			memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
 1399: 		} else {
 1400: 			/* On smart relay add: && !global. */
 1401: 			if (!use_if_id && downstreams->next) {
 1402: 				log_info("Shan't get back the interface.");
 1403: 				return;
 1404: 			}
 1405: 			memset(&relay->link_address, 0, 16);
 1406: 		}
 1407: 	} else {
 1408: 		relay->hop_count = 0;
 1409: 		if (!dp)
 1410: 			return;
 1411: 		memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
 1412: 	}
 1413: 	memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
 1414: 
 1415: 	/* Get an option state. */
 1416: 	opts = NULL;
 1417: 	if (!option_state_allocate(&opts, MDL)) {
 1418: 		log_fatal("No memory for upwards options.");
 1419: 	}
 1420: 	
 1421: 	/* Add an interface-id (if used). */
 1422: 	if (use_if_id) {
 1423: 		int if_id;
 1424: 
 1425: 		if (dp) {
 1426: 			if_id = dp->id;
 1427: 		} else if (!downstreams->next) {
 1428: 			if_id = downstreams->id;
 1429: 		} else {
 1430: 			log_info("Don't know the interface.");
 1431: 			option_state_dereference(&opts, MDL);
 1432: 			return;
 1433: 		}
 1434: 
 1435: 		if (!save_option_buffer(&dhcpv6_universe, opts,
 1436: 					NULL, (unsigned char *) &if_id,
 1437: 					sizeof(int),
 1438: 					D6O_INTERFACE_ID, 0)) {
 1439: 			log_error("Can't save interface-id.");
 1440: 			option_state_dereference(&opts, MDL);
 1441: 			return;
 1442: 		}
 1443: 	}
 1444: 
 1445: 	/* Add the relay-msg carrying the packet. */
 1446: 	if (!save_option_buffer(&dhcpv6_universe, opts,
 1447: 				NULL, (unsigned char *) packet->raw,
 1448: 				packet->packet_length,
 1449: 				D6O_RELAY_MSG, 0)) {
 1450: 		log_error("Can't save relay-msg.");
 1451: 		option_state_dereference(&opts, MDL);
 1452: 		return;
 1453: 	}
 1454: 
 1455: 	/* Finish the relay-forward message. */
 1456: 	cursor += store_options6(forw_data + cursor,
 1457: 				 sizeof(forw_data) - cursor,
 1458: 				 opts, packet, 
 1459: 				 required_forw_opts, NULL);
 1460: 	option_state_dereference(&opts, MDL);
 1461: 
 1462: 	/* Send it to all upstreams. */
 1463: 	for (up = upstreams; up; up = up->next) {
 1464: 		send_packet6(up->ifp, (unsigned char *) forw_data,
 1465: 			     (size_t) cursor, &up->link);
 1466: 	}
 1467: }
 1468: 			     
 1469: /*
 1470:  * Process a packet downwards, i.e., from server to client.
 1471:  */
 1472: static void
 1473: process_down6(struct packet *packet) {
 1474: 	struct stream_list *dp;
 1475: 	struct option_cache *oc;
 1476: 	struct data_string relay_msg;
 1477: 	const struct dhcpv6_packet *msg;
 1478: 	struct data_string if_id;
 1479: 	struct sockaddr_in6 to;
 1480: 	struct iaddr peer;
 1481: 
 1482: 	/* The packet must be a relay-reply message. */
 1483: 	if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
 1484: 		if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
 1485: 			log_info("Discarding %s from %s port %d going down.",
 1486: 				 dhcpv6_type_names[packet->dhcpv6_msg_type],
 1487: 				 piaddr(packet->client_addr),
 1488: 				 ntohs(packet->client_port));
 1489: 		else
 1490: 			log_info("Unknown %d type from %s port %d going down.",
 1491: 				 packet->dhcpv6_msg_type,
 1492: 				 piaddr(packet->client_addr),
 1493: 				 ntohs(packet->client_port));
 1494: 		return;
 1495: 	}
 1496: 
 1497: 	/* Inits. */
 1498: 	memset(&relay_msg, 0, sizeof(relay_msg));
 1499: 	memset(&if_id, 0, sizeof(if_id));
 1500: 	memset(&to, 0, sizeof(to));
 1501: 	to.sin6_family = AF_INET6;
 1502: #ifdef HAVE_SA_LEN
 1503: 	to.sin6_len = sizeof(to);
 1504: #endif
 1505: 	to.sin6_port = remote_port;
 1506: 	peer.len = 16;
 1507: 
 1508: 	/* Get the relay-msg option (carrying the message to relay). */
 1509: 	oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
 1510: 	if (oc == NULL) {
 1511: 		log_info("No relay-msg.");
 1512: 		return;
 1513: 	}
 1514: 	if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
 1515: 				   packet->options, NULL,
 1516: 				   &global_scope, oc, MDL) ||
 1517: 	    (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
 1518: 		log_error("Can't evaluate relay-msg.");
 1519: 		return;
 1520: 	}
 1521: 	msg = (const struct dhcpv6_packet *) relay_msg.data;
 1522: 
 1523: 	/* Get the interface-id (if exists) and the downstream. */
 1524: 	oc = lookup_option(&dhcpv6_universe, packet->options,
 1525: 			   D6O_INTERFACE_ID);
 1526: 	if (oc != NULL) {
 1527: 		int if_index;
 1528: 
 1529: 		if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
 1530: 					   packet->options, NULL,
 1531: 					   &global_scope, oc, MDL) ||
 1532: 		    (if_id.len != sizeof(int))) {
 1533: 			log_info("Can't evaluate interface-id.");
 1534: 			goto cleanup;
 1535: 		}
 1536: 		memcpy(&if_index, if_id.data, sizeof(int));
 1537: 		for (dp = downstreams; dp; dp = dp->next) {
 1538: 			if (dp->id == if_index)
 1539: 				break;
 1540: 		}
 1541: 	} else {
 1542: 		if (use_if_id) {
 1543: 			/* Require an interface-id. */
 1544: 			log_info("No interface-id.");
 1545: 			goto cleanup;
 1546: 		}
 1547: 		for (dp = downstreams; dp; dp = dp->next) {
 1548: 			/* Get the first matching one. */
 1549: 			if (!memcmp(&dp->link.sin6_addr,
 1550: 				    &packet->dhcpv6_link_address,
 1551: 				    sizeof(struct in6_addr)))
 1552: 				break;
 1553: 		}
 1554: 	}
 1555: 	/* Why bother when there is no choice. */
 1556: 	if (!dp && !downstreams->next)
 1557: 		dp = downstreams;
 1558: 	if (!dp) {
 1559: 		log_info("Can't find the down interface.");
 1560: 		goto cleanup;
 1561: 	}
 1562: 	memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
 1563: 	to.sin6_addr = packet->dhcpv6_peer_address;
 1564: 
 1565: 	/* Check if we should relay the carried message. */
 1566: 	switch (msg->msg_type) {
 1567: 		/* Relay-Reply of for another relay, not a client. */
 1568: 	      case DHCPV6_RELAY_REPL:
 1569: 		to.sin6_port = local_port;
 1570: 		/* Fall into: */
 1571: 
 1572: 	      case DHCPV6_ADVERTISE:
 1573: 	      case DHCPV6_REPLY:
 1574: 	      case DHCPV6_RECONFIGURE:
 1575: 	      case DHCPV6_RELAY_FORW:
 1576: 	      case DHCPV6_LEASEQUERY_REPLY:
 1577: 		log_info("Relaying %s to %s port %d down.",
 1578: 			 dhcpv6_type_names[msg->msg_type],
 1579: 			 piaddr(peer),
 1580: 			 ntohs(to.sin6_port));
 1581: 		break;
 1582: 
 1583: 	      case DHCPV6_SOLICIT:
 1584: 	      case DHCPV6_REQUEST:
 1585: 	      case DHCPV6_CONFIRM:
 1586: 	      case DHCPV6_RENEW:
 1587: 	      case DHCPV6_REBIND:
 1588: 	      case DHCPV6_RELEASE:
 1589: 	      case DHCPV6_DECLINE:
 1590: 	      case DHCPV6_INFORMATION_REQUEST:
 1591: 	      case DHCPV6_LEASEQUERY:
 1592: 		log_info("Discarding %s to %s port %d down.",
 1593: 			 dhcpv6_type_names[msg->msg_type],
 1594: 			 piaddr(peer),
 1595: 			 ntohs(to.sin6_port));
 1596: 		goto cleanup;
 1597: 
 1598: 	      default:
 1599: 		log_info("Unknown %d type to %s port %d down.",
 1600: 			 msg->msg_type,
 1601: 			 piaddr(peer),
 1602: 			 ntohs(to.sin6_port));
 1603: 		goto cleanup;
 1604: 	}
 1605: 
 1606: 	/* Send the message to the downstream. */
 1607: 	send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
 1608: 		     (size_t) relay_msg.len, &to);
 1609: 
 1610:       cleanup:
 1611: 	if (relay_msg.data != NULL)
 1612: 		data_string_forget(&relay_msg, MDL);
 1613: 	if (if_id.data != NULL)
 1614: 		data_string_forget(&if_id, MDL);
 1615: }
 1616: 
 1617: /*
 1618:  * Called by the dispatch packet handler with a decoded packet.
 1619:  */
 1620: void
 1621: dhcpv6(struct packet *packet) {
 1622: 	struct stream_list *dp;
 1623: 
 1624: 	/* Try all relay-replies downwards. */
 1625: 	if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
 1626: 		process_down6(packet);
 1627: 		return;
 1628: 	}
 1629: 	/* Others are candidates to go up if they come from down. */
 1630: 	for (dp = downstreams; dp; dp = dp->next) {
 1631: 		if (packet->interface != dp->ifp)
 1632: 			continue;
 1633: 		process_up6(packet, dp);
 1634: 		return;
 1635: 	}
 1636: 	/* Relay-forward could work from an unknown interface. */
 1637: 	if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
 1638: 		process_up6(packet, NULL);
 1639: 		return;
 1640: 	}
 1641: 
 1642: 	log_info("Can't process packet from interface '%s'.",
 1643: 		 packet->interface->name);
 1644: }
 1645: #endif
 1646: 
 1647: /* Stub routines needed for linking with DHCP libraries. */
 1648: void
 1649: bootp(struct packet *packet) {
 1650: 	return;
 1651: }
 1652: 
 1653: void
 1654: dhcp(struct packet *packet) {
 1655: 	return;
 1656: }
 1657: 
 1658: void
 1659: classify(struct packet *p, struct class *c) {
 1660: 	return;
 1661: }
 1662: 
 1663: int
 1664: check_collection(struct packet *p, struct lease *l, struct collection *c) {
 1665: 	return 0;
 1666: }
 1667: 
 1668: isc_result_t
 1669: find_class(struct class **class, const char *c1, const char *c2, int i) {
 1670: 	return ISC_R_NOTFOUND;
 1671: }
 1672: 
 1673: int
 1674: parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
 1675: 	return 0;
 1676: }
 1677: 
 1678: isc_result_t
 1679: dhcp_set_control_state(control_object_state_t oldstate,
 1680: 		       control_object_state_t newstate) {
 1681: 	return ISC_R_SUCCESS;
 1682: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>