summaryrefslogtreecommitdiff
path: root/relay/dhcrelay.c
diff options
context:
space:
mode:
authorEvan Hunt <each@isc.org>2008-06-13 00:55:53 +0000
committerEvan Hunt <each@isc.org>2008-06-13 00:55:53 +0000
commit7de20a9518b7f6073c83702b34fe9d7fcb2abfb6 (patch)
treeb6d0a5042ebbe530bc5d733468f698ba4de247a4 /relay/dhcrelay.c
parentffbaa8801ec3e4806ac70acbeaaa672b0024fe22 (diff)
downloadisc-dhcp-7de20a9518b7f6073c83702b34fe9d7fcb2abfb6.tar.gz
- Merge dhcrelay6 into dhcrelay
- Prep for 4.1.0a2 release
Diffstat (limited to 'relay/dhcrelay.c')
-rw-r--r--relay/dhcrelay.c1317
1 files changed, 1003 insertions, 314 deletions
diff --git a/relay/dhcrelay.c b/relay/dhcrelay.c
index fa294c7d..c2015d54 100644
--- a/relay/dhcrelay.c
+++ b/relay/dhcrelay.c
@@ -3,8 +3,8 @@
DHCP/BOOTP Relay Agent. */
/*
- * Copyright (c) 2004-2008 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1997-2003 by Internet Software Consortium
+ * Copyright(c) 2004-2008 by Internet Systems Consortium, Inc.("ISC")
+ * Copyright(c) 1997-2003 by Internet Software Consortium
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -36,11 +36,11 @@
#include <syslog.h>
#include <sys/time.h>
-static void usage PROTO ((void));
-
TIME default_lease_time = 43200; /* 12 hours... */
TIME max_lease_time = 86400; /* 24 hours... */
-struct tree_cache *global_options [256];
+struct tree_cache *global_options[256];
+
+struct option *requested_opts[2];
/* Needed to prevent linking against conflex.c. */
int lexline;
@@ -74,6 +74,10 @@ int missing_circuit_id = 0; /* Circuit ID option in matching RAI option
was missing. */
int max_hop_count = 10; /* Maximum hop count */
+#ifdef DHCPv6
+ /* Force use of DHCPv6 interface-id option. */
+isc_boolean_t use_if_id = ISC_FALSE;
+#endif
/* Maximum size of a packet with agent options added. */
int dhcp_max_agent_option_packet_length = 576;
@@ -94,24 +98,78 @@ struct server_list {
struct sockaddr_in to;
} *servers;
-static char copyright [] = "Copyright 2004-2008 Internet Systems Consortium.";
-static char arr [] = "All rights reserved.";
-static char message [] = "Internet Systems Consortium DHCP Relay Agent";
-static char url [] = "For info, please visit http://www.isc.org/sw/dhcp/";
+#ifdef DHCPv6
+struct stream_list {
+ struct stream_list *next;
+ struct interface_info *ifp;
+ struct sockaddr_in6 link;
+ int id;
+} *downstreams, *upstreams;
+
+static struct stream_list *parse_downstream(char *);
+static struct stream_list *parse_upstream(char *);
+static void setup_streams(void);
+#endif
+
+static void do_relay4(struct interface_info *, struct dhcp_packet *,
+ unsigned int, unsigned int, struct iaddr,
+ struct hardware *);
+static int add_relay_agent_options(struct interface_info *,
+ struct dhcp_packet *, unsigned,
+ struct in_addr);
+static int find_interface_by_agent_option(struct dhcp_packet *,
+ struct interface_info **, u_int8_t *, int);
+static int strip_relay_agent_options(struct interface_info *,
+ struct interface_info **,
+ struct dhcp_packet *, unsigned);
+
+static char copyright[] = "Copyright 2004-2008 Internet Systems Consortium.";
+static char arr[] = "All rights reserved.";
+static char message[] = "Internet Systems Consortium DHCP Relay Agent";
+static char url[] = "For info, please visit http://www.isc.org/sw/dhcp/";
+
+#ifdef DHCPv6
+#define DHCRELAY_USAGE \
+"Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]\n"\
+" [-A <length>] [-c <hops>] [-p <port>]\n" \
+" [-m append|replace|forward|discard]\n" \
+" [-i interface0 [ ... -i interfaceN]\n" \
+" server0 [ ... serverN]\n\n" \
+" dhcrelay -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
+" -l lower0 [ ... -l lowerN]\n" \
+" -u upper0 [ ... -u upperN]\n" \
+" lower (client link): [address%%]interface[#index]\n" \
+" upper (server link): [address%%]interface"
+#else
+#define DHCRELAY_USAGE \
+"Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
+" [-m append|replace|forward|discard]\n" \
+" [-i interface0 [ ... -i interfaceN]\n" \
+" server0 [ ... serverN]\n\n"
+#endif
+
+static void usage() {
+ log_fatal(DHCRELAY_USAGE);
+}
int
main(int argc, char **argv) {
- int fd;
- int i;
+ isc_result_t status;
struct servent *ent;
struct server_list *sp = NULL;
- int no_daemon = 0;
- int quiet = 0;
- isc_result_t status;
- char *s;
+ struct interface_info *tmp = NULL;
+ char *service_local, *service_remote;
+ u_int16_t port_local, port_remote;
+ int no_daemon = 0, quiet = 0;
+ int fd;
+ int i;
+#ifdef DHCPv6
+ struct stream_list *sl = NULL;
+ int local_family_set = 0;
+#endif
- /* Make sure that file descriptors 0 (stdin), 1, (stdout), and
- 2 (stderr) are open. To do this, we assume that when we
+ /* Make sure that file descriptors 0(stdin), 1,(stdout), and
+ 2(stderr) are open. To do this, we assume that when we
open a file the lowest available file descriptor is used. */
fd = open("/dev/null", O_RDWR);
if (fd == 0)
@@ -123,157 +181,312 @@ main(int argc, char **argv) {
else if (fd != -1)
close(fd);
- openlog ("dhcrelay", LOG_NDELAY, LOG_DAEMON);
+ openlog("dhcrelay", LOG_NDELAY, LOG_DAEMON);
#if !defined(DEBUG)
- setlogmask (LOG_UPTO (LOG_INFO));
+ setlogmask(LOG_UPTO(LOG_INFO));
#endif
/* Set up the OMAPI. */
- status = omapi_init ();
+ status = omapi_init();
if (status != ISC_R_SUCCESS)
- log_fatal ("Can't initialize OMAPI: %s",
- isc_result_totext (status));
+ log_fatal("Can't initialize OMAPI: %s",
+ isc_result_totext(status));
/* Set up the OMAPI wrappers for the interface object. */
- interface_setup ();
+ interface_setup();
for (i = 1; i < argc; i++) {
- if (!strcmp (argv [i], "-p")) {
- if (++i == argc)
- usage ();
- local_port = htons (atoi (argv [i]));
- log_debug ("binding to user-specified port %d",
- ntohs (local_port));
- } else if (!strcmp (argv [i], "-d")) {
- no_daemon = 1;
- } else if (!strcmp (argv [i], "-i")) {
- struct interface_info *tmp =
- (struct interface_info *)0;
- status = interface_allocate (&tmp, MDL);
- if (status != ISC_R_SUCCESS)
- log_fatal ("%s: interface_allocate: %s",
- argv [i],
- isc_result_totext (status));
- if (++i == argc) {
- usage ();
+ if (!strcmp(argv[i], "-4")) {
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
}
- strcpy (tmp -> name, argv [i]);
- interface_snorf (tmp, INTERFACE_REQUESTED);
- interface_dereference (&tmp, MDL);
- } else if (!strcmp (argv [i], "-q")) {
+ local_family_set = 1;
+ local_family = AF_INET;
+ } else if (!strcmp(argv[i], "-6")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+#endif
+ } else if (!strcmp(argv[i], "-d")) {
+ no_daemon = 1;
+ } else if (!strcmp(argv[i], "-q")) {
quiet = 1;
quiet_interface_discovery = 1;
- } else if (!strcmp (argv [i], "-a")) {
- add_agent_options = 1;
- } else if (!strcmp (argv [i], "-c")) {
+ } else if (!strcmp(argv[i], "-p")) {
+ if (++i == argc)
+ usage();
+ local_port = htons(atoi (argv[i]));
+ log_debug("binding to user-specified port %d",
+ ntohs(local_port));
+ } else if (!strcmp(argv[i], "-c")) {
int hcount;
if (++i == argc)
- usage ();
+ usage();
hcount = atoi(argv[i]);
if (hcount <= 255)
max_hop_count= hcount;
else
- usage ();
- } else if (!strcmp (argv [i], "-A")) {
+ usage();
+ } else if (!strcmp(argv[i], "-i")) {
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+#endif
+ status = interface_allocate(&tmp, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal("%s: interface_allocate: %s",
+ argv[i],
+ isc_result_totext(status));
+ if (++i == argc) {
+ usage();
+ }
+ strcpy(tmp->name, argv[i]);
+ interface_snorf(tmp, INTERFACE_REQUESTED);
+ interface_dereference(&tmp, MDL);
+ } else if (!strcmp(argv[i], "-a")) {
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+#endif
+ add_agent_options = 1;
+ } else if (!strcmp(argv[i], "-A")) {
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+#endif
if (++i == argc)
- usage ();
- dhcp_max_agent_option_packet_length = atoi (argv [i]);
- } else if (!strcmp (argv [i], "-m")) {
+ usage();
+ dhcp_max_agent_option_packet_length = atoi(argv[i]);
+ } else if (!strcmp(argv[i], "-m")) {
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+#endif
if (++i == argc)
- usage ();
- if (!strcasecmp (argv [i], "append")) {
+ usage();
+ if (!strcasecmp(argv[i], "append")) {
agent_relay_mode = forward_and_append;
- } else if (!strcasecmp (argv [i], "replace")) {
+ } else if (!strcasecmp(argv[i], "replace")) {
agent_relay_mode = forward_and_replace;
- } else if (!strcasecmp (argv [i], "forward")) {
+ } else if (!strcasecmp(argv[i], "forward")) {
agent_relay_mode = forward_untouched;
- } else if (!strcasecmp (argv [i], "discard")) {
+ } else if (!strcasecmp(argv[i], "discard")) {
agent_relay_mode = discard;
} else
- usage ();
- } else if (!strcmp (argv [i], "-D")) {
+ usage();
+ } else if (!strcmp(argv[i], "-D")) {
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+#endif
drop_agent_mismatches = 1;
- } else if (!strcmp (argv [i], "--version")) {
- log_info ("isc-dhcrelay-%s", PACKAGE_VERSION);
- exit (0);
- } else if (argv [i][0] == '-') {
- usage ();
+#ifdef DHCPv6
+ } else if (!strcmp(argv[i], "-I")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ use_if_id = ISC_TRUE;
+ } else if (!strcmp(argv[i], "-l")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ if (downstreams != NULL)
+ use_if_id = ISC_TRUE;
+ if (++i == argc)
+ usage();
+ sl = parse_downstream(argv[i]);
+ sl->next = downstreams;
+ downstreams = sl;
+ } else if (!strcmp(argv[i], "-u")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ if (++i == argc)
+ usage();
+ sl = parse_upstream(argv[i]);
+ sl->next = upstreams;
+ upstreams = sl;
+#endif
+ } else if (!strcmp(argv[i], "--version")) {
+ log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
+ exit(0);
+ } else if (!strcmp(argv[i], "--help") ||
+ !strcmp(argv[i], "-h")) {
+ log_info(DHCRELAY_USAGE);
+ exit(0);
+ } else if (argv[i][0] == '-') {
+ usage();
} else {
struct hostent *he;
- struct in_addr ia, *iap = (struct in_addr *)0;
- if (inet_aton (argv [i], &ia)) {
+ struct in_addr ia, *iap = NULL;
+
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+#endif
+ if (inet_aton(argv[i], &ia)) {
iap = &ia;
} else {
- he = gethostbyname (argv [i]);
+ he = gethostbyname(argv[i]);
if (!he) {
- log_error ("%s: host unknown",
- argv [i]);
+ log_error("%s: host unknown", argv[i]);
} else {
iap = ((struct in_addr *)
- he -> h_addr_list [0]);
+ he->h_addr_list[0]);
}
}
+
if (iap) {
sp = ((struct server_list *)
- dmalloc (sizeof *sp, MDL));
+ dmalloc(sizeof *sp, MDL));
if (!sp)
- log_fatal ("no memory for server.\n");
- sp -> next = servers;
+ log_fatal("no memory for server.\n");
+ sp->next = servers;
servers = sp;
- memcpy (&sp -> to.sin_addr,
- iap, sizeof *iap);
+ memcpy(&sp->to.sin_addr, iap, sizeof *iap);
}
}
}
- if ((s = getenv ("PATH_DHCRELAY_PID"))) {
- path_dhcrelay_pid = s;
- }
+ if (local_family == AF_INET) {
+ path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
+ if (path_dhcrelay_pid == NULL)
+ path_dhcrelay_pid = _PATH_DHCRELAY_PID;
+ }
+#ifdef DHCPv6
+ else {
+ path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
+ if (path_dhcrelay_pid == NULL)
+ path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
+ }
+#endif
if (!quiet) {
- log_info ("%s %s", message, PACKAGE_VERSION);
- log_info (copyright);
- log_info (arr);
- log_info (url);
+ log_info("%s %s", message, PACKAGE_VERSION);
+ log_info(copyright);
+ log_info(arr);
+ log_info(url);
} else {
quiet = 0;
log_perror = 0;
}
- /* Default to the DHCP/BOOTP port. */
- if (!local_port) {
- ent = getservbyname ("dhcps", "udp");
- if (!ent)
- local_port = htons (67);
- else
- local_port = ent -> s_port;
- endservent ();
- }
- remote_port = htons (ntohs (local_port) + 1);
-
- /* We need at least one server. */
- if (!sp) {
- usage ();
- }
+ /* Set default port */
+ if (local_family == AF_INET) {
+ service_local = "dhcps";
+ service_remote = "dhcps";
+ port_local = htons(67);
+ port_remote = htons(67);
+ }
+#ifdef DHCPv6
+ else {
+ service_local = "dhcpv6-server";
+ service_remote = "dhcpv6-client";
+ port_local = htons(547);
+ port_remote = htons(546);
+ }
+#endif
- /* Set up the server sockaddrs. */
- for (sp = servers; sp; sp = sp -> next) {
- sp -> to.sin_port = local_port;
- sp -> to.sin_family = AF_INET;
+ if (!local_port) {
+ ent = getservbyname(service_local, "udp");
+ if (ent)
+ local_port = ent->s_port;
+ else
+ local_port = port_local;
+
+ ent = getservbyname(service_remote, "udp");
+ if (ent)
+ remote_port = ent->s_port;
+ else
+ remote_port = port_remote;
+
+ endservent();
+ }
+
+ if (local_family == AF_INET) {
+ /* We need at least one server */
+ if (servers == NULL) {
+ log_fatal("No servers specified.");
+ }
+
+
+ /* Set up the server sockaddrs. */
+ for (sp = servers; sp; sp = sp->next) {
+ sp->to.sin_port = local_port;
+ sp->to.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
- sp -> to.sin_len = sizeof sp -> to;
+ sp->to.sin_len = sizeof sp->to;
+#endif
+ }
+ }
+#ifdef DHCPv6
+ else {
+ unsigned code;
+
+ /* We need at least one upstream and one downstream interface */
+ if (upstreams == NULL || downstreams == NULL) {
+ log_info("Must specify at least one lower "
+ "and one upper interface.\n");
+ usage();
+ }
+
+ /* Set up the initial dhcp option universe. */
+ initialize_common_option_spaces();
+
+ /* Check requested options. */
+ code = D6O_RELAY_MSG;
+ if (!option_code_hash_lookup(&requested_opts[0],
+ dhcpv6_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find the RELAY_MSG "
+ "option definition.");
+ code = D6O_INTERFACE_ID;
+ if (!option_code_hash_lookup(&requested_opts[1],
+ dhcpv6_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find the INTERFACE_ID "
+ "option definition.");
+ }
#endif
- }
/* Get the current time... */
gettimeofday(&cur_tv, NULL);
/* Discover all the network interfaces. */
- discover_interfaces (DISCOVER_RELAY);
+ discover_interfaces(DISCOVER_RELAY);
- /* Set up the bootp packet handler... */
- bootp_packet_handler = relay;
+#ifdef DHCPv6
+ if (local_family == AF_INET6)
+ setup_streams();
+#endif
/* Become a daemon... */
if (!no_daemon) {
@@ -284,63 +497,67 @@ main(int argc, char **argv) {
log_perror = 0;
if ((pid = fork()) < 0)
- log_fatal ("can't fork daemon: %m");
+ log_fatal("Can't fork daemon: %m");
else if (pid)
- exit (0);
+ exit(0);
- pfdesc = open (path_dhcrelay_pid,
+ pfdesc = open(path_dhcrelay_pid,
O_CREAT | O_TRUNC | O_WRONLY, 0644);
if (pfdesc < 0) {
- log_error ("Can't create %s: %m", path_dhcrelay_pid);
+ log_error("Can't create %s: %m", path_dhcrelay_pid);
} else {
- pf = fdopen (pfdesc, "w");
+ pf = fdopen(pfdesc, "w");
if (!pf)
- log_error ("Can't fdopen %s: %m",
+ log_error("Can't fdopen %s: %m",
path_dhcrelay_pid);
else {
- fprintf (pf, "%ld\n", (long)getpid ());
- fclose (pf);
+ fprintf(pf, "%ld\n",(long)getpid());
+ fclose(pf);
}
}
- close (0);
- close (1);
- close (2);
- pid = setsid ();
+ close(0);
+ close(1);
+ close(2);
+ pid = setsid();
chdir("/");
}
+ /* Set up the packet handler... */
+ if (local_family == AF_INET)
+ bootp_packet_handler = do_relay4;
+#ifdef DHCPv6
+ else
+ dhcpv6_packet_handler = do_packet6;
+#endif
+
/* Start dispatching packets and timeouts... */
- dispatch ();
+ dispatch();
- /*NOTREACHED*/
- return 0;
+ /* Not reached */
+ return (0);
}
-void relay (ip, packet, length, from_port, from, hfrom)
- struct interface_info *ip;
- struct dhcp_packet *packet;
- unsigned length;
- unsigned int from_port;
- struct iaddr from;
- struct hardware *hfrom;
-{
+static void
+do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
+ unsigned int length, unsigned int from_port, struct iaddr from,
+ struct hardware *hfrom) {
struct server_list *sp;
struct sockaddr_in to;
struct interface_info *out;
struct hardware hto, *htop;
- if (packet -> hlen > sizeof packet -> chaddr) {
- log_info ("Discarding packet with invalid hlen.");
+ if (packet->hlen > sizeof packet->chaddr) {
+ log_info("Discarding packet with invalid hlen.");
return;
}
/* Find the interface that corresponds to the giaddr
in the packet. */
- if (packet -> giaddr.s_addr) {
- for (out = interfaces; out; out = out -> next) {
+ if (packet->giaddr.s_addr) {
+ for (out = interfaces; out; out = out->next) {
int i;
for (i = 0 ; i < out->address_count ; i++ ) {
@@ -354,20 +571,20 @@ void relay (ip, packet, length, from_port, from, hfrom)
break;
}
} else {
- out = (struct interface_info *)0;
+ out = NULL;
}
/* If it's a bootreply, forward it to the client. */
- if (packet -> op == BOOTREPLY) {
- if (!(packet -> flags & htons (BOOTP_BROADCAST)) &&
- can_unicast_without_arp (out)) {
- to.sin_addr = packet -> yiaddr;
+ if (packet->op == BOOTREPLY) {
+ if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
+ can_unicast_without_arp(out)) {
+ to.sin_addr = packet->yiaddr;
to.sin_port = remote_port;
/* and hardware address is not broadcast */
htop = &hto;
} else {
- to.sin_addr.s_addr = htonl (INADDR_BROADCAST);
+ to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
to.sin_port = remote_port;
/* hardware address is broadcast */
@@ -378,21 +595,21 @@ void relay (ip, packet, length, from_port, from, hfrom)
to.sin_len = sizeof to;
#endif
- memcpy (&hto.hbuf [1], packet -> chaddr, packet -> hlen);
- hto.hbuf [0] = packet -> htype;
- hto.hlen = packet -> hlen + 1;
+ memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
+ hto.hbuf[0] = packet->htype;
+ hto.hlen = packet->hlen + 1;
/* Wipe out the agent relay options and, if possible, figure
out which interface to use based on the contents of the
option that we put on the request to which the server is
replying. */
if (!(length =
- strip_relay_agent_options (ip, &out, packet, length)))
+ strip_relay_agent_options(ip, &out, packet, length)))
return;
if (!out) {
- log_error ("packet to bogus giaddr %s.\n",
- inet_ntoa (packet -> giaddr));
+ log_error("Packet to bogus giaddr %s.\n",
+ inet_ntoa(packet->giaddr));
++bogus_giaddr_drops;
return;
}
@@ -401,10 +618,10 @@ void relay (ip, packet, length, from_port, from, hfrom)
&to, htop) < 0) {
++server_packet_errors;
} else {
- log_debug ("forwarded BOOTREPLY for %s to %s",
- print_hw_addr (packet -> htype, packet -> hlen,
- packet -> chaddr),
- inet_ntoa (to.sin_addr));
+ log_debug("Forwarded BOOTREPLY for %s to %s",
+ print_hw_addr(packet->htype, packet->hlen,
+ packet->chaddr),
+ inet_ntoa(to.sin_addr));
++server_packets_relayed;
}
@@ -427,122 +644,41 @@ void relay (ip, packet, length, from_port, from, hfrom)
forward the response to the correct net. If it's already
set, the response will be sent directly to the relay agent
that set giaddr, so we won't see it. */
- if (!packet -> giaddr.s_addr)
+ if (!packet->giaddr.s_addr)
packet->giaddr = ip->addresses[0];
- if (packet -> hops < max_hop_count)
- packet -> hops = packet -> hops + 1;
+ if (packet->hops < max_hop_count)
+ packet->hops = packet->hops + 1;
else
return;
/* Otherwise, it's a BOOTREQUEST, so forward it to all the
servers. */
- for (sp = servers; sp; sp = sp -> next) {
+ for (sp = servers; sp; sp = sp->next) {
if (send_packet((fallback_interface
? fallback_interface : interfaces),
NULL, packet, length, ip->addresses[0],
&sp->to, NULL) < 0) {
++client_packet_errors;
} else {
- log_debug ("forwarded BOOTREQUEST for %s to %s",
- print_hw_addr (packet -> htype, packet -> hlen,
- packet -> chaddr),
- inet_ntoa (sp -> to.sin_addr));
+ log_debug("Forwarded BOOTREQUEST for %s to %s",
+ print_hw_addr(packet->htype, packet->hlen,
+ packet->chaddr),
+ inet_ntoa(sp->to.sin_addr));
++client_packets_relayed;
}
}
}
-static void usage ()
-{
- log_fatal ("Usage: dhcrelay [-p <port>] [-d] [-D] [-i %s%s%s%s",
- "interface] [-q] [-a]\n ",
- "[-c count] [-A length] ",
- "[-m append|replace|forward|discard]\n",
- " [server1 [... serverN]]");
-}
-
-int write_lease (lease)
- struct lease *lease;
-{
- return 1;
-}
-
-int write_host (host)
- struct host_decl *host;
-{
- return 1;
-}
-
-int commit_leases ()
-{
- return 1;
-}
-
-void bootp (packet)
- struct packet *packet;
-{
-}
-
-void dhcp (packet)
- struct packet *packet;
-{
-}
-
-#ifdef DHCPv6
-void
-dhcpv6(struct packet *packet) {
- /* XXX: should we warn or something here? */
-}
-#endif /* DHCPv6 */
-
-int find_subnet (struct subnet **sp,
- struct iaddr addr, const char *file, int line)
-{
- return 0;
-}
-
-#if defined (DEBUG)
-int check_collection (struct packet *p, struct lease *l,
- struct collection *c)
-{
- return 0;
-}
-
-void classify (struct packet *p, struct class *c)
-{
-}
-
-isc_result_t find_class (struct class **class, const char *c1,
- const char *c2, int i)
-{
- return ISC_R_NOTFOUND;
-}
-
-int parse_allow_deny (struct option_cache **oc, struct parse *p, int i)
-{
- return 0;
-}
-
-/* As a wise man once said in dhcpctl/omshell.c: */
-/* Sigh */
-isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
- control_object_state_t newstate)
-{
- return ISC_R_SUCCESS;
-}
-
-#endif
-
/* Strip any Relay Agent Information options from the DHCP packet
option buffer. If there is a circuit ID suboption, look up the
outgoing interface based upon it. */
-int strip_relay_agent_options (in, out, packet, length)
- struct interface_info *in, **out;
- struct dhcp_packet *packet;
- unsigned length;
-{
+static int
+strip_relay_agent_options(struct interface_info *in,
+ struct interface_info **out,
+ struct dhcp_packet *packet,
+ unsigned length) {
int is_dhcp = 0;
u_int8_t *op, *nextop, *sp, *max;
int good_agent_option = 0;
@@ -551,18 +687,18 @@ int strip_relay_agent_options (in, out, packet, length)
/* If we're not adding agent options to packets, we're not taking
them out either. */
if (!add_agent_options)
- return length;
+ return (length);
/* If there's no cookie, it's a bootp packet, so we should just
forward it unchanged. */
- if (memcmp (packet -> options, DHCP_OPTIONS_COOKIE, 4))
- return length;
+ if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
+ return (length);
max = ((u_int8_t *)packet) + length;
- sp = op = &packet -> options [4];
+ sp = op = &packet->options[4];
while (op < max) {
- switch (*op) {
+ switch(*op) {
/* Skip padding... */
case DHO_PAD:
if (sp != op)
@@ -595,13 +731,13 @@ int strip_relay_agent_options (in, out, packet, length)
*/
nextop = op + op[1] + 2;
if (nextop > max)
- return 0;
+ return (0);
- status = find_interface_by_agent_option (packet,
- out, op + 2,
- op [1]);
+ status = find_interface_by_agent_option(packet,
+ out, op + 2,
+ op[1]);
if (status == -1 && drop_agent_mismatches)
- return 0;
+ return (0);
if (status)
good_agent_option = 1;
op = nextop;
@@ -611,11 +747,11 @@ int strip_relay_agent_options (in, out, packet, length)
/* Skip over other options. */
default:
/* Fail if processing this option will exceed the
- * buffer (op[1] is malformed).
+ * buffer(op[1] is malformed).
*/
nextop = op + op[1] + 2;
if (nextop > max)
- return 0;
+ return (0);
if (sp != op) {
memmove(sp, op, op[1] + 2);
@@ -631,7 +767,7 @@ int strip_relay_agent_options (in, out, packet, length)
/* If it's not a DHCP packet, we're not supposed to touch it. */
if (!is_dhcp)
- return length;
+ return (length);
/* If none of the agent options we found matched, or if we didn't
find any agent options, count this packet as not having any
@@ -641,21 +777,21 @@ int strip_relay_agent_options (in, out, packet, length)
if (!good_agent_option) {
++missing_agent_option;
if (drop_agent_mismatches)
- return 0;
+ return (0);
}
/* Adjust the length... */
if (sp != op) {
- length = sp - ((u_int8_t *)packet);
+ length = sp -((u_int8_t *)packet);
- /* Make sure the packet isn't short (this is unlikely,
+ /* Make sure the packet isn't short(this is unlikely,
but WTH) */
if (length < BOOTP_MIN_LEN) {
- memset (sp, DHO_PAD, BOOTP_MIN_LEN - length);
+ memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
length = BOOTP_MIN_LEN;
}
}
- return length;
+ return (length);
}
@@ -670,12 +806,10 @@ int strip_relay_agent_options (in, out, packet, length)
we find a circuit ID that matches an existing interface do we tell
the caller to go ahead and process the packet. */
-int find_interface_by_agent_option (packet, out, buf, len)
- struct dhcp_packet *packet;
- struct interface_info **out;
- u_int8_t *buf;
- int len;
-{
+static int
+find_interface_by_agent_option(struct dhcp_packet *packet,
+ struct interface_info **out,
+ u_int8_t *buf, int len) {
int i = 0;
u_int8_t *circuit_id = 0;
unsigned circuit_id_len = 0;
@@ -685,20 +819,20 @@ int find_interface_by_agent_option (packet, out, buf, len)
/* If the next agent option overflows the end of the
packet, the agent option buffer is corrupt. */
if (i + 1 == len ||
- i + buf [i + 1] + 2 > len) {
+ i + buf[i + 1] + 2 > len) {
++corrupt_agent_options;
- return -1;
+ return (-1);
}
- switch (buf [i]) {
+ switch(buf[i]) {
/* Remember where the circuit ID is... */
case RAI_CIRCUIT_ID:
- circuit_id = &buf [i + 2];
- circuit_id_len = buf [i + 1];
+ circuit_id = &buf[i + 2];
+ circuit_id_len = buf[i + 1];
i += circuit_id_len + 2;
continue;
default:
- i += buf [i + 1] + 2;
+ i += buf[i + 1] + 2;
break;
}
}
@@ -707,40 +841,38 @@ int find_interface_by_agent_option (packet, out, buf, len)
it's no good. */
if (!circuit_id) {
++missing_circuit_id;
- return -1;
+ return (-1);
}
/* Scan the interface list looking for an interface whose
name matches the one specified in circuit_id. */
- for (ip = interfaces; ip; ip = ip -> next) {
- if (ip -> circuit_id &&
- ip -> circuit_id_len == circuit_id_len &&
- !memcmp (ip -> circuit_id, circuit_id, circuit_id_len))
+ for (ip = interfaces; ip; ip = ip->next) {
+ if (ip->circuit_id &&
+ ip->circuit_id_len == circuit_id_len &&
+ !memcmp(ip->circuit_id, circuit_id, circuit_id_len))
break;
}
/* If we got a match, use it. */
if (ip) {
*out = ip;
- return 1;
+ return (1);
}
/* If we didn't get a match, the circuit ID was bogus. */
++bad_circuit_id;
- return -1;
+ return (-1);
}
-/* Examine a packet to see if it's a candidate to have a Relay
- Agent Information option tacked onto its tail. If it is, tack
- the option on. */
-
-int add_relay_agent_options (ip, packet, length, giaddr)
- struct interface_info *ip;
- struct dhcp_packet *packet;
- unsigned length;
- struct in_addr giaddr;
-{
+/*
+ * Examine a packet to see if it's a candidate to have a Relay
+ * Agent Information option tacked onto its tail. If it is, tack
+ * the option on.
+ */
+static int
+add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
+ unsigned length, struct in_addr giaddr) {
int is_dhcp = 0;
unsigned optlen;
u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
@@ -748,12 +880,12 @@ int add_relay_agent_options (ip, packet, length, giaddr)
/* If we're not adding agent options to packets, we can skip
this. */
if (!add_agent_options)
- return length;
+ return (length);
/* If there's no cookie, it's a bootp packet, so we should just
forward it unchanged. */
if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
- return length;
+ return (length);
max = ((u_int8_t *)packet) + length;
@@ -761,7 +893,7 @@ int add_relay_agent_options (ip, packet, length, giaddr)
sp = op = &packet->options[4];
while (op < max) {
- switch (*op) {
+ switch(*op) {
/* Skip padding... */
case DHO_PAD:
/* Remember the first pad byte so we can commandeer
@@ -770,7 +902,7 @@ int add_relay_agent_options (ip, packet, length, giaddr)
* XXX: Is this really a good idea? Sure, we can
* seemingly reduce the packet while we're looking,
* but if the packet was signed by the client then
- * this padding is part of the checksum (RFC3118),
+ * this padding is part of the checksum(RFC3118),
* and its nonpresence would break authentication.
*/
if (end_pad == NULL)
@@ -806,13 +938,13 @@ int add_relay_agent_options (ip, packet, length, giaddr)
in this packet. How embarrassing. Decide what
to do based on the mode the user specified. */
- switch (agent_relay_mode) {
+ switch(agent_relay_mode) {
case forward_and_append:
goto skip;
case forward_untouched:
- return length;
+ return (length);
case discard:
- return 0;
+ return (0);
case forward_and_replace:
default:
break;
@@ -827,11 +959,11 @@ int add_relay_agent_options (ip, packet, length, giaddr)
/* Skip over other options. */
default:
/* Fail if processing this option will exceed the
- * buffer (op[1] is malformed).
+ * buffer(op[1] is malformed).
*/
nextop = op + op[1] + 2;
if (nextop > max)
- return 0;
+ return (0);
end_pad = NULL;
@@ -849,7 +981,7 @@ int add_relay_agent_options (ip, packet, length, giaddr)
/* If it's not a DHCP packet, we're not supposed to touch it. */
if (!is_dhcp)
- return length;
+ return (length);
/* If the packet was padded out, we can store the agent option
at the beginning of the padding. */
@@ -862,28 +994,28 @@ int add_relay_agent_options (ip, packet, length, giaddr)
op = sp;
/* Sanity check. Had better not ever happen. */
- if ((ip->circuit_id_len > 255) || (ip->circuit_id_len < 1))
- log_fatal("circuit id length %d out of range [1-255] on "
+ if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
+ log_fatal("Circuit ID length %d out of range [1-255] on "
"%s\n", ip->circuit_id_len, ip->name);
optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */
if (ip->remote_id) {
if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
- log_fatal("remote id length %d out of range [1-255] "
+ log_fatal("Remote ID length %d out of range [1-255] "
"on %s\n", ip->circuit_id_len, ip->name);
optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */
}
- /* We do not support relay option fragmenting (multiple options to
+ /* We do not support relay option fragmenting(multiple options to
* support an option data exceeding 255 bytes).
*/
- if ((optlen < 3) || (optlen > 255))
- log_fatal ("total agent option length (%u) out of range "
+ if ((optlen < 3) ||(optlen > 255))
+ log_fatal("Total agent option length(%u) out of range "
"[3 - 255] on %s\n", optlen, ip->name);
/* Is there room for the option, its code+len, and DHO_END? */
- if ((sp > max) || (max - sp < optlen + 3))
- return 0;
+ if ((sp > max) ||(max - sp < optlen + 3))
+ return (0);
/* Okay, cons up *our* Relay Agent Information option. */
*sp++ = DHO_DHCP_AGENT_OPTIONS;
@@ -907,13 +1039,570 @@ int add_relay_agent_options (ip, packet, length, giaddr)
*sp++ = DHO_END;
/* Recalculate total packet length. */
- length = sp - ((u_int8_t *)packet);
+ length = sp -((u_int8_t *)packet);
- /* Make sure the packet isn't short (this is unlikely, but WTH) */
+ /* Make sure the packet isn't short(this is unlikely, but WTH) */
if (length < BOOTP_MIN_LEN) {
memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
- return BOOTP_MIN_LEN;
+ return (BOOTP_MIN_LEN);
+ }
+
+ return (length);
+}
+
+#ifdef DHCPv6
+/*
+ * Parse a downstream argument: [address%]interface[#index].
+ */
+static struct stream_list *
+parse_downstream(char *arg) {
+ struct stream_list *dp, *up;
+ struct interface_info *ifp = NULL;
+ char *ifname, *addr, *iid;
+ isc_result_t status;
+
+ if (!supports_multiple_interfaces(ifp) &&
+ (downstreams != NULL))
+ log_fatal("No support for multiple interfaces.");
+
+ /* Decode the argument. */
+ ifname = strchr(arg, '%');
+ if (ifname == NULL) {
+ ifname = arg;
+ addr = NULL;
+ } else {
+ *ifname++ = '\0';
+ addr = arg;
+ }
+ iid = strchr(ifname, '#');
+ if (iid != NULL) {
+ *iid++ = '\0';
+ }
+ if (strlen(ifname) >= sizeof(ifp->name)) {
+ log_error("Interface name '%s' too long", ifname);
+ usage();
+ }
+
+ /* Don't declare twice. */
+ for (dp = downstreams; dp; dp = dp->next) {
+ if (strcmp(ifname, dp->ifp->name) == 0)
+ log_fatal("Down interface '%s' declared twice.",
+ ifname);
+ }
+
+ /* Share with up side? */
+ for (up = upstreams; up; up = up->next) {
+ if (strcmp(ifname, up->ifp->name) == 0) {
+ log_info("Interface '%s' is both down and up.",
+ ifname);
+ ifp = up->ifp;
+ break;
+ }
+ }
+
+ /* New interface. */
+ if (ifp == NULL) {
+ status = interface_allocate(&ifp, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal("%s: interface_allocate: %s",
+ arg, isc_result_totext(status));
+ strcpy(ifp->name, ifname);
+ if (interfaces) {
+ interface_reference(&ifp->next, interfaces, MDL);
+ interface_dereference(&interfaces, MDL);
+ }
+ interface_reference(&interfaces, ifp, MDL);
+ ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
+ }
+
+ /* New downstream. */
+ dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
+ if (!dp)
+ log_fatal("No memory for downstream.");
+ dp->ifp = ifp;
+ if (iid != NULL) {
+ dp->id = atoi(iid);
+ } else {
+ dp->id = -1;
+ }
+ /* !addr case handled by setup. */
+ if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
+ log_fatal("Bad link address '%s'", addr);
+
+ return dp;
+}
+
+/*
+ * Parse an upstream argument: [address]%interface.
+ */
+static struct stream_list *
+parse_upstream(char *arg) {
+ struct stream_list *up, *dp;
+ struct interface_info *ifp = NULL;
+ char *ifname, *addr;
+ isc_result_t status;
+
+ /* Decode the argument. */
+ ifname = strchr(arg, '%');
+ if (ifname == NULL) {
+ ifname = arg;
+ addr = All_DHCP_Servers;
+ } else {
+ *ifname++ = '\0';
+ addr = arg;
+ }
+ if (strlen(ifname) >= sizeof(ifp->name)) {
+ log_fatal("Interface name '%s' too long", ifname);
+ }
+
+ /* Shared up interface? */
+ for (up = upstreams; up; up = up->next) {
+ if (strcmp(ifname, up->ifp->name) == 0) {
+ ifp = up->ifp;
+ break;
+ }
+ }
+ for (dp = downstreams; dp; dp = dp->next) {
+ if (strcmp(ifname, dp->ifp->name) == 0) {
+ ifp = dp->ifp;
+ break;
+ }
+ }
+
+ /* New interface. */
+ if (ifp == NULL) {
+ status = interface_allocate(&ifp, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal("%s: interface_allocate: %s",
+ arg, isc_result_totext(status));
+ strcpy(ifp->name, ifname);
+ if (interfaces) {
+ interface_reference(&ifp->next, interfaces, MDL);
+ interface_dereference(&interfaces, MDL);
+ }
+ interface_reference(&interfaces, ifp, MDL);
+ ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
+ }
+
+ /* New upstream. */
+ up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
+ if (up == NULL)
+ log_fatal("No memory for upstream.");
+
+ up->ifp = ifp;
+
+ if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
+ log_fatal("Bad address %s", addr);
+
+ return up;
+}
+
+/*
+ * Setup downstream interfaces.
+ */
+static void
+setup_streams(void) {
+ struct stream_list *dp, *up;
+ int i;
+ isc_boolean_t link_is_set;
+
+ for (dp = downstreams; dp; dp = dp->next) {
+ /* Check interface */
+ if (dp->ifp->v6address_count == 0)
+ log_fatal("Interface '%s' has no IPv6 addresses.",
+ dp->ifp->name);
+
+ /* Check/set link. */
+ if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
+ link_is_set = ISC_FALSE;
+ else
+ link_is_set = ISC_TRUE;
+ for (i = 0; i < dp->ifp->v6address_count; i++) {
+ if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
+ continue;
+ if (!link_is_set)
+ break;
+ if (!memcmp(&dp->ifp->v6addresses[i],
+ &dp->link.sin6_addr,
+ sizeof(dp->link.sin6_addr)))
+ break;
+ }
+ if (i == dp->ifp->v6address_count)
+ log_fatal("Can't find link address for interface '%s'.",
+ dp->ifp->name);
+ if (!link_is_set)
+ memcpy(&dp->link.sin6_addr,
+ &dp->ifp->v6addresses[i],
+ sizeof(dp->link.sin6_addr));
+
+ /* Set interface-id. */
+ if (dp->id == -1)
+ dp->id = dp->ifp->index;
+ }
+
+ for (up = upstreams; up; up = up->next) {
+ up->link.sin6_port = local_port;
+ up->link.sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+ up->link.sin6_len = sizeof(up->link);
+#endif
+
+ if (up->ifp->v6address_count == 0)
+ log_fatal("Interface '%s' has no IPv6 addresses.",
+ up->ifp->name);
+ }
+}
+
+/*
+ * Add DHCPv6 agent options here.
+ */
+static const int required_forw_opts[] = {
+ D6O_INTERFACE_ID,
+ D6O_RELAY_MSG,
+ 0
+};
+
+/*
+ * Process a packet upwards, i.e., from client to server.
+ */
+static void
+process_up6(struct packet *packet, struct stream_list *dp) {
+ char forw_data[65535];
+ unsigned cursor;
+ struct dhcpv6_relay_packet *relay;
+ struct option_state *opts;
+ struct stream_list *up;
+
+ /* Check if the message should be relayed to the server. */
+ switch (packet->dhcpv6_msg_type) {
+ case DHCPV6_SOLICIT:
+ case DHCPV6_REQUEST:
+ case DHCPV6_CONFIRM:
+ case DHCPV6_RENEW:
+ case DHCPV6_REBIND:
+ case DHCPV6_RELEASE:
+ case DHCPV6_DECLINE:
+ case DHCPV6_INFORMATION_REQUEST:
+ case DHCPV6_RELAY_FORW:
+ case DHCPV6_LEASEQUERY:
+ log_info("Relaying %s from %s port %d going up.",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ ntohs(packet->client_port));
+ break;
+
+ case DHCPV6_ADVERTISE:
+ case DHCPV6_REPLY:
+ case DHCPV6_RECONFIGURE:
+ case DHCPV6_RELAY_REPL:
+ case DHCPV6_LEASEQUERY_REPLY:
+ log_info("Discarding %s from %s port %d going up.",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ ntohs(packet->client_port));
+ return;
+
+ default:
+ log_info("Unknown %d type from %s port %d going up.",
+ packet->dhcpv6_msg_type,
+ piaddr(packet->client_addr),
+ ntohs(packet->client_port));
+ return;
+ }
+
+ /* Build the relay-forward header. */
+ relay = (struct dhcpv6_relay_packet *) forw_data;
+ cursor = sizeof(*relay);
+ relay->msg_type = DHCPV6_RELAY_FORW;
+ if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
+ if (packet->dhcpv6_hop_count >= max_hop_count) {
+ log_info("Hop count exceeded,");
+ return;
+ }
+ relay->hop_count = packet->dhcpv6_hop_count + 1;
+ if (dp) {
+ memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
+ } else {
+ /* On smart relay add: && !global. */
+ if (!use_if_id && downstreams->next) {
+ log_info("Shan't get back the interface.");
+ return;
+ }
+ memset(&relay->link_address, 0, 16);
+ }
+ } else {
+ relay->hop_count = 0;
+ if (!dp)
+ return;
+ memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
+ }
+ memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
+
+ /* Get an option state. */
+ opts = NULL;
+ if (!option_state_allocate(&opts, MDL)) {
+ log_fatal("No memory for upwards options.");
+ }
+
+ /* Add an interface-id (if used). */
+ if (use_if_id) {
+ int if_id;
+
+ if (dp) {
+ if_id = dp->id;
+ } if (!downstreams->next) {
+ if_id = downstreams->id;
+ } else {
+ log_info("Don't know the interface.");
+ option_state_dereference(&opts, MDL);
+ return;
+ }
+
+ if (!save_option_buffer(&dhcpv6_universe, opts,
+ NULL, (unsigned char *) &if_id,
+ sizeof(int),
+ D6O_INTERFACE_ID, 0)) {
+ log_error("Can't save interface-id.");
+ option_state_dereference(&opts, MDL);
+ return;
+ }
+ }
+
+ /* Add the relay-msg carrying the packet. */
+ if (!save_option_buffer(&dhcpv6_universe, opts,
+ NULL, (unsigned char *) packet->raw,
+ packet->packet_length,
+ D6O_RELAY_MSG, 0)) {
+ log_error("Can't save relay-msg.");
+ option_state_dereference(&opts, MDL);
+ return;
+ }
+
+ /* Finish the relay-forward message. */
+ cursor += store_options6(forw_data + cursor,
+ sizeof(forw_data) - cursor,
+ opts, packet,
+ required_forw_opts, NULL);
+ option_state_dereference(&opts, MDL);
+
+ /* Send it to all upstreams. */
+ for (up = upstreams; up; up = up->next) {
+ send_packet6(up->ifp, (unsigned char *) forw_data,
+ (size_t) cursor, &up->link);
+ }
+}
+
+/*
+ * Process a packet downwards, i.e., from server to client.
+ */
+static void
+process_down6(struct packet *packet) {
+ struct stream_list *dp;
+ struct option_cache *oc;
+ struct data_string relay_msg;
+ const struct dhcpv6_packet *msg;
+ struct data_string if_id;
+ struct sockaddr_in6 to;
+ struct iaddr peer;
+
+ /* The packet must be a relay-reply message. */
+ if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
+ if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
+ log_info("Discarding %s from %s port %d going down.",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ ntohs(packet->client_port));
+ else
+ log_info("Unknown %d type from %s port %d going down.",
+ packet->dhcpv6_msg_type,
+ piaddr(packet->client_addr),
+ ntohs(packet->client_port));
+ return;
+ }
+
+ /* Inits. */
+ memset(&relay_msg, 0, sizeof(relay_msg));
+ memset(&if_id, 0, sizeof(if_id));
+ memset(&to, 0, sizeof(to));
+ to.sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+ to.sin6_len = sizeof(to);
+#endif
+ to.sin6_port = remote_port;
+ peer.len = 16;
+
+ /* Get the relay-msg option (carrying the message to relay). */
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
+ if (oc == NULL) {
+ log_info("No relay-msg.");
+ return;
+ }
+ if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL) ||
+ (relay_msg.len < sizeof(struct dhcpv6_packet))) {
+ log_error("Can't evaluate relay-msg.");
+ return;
+ }
+ msg = (const struct dhcpv6_packet *) relay_msg.data;
+
+ /* Get the interface-id (if exists) and the downstream. */
+ oc = lookup_option(&dhcpv6_universe, packet->options,
+ D6O_INTERFACE_ID);
+ if (oc != NULL) {
+ int if_index;
+
+ if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL) ||
+ (if_id.len != sizeof(int))) {
+ log_info("Can't evaluate interface-id.");
+ goto cleanup;
+ }
+ memcpy(&if_index, if_id.data, sizeof(int));
+ for (dp = downstreams; dp; dp = dp->next) {
+ if (dp->id == if_index)
+ break;
+ }
+ } else {
+ if (use_if_id) {
+ /* Require an interface-id. */
+ log_info("No interface-id.");
+ goto cleanup;
+ }
+ for (dp = downstreams; dp; dp = dp->next) {
+ /* Get the first matching one. */
+ if (!memcmp(&dp->link.sin6_addr,
+ &packet->dhcpv6_link_address,
+ sizeof(struct in6_addr)))
+ break;
+ }
+ }
+ /* Why bother when there is no choice. */
+ if (!dp && !downstreams->next)
+ dp = downstreams;
+ if (!dp) {
+ log_info("Can't find the down interface.");
+ goto cleanup;
+ }
+ memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
+ to.sin6_addr = packet->dhcpv6_peer_address;
+
+ /* Check if we should relay the carried message. */
+ switch (msg->msg_type) {
+ /* Relay-Reply of for another relay, not a client. */
+ case DHCPV6_RELAY_REPL:
+ to.sin6_port = local_port;
+ /* Fall into: */
+
+ case DHCPV6_ADVERTISE:
+ case DHCPV6_REPLY:
+ case DHCPV6_RECONFIGURE:
+ case DHCPV6_RELAY_FORW:
+ case DHCPV6_LEASEQUERY_REPLY:
+ log_info("Relaying %s to %s port %d down.",
+ dhcpv6_type_names[msg->msg_type],
+ piaddr(peer),
+ ntohs(to.sin6_port));
+ break;
+
+ case DHCPV6_SOLICIT:
+ case DHCPV6_REQUEST:
+ case DHCPV6_CONFIRM:
+ case DHCPV6_RENEW:
+ case DHCPV6_REBIND:
+ case DHCPV6_RELEASE:
+ case DHCPV6_DECLINE:
+ case DHCPV6_INFORMATION_REQUEST:
+ case DHCPV6_LEASEQUERY:
+ log_info("Discarding %s to %s port %d down.",
+ dhcpv6_type_names[msg->msg_type],
+ piaddr(peer),
+ ntohs(to.sin6_port));
+ goto cleanup;
+
+ default:
+ log_info("Unknown %d type to %s port %d down.",
+ msg->msg_type,
+ piaddr(peer),
+ ntohs(to.sin6_port));
+ goto cleanup;
+ }
+
+ /* Send the message to the downstream. */
+ send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
+ (size_t) relay_msg.len, &to);
+
+ cleanup:
+ if (relay_msg.data != NULL)
+ data_string_forget(&relay_msg, MDL);
+ if (if_id.data != NULL)
+ data_string_forget(&if_id, MDL);
+}
+
+/*
+ * Called by the dispatch packet handler with a decoded packet.
+ */
+void
+dhcpv6(struct packet *packet) {
+ struct stream_list *dp;
+
+ /* Try all relay-replies downwards. */
+ if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
+ process_down6(packet);
+ return;
+ }
+ /* Others are candidates to go up if they come from down. */
+ for (dp = downstreams; dp; dp = dp->next) {
+ if (packet->interface != dp->ifp)
+ continue;
+ process_up6(packet, dp);
+ return;
+ }
+ /* Relay-forward could work from an unknown interface. */
+ if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
+ process_up6(packet, NULL);
+ return;
}
- return length;
+ log_info("Can't process packet from interface '%s'.",
+ packet->interface->name);
+}
+#endif
+
+/* Stub routines needed for linking with DHCP libraries. */
+void
+bootp(struct packet *packet) {
+ return;
+}
+
+void
+dhcp(struct packet *packet) {
+ return;
+}
+
+void
+classify(struct packet *p, struct class *c) {
+ return;
+}
+
+int
+check_collection(struct packet *p, struct lease *l, struct collection *c) {
+ return 0;
+}
+
+isc_result_t
+find_class(struct class **class, const char *c1, const char *c2, int i) {
+ return ISC_R_NOTFOUND;
+}
+
+int
+parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
+ return 0;
+}
+
+isc_result_t
+dhcp_set_control_state(control_object_state_t oldstate,
+ control_object_state_t newstate) {
+ return ISC_R_SUCCESS;
}