summaryrefslogtreecommitdiff
path: root/relay
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
parentffbaa8801ec3e4806ac70acbeaaa672b0024fe22 (diff)
downloadisc-dhcp-7de20a9518b7f6073c83702b34fe9d7fcb2abfb6.tar.gz
- Merge dhcrelay6 into dhcrelay
- Prep for 4.1.0a2 release
Diffstat (limited to 'relay')
-rw-r--r--relay/Makefile.am2
-rw-r--r--relay/Makefile.in4
-rw-r--r--relay/dhcrelay.8329
-rw-r--r--relay/dhcrelay.c1317
4 files changed, 1164 insertions, 488 deletions
diff --git a/relay/Makefile.am b/relay/Makefile.am
index 2c226cab..ab95e84f 100644
--- a/relay/Makefile.am
+++ b/relay/Makefile.am
@@ -2,7 +2,7 @@ AM_CPPFLAGS = -DLOCALSTATEDIR='"@localstatedir@"'
sbin_PROGRAMS = dhcrelay
dhcrelay_SOURCES = dhcrelay.c
-dhcrelay_LDADD = ../common/libdhcp.a ../omapip/libomapi.a ../dst/libdst.a
+dhcrelay_LDADD = ../common/libdhcp.a ../omapip/libomapi.a ../dst/libdst.a ../minires/libres.a
man_MANS = dhcrelay.8
EXTRA_DIST = $(man_MANS)
diff --git a/relay/Makefile.in b/relay/Makefile.in
index cff9f70e..2c49a5df 100644
--- a/relay/Makefile.in
+++ b/relay/Makefile.in
@@ -50,7 +50,7 @@ PROGRAMS = $(sbin_PROGRAMS)
am_dhcrelay_OBJECTS = dhcrelay.$(OBJEXT)
dhcrelay_OBJECTS = $(am_dhcrelay_OBJECTS)
dhcrelay_DEPENDENCIES = ../common/libdhcp.a ../omapip/libomapi.a \
- ../dst/libdst.a
+ ../dst/libdst.a ../minires/libres.a
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/includes
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@@ -143,7 +143,7 @@ sysconfdir = @sysconfdir@
target_alias = @target_alias@
AM_CPPFLAGS = -DLOCALSTATEDIR='"@localstatedir@"'
dhcrelay_SOURCES = dhcrelay.c
-dhcrelay_LDADD = ../common/libdhcp.a ../omapip/libomapi.a ../dst/libdst.a
+dhcrelay_LDADD = ../common/libdhcp.a ../omapip/libomapi.a ../dst/libdst.a ../minires/libres.a
man_MANS = dhcrelay.8
EXTRA_DIST = $(man_MANS)
all: all-am
diff --git a/relay/dhcrelay.8 b/relay/dhcrelay.8
index 85f19fce..783e32d4 100644
--- a/relay/dhcrelay.8
+++ b/relay/dhcrelay.8
@@ -27,7 +27,7 @@
.\" see ``http://www.isc.org/isc''. To learn more about Vixie
.\" Enterprises, see ``http://www.vix.com''.
.\"
-.\" $Id: dhcrelay.8,v 1.14 2007/05/19 19:16:26 dhankins Exp $
+.\" $Id: dhcrelay.8,v 1.15 2008/06/13 00:55:53 each Exp $
.\"
.TH dhcrelay 8
.SH NAME
@@ -35,26 +35,14 @@ dhcrelay - Dynamic Host Configuration Protocol Relay Agent
.SH SYNOPSIS
.B dhcrelay
[
-.B -p
-.I port
+.B -4
]
[
-.B -d
+.B -dqaD
]
[
-.B -q
-]
-[
-.B -i
-.I if0
-[
-.B ...
-.B -i
-.I ifN
-]
-]
-[
-.B -a
+.B -p
+.I port
]
[
.B -c
@@ -65,9 +53,6 @@ dhcrelay - Dynamic Host Configuration Protocol Relay Agent
.I length
]
[
-.B -D
-]
-[
.B -m
.I append
|
@@ -77,169 +62,171 @@ dhcrelay - Dynamic Host Configuration Protocol Relay Agent
|
.I discard
]
+[
+.B -i
+.I interface0
+[
+.B ...
+.B -i
+.I interfaceN
+]
+]
.I server0
[
.I ...serverN
]
+.PP
+.B dhcrelay -6
+[
+.B -dqI
+]
+[
+.B -p
+.I port
+]
+[
+.B -c
+.I count
+]
+.B -l
+.I lower0
+[
+.B ...
+.B -l
+.I lowerN
+]
+.B -u
+.I upper0
+[
+.B ...
+.B -u
+.I upperN
+]
.SH DESCRIPTION
The Internet Systems Consortium DHCP Relay Agent, dhcrelay, provides a
means for relaying DHCP and BOOTP requests from a subnet to which
-no DHCP server is directly connected to one or more DHCP servers on other
-subnets.
+no DHCP server is directly connected to one or more DHCP servers on
+other subnets. It supports both DHCPv4/BOOTP and DHCPv6 protocols.
.SH OPERATION
.PP
-The DHCP Relay Agent listens for DHCP and BOOTP queries and responses.
-When a query is received from a client, dhcrelay forwards it to the
-list of DHCP servers specified on the command line. When a reply is
-received from a server, it is broadcast or unicast (according to the
-relay agent's ability or the client's request) on the network from
-which the original request came.
+The DHCP Relay Agent listens for DHCPv4 or DHCPv6 queries from clients or
+other relay agents on one or more interfaces, passing them along to
+``upstream'' servers or relay agents as specified on the command line.
+When a reply is received from upstream, it is multicast or unicast back
+downstream to the source of the original request.
.SH COMMAND LINE
.PP
-The names of the network interfaces that dhcrelay should attempt to
-configure may be specified on the command line using the
-.B -i
-option. If no interface names
-are specified on the command line dhcrelay will identify all network
-interfaces, elimininating non-broadcast interfaces if possible, and
-attempt to configure each interface.
-.PP
-The
-.B -i
-flag can be used to specify the network interfaces on which the relay
-agent should listen. In general, it must listen not only on those
-network interfaces to which clients are attached, but also on those
-network interfaces to which the server (or the router that reaches the
-server) is attached. However, in some cases it may be necessary to
-exclude some networks; in this case, you must list all those network
-interfaces that should \fInot\fR be excluded using the \fB-i\fR flag.
-.PP
-In some cases it
-.I is
-helpful for the relay agent to forward requests from networks on which
-a DHCP server is running to other DHCP servers. This would be the
-case if two DHCP servers on different networks were being used to
-provide backup service for each other's networks.
-.PP
-If dhcrelay should listen and transmit on a port other than the
-standard (port 67), the
-.B -p
-flag may used. It should be followed by the udp port number that
-dhcrelay should use. This is mostly useful for debugging purposes.
-.PP
-Dhcrelay will normally run in the foreground until it has configured
-an interface, and then will revert to running in the background.
-To force dhcrelay to always run as a foreground process, the
-.B -d
-flag should be specified. This is useful when running dhcrelay under
-a debugger, or when running it out of inittab on System V systems.
-.PP
-Dhcrelay will normally print its network configuration on startup.
-This can be unhelpful in a system startup script - to disable this
-behaviour, specify the
-.B -q
-flag.
-.SH RELAY AGENT INFORMATION OPTIONS
-If the
-.B -a
-flag is set the relay agent will append an agent option field to each
-request before forwarding it to the server. Agent option fields in
-responses sent from servers to clients will be stripped before
-forwarding such responses back to the client.
-.PP
-The agent option field will contain two agent options: the Circuit ID
-suboption and the Remote ID suboption. Currently, the Circuit ID will
-be the printable name of the interface on which the client request was
-received. The client supports inclusion of a Remote ID suboption as
-well, but this is not used by default.
-.PP
-When forwarding packets, dhcrelay discards packets which have reached a hop
-count of 10. If a lower or higher threshold (up to 255) is desired, depending
-on your environment, you can specify the max hop count threshold as a number
-following the
-.B -c
+\fIProtocol selection options:\fR
+.TP
+-6
+Run dhcrelay as a DHCPv6 relay agent. Incompatible with the \fB-4\fR
option.
-.PP
-Relay Agent options are added to a DHCP packet without the knowledge
-of the DHCP client. The client may have filled the DHCP packet
-option buffer completely, in which case there theoretically isn't any
-space to add Agent options. However, the DHCP server may be able to
-handle a much larger packet than most DHCP clients would send. The
-current Agent Options draft requires that the relay agent use a
-maximum packet size of 576 bytes.
-.PP
-It is recommended that with the Internet Systems Consortium DHCP
-server, the maximum packet size be set to about 1400, allowing plenty
-of extra space in which the relay agent can put the agent option
-field, while still fitting into the Ethernet MTU size. This can be
-done by specifying the
-.B -A
-flag, followed by the desired maximum packet size (e.g., 1400).
-.PP
-Note that this is reasonably safe to do even if the MTU between the
-server and the client is less than 1500, as long as the hosts on which
-the server and client are running support IP fragmentation (and they
-should). With some knowledge as to how large the agent options might
-get in a particular configuration, this parameter can be tuned as
-finely as necessary.
-.PP
-It is possible for a relay agent to receive a packet which already
-contains an agent option field. If this packet does not have a giaddr
-set, the standard requires that the packet be discarded.
-.PP
-If giaddr is set, the server may handle the situation in one of four
-ways: it may
-.I append
-its own set of relay options to the packet, leaving the
-supplied option field intact. It may
-.I replace
-the existing agent option field.
-It may
-.I forward
-the packet unchanged. Or, it may
-.I discard
-it.
-.PP
-Which of these behaviours is followed by the Internet Systems
-Consortium DHCP Relay Agent may be configured with the
-.B -m
-flag, followed by one of the four keywords specified in
-.I italics
-above.
-.PP
-When the relay agent receives a reply from a server that it's supposed
-to forward to a client, and Relay Agent Information option processing
-is enabled, the relay agent scans the packet for Relay Agent
-Information options and removes them. As it's scanning, if it finds
-a Relay Agent Information option field containing an Agent ID
-suboption that matches one of its IP addresses, that option is
-recognized as its own. If no such option is found, the relay agent
-can either drop the packet, or relay it anyway. If the
-.B -D
-option is specified, all packets that don't contain a match will be
-dropped.
-.SH SPECIFYING DHCP SERVERS
-The name or IP address of at least one DHCP server to which DHCP and
-BOOTP requests should be relayed must be specified on the command
-line.
+.TP
+-4
+Run dhcrelay as a DHCPv4/BOOTP relay agent. This is the default mode of
+operation, so the argument is not necessary, but may be specified for
+clarity. Incompatible with \fB-6\fR.
+.PP
+\fISpecifying DHCPv4/BOOTP servers\fR
+.PP
+In DHCPv4 mode, a list of one or more server addresses must be specified on
+the command line, to which DHCP/BOOTP queries should be relayed.
+.PP
+\fIOptions available for both DHCPv4 and DHCPv6:\fR
+.TP
+-c COUNT
+Maximum hop count. When forwarding packets, dhcrelay discards packets
+which have reached a hop count of COUNT. Default is 10. Maximum is 255.
+.TP
+-d
+Force dhcrelay to run as a foreground process. Useful when running
+dhcrelay under a debugger, or running out of inittab on System V systems.
+.TP
+-p PORT
+Listen and transmit on port PORT. This is mostly useful for debugging
+purposes. Default is port 67 for DHCPv4/BOOTP, or port 547 for DHCPv6.
+.TP
+-q
+Quiet mode. Prevents dhcrelay6 from printing its network configuration
+on startup.
+.PP
+\fIOptions available in DHCPv4 mode only:\fR
+.TP
+-a
+Append an agent option field to each request before forwarding it to
+the server. Agent option fields in responses sent from servers to
+clients will be stripped before forwarding such responses back to the
+client. The agent option field will contain two agent options: the Circuit
+ID suboption and the Remote ID suboption. Currently, the Circuit ID will
+be the printable name of the interface on which the client request was
+received. The client supports inclusion of a Remote ID suboption as well,
+but this is not used by default.
+.TP
+-A LENGTH
+Specify the maximum packet size to send to a DHCPv4/BOOTP server. This
+might be done to allow sufficient space for addition of relay agent
+options while still fitting into the Ethernet MTU size.
+.TP
+-D
+Drop packets from upstream servers if they contain Relay Agent
+Information options that indicate they were generated in response to
+a query that came via a different relay agent. If this option is not
+specified, such packets will be relayed anyway.
+.TP
+-i \fIifname\fR
+Listen for DHCPv4/BOOTP queries on interface \fIifname\fR. Multiple
+interfaces may be specified by using more than one \fB-i\fR option. If
+no interfaces are specified on the command line, dhcrelay will identify
+all network interfaces, eliminating non-broadcast interfaces if possible,
+and attempt to listen on all of them.
+.TP
+-m \fIappend\fR|\fIreplace\fR|\fIforward\fR|\fIdiscard\fR
+Control the handling of incoming DHCPv4 packets which already contain
+relay agent options. If such a packet does not have \fIgiaddr\fR set in
+its header, the DHCP standard requires that the packet be discarded.
+However, if \fIgiaddr\fR is set, the relay agent may handle the situation
+in four ways: It may \fIappend\fR its own set of relay options to the
+packet, leaving the supplied option field intact; it may \fIreplace\fR the
+existing agent option field; it may \fIforward\fR the packet unchanged; or,
+it may \fIdiscard\fR it.
+.PP
+\fIOptions available in DHCPv6 mode only:\fR
+.TP
+-I
+Force use of the DHCPv6 Interface-ID option. This option is
+automatically sent when there are two or more downstream interfaces
+in use, to disambiguate between them. The \fB-I\fR option causes
+dhcrelay to send the option even if there is only one downstream
+interface.
+.TP
+-l [\fIaddress%\fR]\fIifname\fR[\fI#index\fR]
+Specifies the ``lower'' network interface for DHCPv6 relay mode: the
+interface on which queries will be received from clients or from other
+relay agents. At least one \fB-l\fR option must be included in the command
+line when running in DHCPv6 mode. The interface name \fIifname\fR is a
+mandatory parameter. The link address can be specified by \fIaddress%\fR;
+if it isn't, dhcrelay will use the first non-link-local address configured
+on the interface. The optional \fI#index\fR parameter specifies the
+interface index.
+.TP
+-u [\fIaddress%\fR]\fIifname\fR
+Specifies the ``upper'' network interface for DHCPv6 relay mode: the
+interface to which queries from clients and other relay agents should be
+forwarded. At least one \fB-u\fR option must be included in the command
+line when running in DHCPv6 mode. The interface name \fIifname\fR is a
+mandatory parameter. The destination unicast or multicast address can be
+specified by \fIaddress%\fR; if not specified, the relay agent will forward
+to the DHCPv6 \fIAll_DHCP_Relay_Agents_and_Servers\fR multicast address.
+.PP
+It is possible to specify the same interface with different addresses
+more than once, and even, when the system supports it, to use the same
+interface as both upper and lower interfaces.
.SH SEE ALSO
-dhclient(8), dhcpd(8), RFC2132, RFC2131, draft-ietf-dhc-agent-options-03.txt.
+dhclient(8), dhcpd(8), RFC3315, RFC2132, RFC2131.
.SH BUGS
-It should be possible for the user to define the Circuit ID and Remote
-ID values on a per-interface basis.
.PP
-The relay agent should not relay packets received on a physical
-network to DHCP servers on the same physical network - if they do, the
-server will receive duplicate packets. In order to fix this,
-however, the relay agent needs to be able to learn about the network
-topology, which requires that it have a configuration file.
-.SH AUTHOR
-.B dhcrelay(8)
-has been written for Internet Systems Consortium
-by Ted Lemon in cooperation with Vixie
-Enterprises. To learn more about Internet Systems Consortium,
-see
-.B http://www.isc.org/isc.
-To learn more about Vixie
-Enterprises, see
-.B http://www.vix.com.
+Using the same interface on both upper and lower sides may cause
+loops, so when running this way, the maximum hop count should be set
+to a low value.
+.PP
+The loopback interface is not (yet) recognized as a valid interface.
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;
}