3
DHCP/BOOTP Relay Agent. */
6
* Copyright(c) 2004-2008 by Internet Systems Consortium, Inc.("ISC")
7
* Copyright(c) 1997-2003 by Internet Software Consortium
9
* Permission to use, copy, modify, and distribute this software for any
10
* purpose with or without fee is hereby granted, provided that the above
11
* copyright notice and this permission notice appear in all copies.
13
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21
* Internet Systems Consortium, Inc.
23
* Redwood City, CA 94063
27
* This software has been written for Internet Systems Consortium
28
* by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29
* To learn more about Internet Systems Consortium, see
30
* ``http://www.isc.org/''. To learn more about Vixie Enterprises,
31
* see ``http://www.vix.com''. To learn more about Nominum, Inc., see
32
* ``http://www.nominum.com''.
39
TIME default_lease_time = 43200; /* 12 hours... */
40
TIME max_lease_time = 86400; /* 24 hours... */
41
struct tree_cache *global_options[256];
43
struct option *requested_opts[2];
45
/* Needed to prevent linking against conflex.c. */
51
const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID;
53
int bogus_agent_drops = 0; /* Packets dropped because agent option
54
field was specified and we're not relaying
55
packets that already have an agent option
57
int bogus_giaddr_drops = 0; /* Packets sent to us to relay back to a
58
client, but with a bogus giaddr. */
59
int client_packets_relayed = 0; /* Packets relayed from client to server. */
60
int server_packet_errors = 0; /* Errors sending packets to servers. */
61
int server_packets_relayed = 0; /* Packets relayed from server to client. */
62
int client_packet_errors = 0; /* Errors sending packets to clients. */
64
int add_agent_options = 0; /* If nonzero, add relay agent options. */
66
int agent_option_errors = 0; /* Number of packets forwarded without
67
agent options because there was no room. */
68
int drop_agent_mismatches = 0; /* If nonzero, drop server replies that
69
don't have matching circuit-id's. */
70
int corrupt_agent_options = 0; /* Number of packets dropped because
71
relay agent information option was bad. */
72
int missing_agent_option = 0; /* Number of packets dropped because no
73
RAI option matching our ID was found. */
74
int bad_circuit_id = 0; /* Circuit ID option in matching RAI option
75
did not match any known circuit ID. */
76
int missing_circuit_id = 0; /* Circuit ID option in matching RAI option
78
int max_hop_count = 10; /* Maximum hop count */
81
/* Force use of DHCPv6 interface-id option. */
82
isc_boolean_t use_if_id = ISC_FALSE;
85
/* Maximum size of a packet with agent options added. */
86
int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
88
/* What to do about packets we're asked to relay that
89
already have a relay option: */
90
enum { forward_and_append, /* Forward and append our own relay option. */
91
forward_and_replace, /* Forward, but replace theirs with ours. */
92
forward_untouched, /* Forward without changes. */
93
discard } agent_relay_mode = forward_and_replace;
96
u_int16_t remote_port;
98
/* Relay agent server list. */
100
struct server_list *next;
101
struct sockaddr_in to;
106
struct stream_list *next;
107
struct interface_info *ifp;
108
struct sockaddr_in6 link;
110
} *downstreams, *upstreams;
112
static struct stream_list *parse_downstream(char *);
113
static struct stream_list *parse_upstream(char *);
114
static void setup_streams(void);
117
static void do_relay4(struct interface_info *, struct dhcp_packet *,
118
unsigned int, unsigned int, struct iaddr,
120
static int add_relay_agent_options(struct interface_info *,
121
struct dhcp_packet *, unsigned,
123
static int find_interface_by_agent_option(struct dhcp_packet *,
124
struct interface_info **, u_int8_t *, int);
125
static int strip_relay_agent_options(struct interface_info *,
126
struct interface_info **,
127
struct dhcp_packet *, unsigned);
129
static char copyright[] = "Copyright 2004-2008 Internet Systems Consortium.";
130
static char arr[] = "All rights reserved.";
131
static char message[] = "Internet Systems Consortium DHCP Relay Agent";
132
static char url[] = "For info, please visit http://www.isc.org/sw/dhcp/";
135
#define DHCRELAY_USAGE \
136
"Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]\n"\
137
" [-A <length>] [-c <hops>] [-p <port>]\n" \
138
" [-m append|replace|forward|discard]\n" \
139
" [-i interface0 [ ... -i interfaceN]\n" \
140
" server0 [ ... serverN]\n\n" \
141
" dhcrelay -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
142
" -l lower0 [ ... -l lowerN]\n" \
143
" -u upper0 [ ... -u upperN]\n" \
144
" lower (client link): [address%%]interface[#index]\n" \
145
" upper (server link): [address%%]interface"
147
#define DHCRELAY_USAGE \
148
"Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
149
" [-m append|replace|forward|discard]\n" \
150
" [-i interface0 [ ... -i interfaceN]\n" \
151
" server0 [ ... serverN]\n\n"
154
static void usage() {
155
log_fatal(DHCRELAY_USAGE);
159
main(int argc, char **argv) {
162
struct server_list *sp = NULL;
163
struct interface_info *tmp = NULL;
164
char *service_local, *service_remote;
165
u_int16_t port_local, port_remote;
166
int no_daemon = 0, quiet = 0;
170
struct stream_list *sl = NULL;
171
int local_family_set = 0;
174
/* Make sure that file descriptors 0(stdin), 1,(stdout), and
175
2(stderr) are open. To do this, we assume that when we
176
open a file the lowest available file descriptor is used. */
177
fd = open("/dev/null", O_RDWR);
179
fd = open("/dev/null", O_RDWR);
181
fd = open("/dev/null", O_RDWR);
183
log_perror = 0; /* No sense logging to /dev/null. */
187
openlog("dhcrelay", LOG_NDELAY, LOG_DAEMON);
190
setlogmask(LOG_UPTO(LOG_INFO));
193
/* Set up the OMAPI. */
194
status = omapi_init();
195
if (status != ISC_R_SUCCESS)
196
log_fatal("Can't initialize OMAPI: %s",
197
isc_result_totext(status));
199
/* Set up the OMAPI wrappers for the interface object. */
202
for (i = 1; i < argc; i++) {
203
if (!strcmp(argv[i], "-4")) {
205
if (local_family_set && (local_family == AF_INET6)) {
208
local_family_set = 1;
209
local_family = AF_INET;
210
} else if (!strcmp(argv[i], "-6")) {
211
if (local_family_set && (local_family == AF_INET)) {
214
local_family_set = 1;
215
local_family = AF_INET6;
217
} else if (!strcmp(argv[i], "-d")) {
219
} else if (!strcmp(argv[i], "-q")) {
221
quiet_interface_discovery = 1;
222
} else if (!strcmp(argv[i], "-p")) {
225
local_port = htons(atoi (argv[i]));
226
log_debug("binding to user-specified port %d",
228
} else if (!strcmp(argv[i], "-c")) {
232
hcount = atoi(argv[i]);
234
max_hop_count= hcount;
237
} else if (!strcmp(argv[i], "-i")) {
239
if (local_family_set && (local_family == AF_INET6)) {
242
local_family_set = 1;
243
local_family = AF_INET;
245
status = interface_allocate(&tmp, MDL);
246
if (status != ISC_R_SUCCESS)
247
log_fatal("%s: interface_allocate: %s",
249
isc_result_totext(status));
253
strcpy(tmp->name, argv[i]);
254
interface_snorf(tmp, INTERFACE_REQUESTED);
255
interface_dereference(&tmp, MDL);
256
} else if (!strcmp(argv[i], "-a")) {
258
if (local_family_set && (local_family == AF_INET6)) {
261
local_family_set = 1;
262
local_family = AF_INET;
264
add_agent_options = 1;
265
} else if (!strcmp(argv[i], "-A")) {
267
if (local_family_set && (local_family == AF_INET6)) {
270
local_family_set = 1;
271
local_family = AF_INET;
276
dhcp_max_agent_option_packet_length = atoi(argv[i]);
278
if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
279
log_fatal("%s: packet length exceeds "
280
"longest possible MTU\n",
282
} else if (!strcmp(argv[i], "-m")) {
284
if (local_family_set && (local_family == AF_INET6)) {
287
local_family_set = 1;
288
local_family = AF_INET;
292
if (!strcasecmp(argv[i], "append")) {
293
agent_relay_mode = forward_and_append;
294
} else if (!strcasecmp(argv[i], "replace")) {
295
agent_relay_mode = forward_and_replace;
296
} else if (!strcasecmp(argv[i], "forward")) {
297
agent_relay_mode = forward_untouched;
298
} else if (!strcasecmp(argv[i], "discard")) {
299
agent_relay_mode = discard;
302
} else if (!strcmp(argv[i], "-D")) {
304
if (local_family_set && (local_family == AF_INET6)) {
307
local_family_set = 1;
308
local_family = AF_INET;
310
drop_agent_mismatches = 1;
312
} else if (!strcmp(argv[i], "-I")) {
313
if (local_family_set && (local_family == AF_INET)) {
316
local_family_set = 1;
317
local_family = AF_INET6;
318
use_if_id = ISC_TRUE;
319
} else if (!strcmp(argv[i], "-l")) {
320
if (local_family_set && (local_family == AF_INET)) {
323
local_family_set = 1;
324
local_family = AF_INET6;
325
if (downstreams != NULL)
326
use_if_id = ISC_TRUE;
329
sl = parse_downstream(argv[i]);
330
sl->next = downstreams;
332
} else if (!strcmp(argv[i], "-u")) {
333
if (local_family_set && (local_family == AF_INET)) {
336
local_family_set = 1;
337
local_family = AF_INET6;
340
sl = parse_upstream(argv[i]);
341
sl->next = upstreams;
344
} else if (!strcmp(argv[i], "--version")) {
345
log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
347
} else if (!strcmp(argv[i], "--help") ||
348
!strcmp(argv[i], "-h")) {
349
log_info(DHCRELAY_USAGE);
351
} else if (argv[i][0] == '-') {
355
struct in_addr ia, *iap = NULL;
358
if (local_family_set && (local_family == AF_INET6)) {
361
local_family_set = 1;
362
local_family = AF_INET;
364
if (inet_aton(argv[i], &ia)) {
367
he = gethostbyname(argv[i]);
369
log_error("%s: host unknown", argv[i]);
371
iap = ((struct in_addr *)
377
sp = ((struct server_list *)
378
dmalloc(sizeof *sp, MDL));
380
log_fatal("no memory for server.\n");
383
memcpy(&sp->to.sin_addr, iap, sizeof *iap);
388
if (local_family == AF_INET) {
389
path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
390
if (path_dhcrelay_pid == NULL)
391
path_dhcrelay_pid = _PATH_DHCRELAY_PID;
395
path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
396
if (path_dhcrelay_pid == NULL)
397
path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
402
log_info("%s %s", message, PACKAGE_VERSION);
411
/* Set default port */
412
if (local_family == AF_INET) {
413
service_local = "bootps";
414
service_remote = "bootpc";
415
port_local = htons(67);
416
port_remote = htons(68);
420
service_local = "dhcpv6-server";
421
service_remote = "dhcpv6-client";
422
port_local = htons(547);
423
port_remote = htons(546);
428
ent = getservbyname(service_local, "udp");
430
local_port = ent->s_port;
432
local_port = port_local;
434
ent = getservbyname(service_remote, "udp");
436
remote_port = ent->s_port;
438
remote_port = port_remote;
443
if (local_family == AF_INET) {
444
/* We need at least one server */
445
if (servers == NULL) {
446
log_fatal("No servers specified.");
450
/* Set up the server sockaddrs. */
451
for (sp = servers; sp; sp = sp->next) {
452
sp->to.sin_port = local_port;
453
sp->to.sin_family = AF_INET;
455
sp->to.sin_len = sizeof sp->to;
463
/* We need at least one upstream and one downstream interface */
464
if (upstreams == NULL || downstreams == NULL) {
465
log_info("Must specify at least one lower "
466
"and one upper interface.\n");
470
/* Set up the initial dhcp option universe. */
471
initialize_common_option_spaces();
473
/* Check requested options. */
474
code = D6O_RELAY_MSG;
475
if (!option_code_hash_lookup(&requested_opts[0],
476
dhcpv6_universe.code_hash,
478
log_fatal("Unable to find the RELAY_MSG "
479
"option definition.");
480
code = D6O_INTERFACE_ID;
481
if (!option_code_hash_lookup(&requested_opts[1],
482
dhcpv6_universe.code_hash,
484
log_fatal("Unable to find the INTERFACE_ID "
485
"option definition.");
489
/* Get the current time... */
490
gettimeofday(&cur_tv, NULL);
492
/* Discover all the network interfaces. */
493
discover_interfaces(DISCOVER_RELAY);
496
if (local_family == AF_INET6)
500
/* Become a daemon... */
508
if ((pid = fork()) < 0)
509
log_fatal("Can't fork daemon: %m");
513
pfdesc = open(path_dhcrelay_pid,
514
O_CREAT | O_TRUNC | O_WRONLY, 0644);
517
log_error("Can't create %s: %m", path_dhcrelay_pid);
519
pf = fdopen(pfdesc, "w");
521
log_error("Can't fdopen %s: %m",
524
fprintf(pf, "%ld\n",(long)getpid());
537
/* Set up the packet handler... */
538
if (local_family == AF_INET)
539
bootp_packet_handler = do_relay4;
542
dhcpv6_packet_handler = do_packet6;
545
/* Start dispatching packets and timeouts... */
553
do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
554
unsigned int length, unsigned int from_port, struct iaddr from,
555
struct hardware *hfrom) {
556
struct server_list *sp;
557
struct sockaddr_in to;
558
struct interface_info *out;
559
struct hardware hto, *htop;
561
if (packet->hlen > sizeof packet->chaddr) {
562
log_info("Discarding packet with invalid hlen.");
566
/* Find the interface that corresponds to the giaddr
568
if (packet->giaddr.s_addr) {
569
for (out = interfaces; out; out = out->next) {
572
for (i = 0 ; i < out->address_count ; i++ ) {
573
if (out->addresses[i].s_addr ==
574
packet->giaddr.s_addr)
586
/* If it's a bootreply, forward it to the client. */
587
if (packet->op == BOOTREPLY) {
588
if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
589
can_unicast_without_arp(out)) {
590
to.sin_addr = packet->yiaddr;
591
to.sin_port = remote_port;
593
/* and hardware address is not broadcast */
596
to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
597
to.sin_port = remote_port;
599
/* hardware address is broadcast */
602
to.sin_family = AF_INET;
604
to.sin_len = sizeof to;
607
memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
608
hto.hbuf[0] = packet->htype;
609
hto.hlen = packet->hlen + 1;
611
/* Wipe out the agent relay options and, if possible, figure
612
out which interface to use based on the contents of the
613
option that we put on the request to which the server is
616
strip_relay_agent_options(ip, &out, packet, length)))
620
log_error("Packet to bogus giaddr %s.\n",
621
inet_ntoa(packet->giaddr));
622
++bogus_giaddr_drops;
626
if (send_packet(out, NULL, packet, length, out->addresses[0],
628
++server_packet_errors;
630
log_debug("Forwarded BOOTREPLY for %s to %s",
631
print_hw_addr(packet->htype, packet->hlen,
633
inet_ntoa(to.sin_addr));
635
++server_packets_relayed;
640
/* If giaddr matches one of our addresses, ignore the packet -
645
/* Add relay agent options if indicated. If something goes wrong,
647
if (!(length = add_relay_agent_options(ip, packet, length,
651
/* If giaddr is not already set, Set it so the server can
652
figure out what net it's from and so that we can later
653
forward the response to the correct net. If it's already
654
set, the response will be sent directly to the relay agent
655
that set giaddr, so we won't see it. */
656
if (!packet->giaddr.s_addr)
657
packet->giaddr = ip->addresses[0];
658
if (packet->hops < max_hop_count)
659
packet->hops = packet->hops + 1;
663
/* Otherwise, it's a BOOTREQUEST, so forward it to all the
665
for (sp = servers; sp; sp = sp->next) {
666
if (send_packet((fallback_interface
667
? fallback_interface : interfaces),
668
NULL, packet, length, ip->addresses[0],
669
&sp->to, NULL) < 0) {
670
++client_packet_errors;
672
log_debug("Forwarded BOOTREQUEST for %s to %s",
673
print_hw_addr(packet->htype, packet->hlen,
675
inet_ntoa(sp->to.sin_addr));
676
++client_packets_relayed;
682
/* Strip any Relay Agent Information options from the DHCP packet
683
option buffer. If there is a circuit ID suboption, look up the
684
outgoing interface based upon it. */
687
strip_relay_agent_options(struct interface_info *in,
688
struct interface_info **out,
689
struct dhcp_packet *packet,
692
u_int8_t *op, *nextop, *sp, *max;
693
int good_agent_option = 0;
696
/* If we're not adding agent options to packets, we're not taking
698
if (!add_agent_options)
701
/* If there's no cookie, it's a bootp packet, so we should just
702
forward it unchanged. */
703
if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
706
max = ((u_int8_t *)packet) + length;
707
sp = op = &packet->options[4];
711
/* Skip padding... */
719
/* If we see a message type, it's a DHCP packet. */
720
case DHO_DHCP_MESSAGE_TYPE:
725
/* Quit immediately if we hit an End option. */
731
case DHO_DHCP_AGENT_OPTIONS:
732
/* We shouldn't see a relay agent option in a
733
packet before we've seen the DHCP packet type,
734
but if we do, we have to leave it alone. */
738
/* Do not process an agent option if it exceeds the
739
* buffer. Fail this packet.
741
nextop = op + op[1] + 2;
745
status = find_interface_by_agent_option(packet,
748
if (status == -1 && drop_agent_mismatches)
751
good_agent_option = 1;
756
/* Skip over other options. */
758
/* Fail if processing this option will exceed the
759
* buffer(op[1] is malformed).
761
nextop = op + op[1] + 2;
766
memmove(sp, op, op[1] + 2);
777
/* If it's not a DHCP packet, we're not supposed to touch it. */
781
/* If none of the agent options we found matched, or if we didn't
782
find any agent options, count this packet as not having any
783
matching agent options, and if we're relying on agent options
784
to determine the outgoing interface, drop the packet. */
786
if (!good_agent_option) {
787
++missing_agent_option;
788
if (drop_agent_mismatches)
792
/* Adjust the length... */
794
length = sp -((u_int8_t *)packet);
796
/* Make sure the packet isn't short(this is unlikely,
798
if (length < BOOTP_MIN_LEN) {
799
memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
800
length = BOOTP_MIN_LEN;
807
/* Find an interface that matches the circuit ID specified in the
808
Relay Agent Information option. If one is found, store it through
809
the pointer given; otherwise, leave the existing pointer alone.
811
We actually deviate somewhat from the current specification here:
812
if the option buffer is corrupt, we suggest that the caller not
813
respond to this packet. If the circuit ID doesn't match any known
814
interface, we suggest that the caller to drop the packet. Only if
815
we find a circuit ID that matches an existing interface do we tell
816
the caller to go ahead and process the packet. */
819
find_interface_by_agent_option(struct dhcp_packet *packet,
820
struct interface_info **out,
821
u_int8_t *buf, int len) {
823
u_int8_t *circuit_id = 0;
824
unsigned circuit_id_len = 0;
825
struct interface_info *ip;
828
/* If the next agent option overflows the end of the
829
packet, the agent option buffer is corrupt. */
831
i + buf[i + 1] + 2 > len) {
832
++corrupt_agent_options;
836
/* Remember where the circuit ID is... */
838
circuit_id = &buf[i + 2];
839
circuit_id_len = buf[i + 1];
840
i += circuit_id_len + 2;
849
/* If there's no circuit ID, it's not really ours, tell the caller
852
++missing_circuit_id;
856
/* Scan the interface list looking for an interface whose
857
name matches the one specified in circuit_id. */
859
for (ip = interfaces; ip; ip = ip->next) {
860
if (ip->circuit_id &&
861
ip->circuit_id_len == circuit_id_len &&
862
!memcmp(ip->circuit_id, circuit_id, circuit_id_len))
866
/* If we got a match, use it. */
872
/* If we didn't get a match, the circuit ID was bogus. */
878
* Examine a packet to see if it's a candidate to have a Relay
879
* Agent Information option tacked onto its tail. If it is, tack
883
add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
884
unsigned length, struct in_addr giaddr) {
885
int is_dhcp = 0, mms;
887
u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
889
/* If we're not adding agent options to packets, we can skip
891
if (!add_agent_options)
894
/* If there's no cookie, it's a bootp packet, so we should just
895
forward it unchanged. */
896
if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
899
max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
901
/* Commence processing after the cookie. */
902
sp = op = &packet->options[4];
906
/* Skip padding... */
908
/* Remember the first pad byte so we can commandeer
911
* XXX: Is this really a good idea? Sure, we can
912
* seemingly reduce the packet while we're looking,
913
* but if the packet was signed by the client then
914
* this padding is part of the checksum(RFC3118),
915
* and its nonpresence would break authentication.
927
/* If we see a message type, it's a DHCP packet. */
928
case DHO_DHCP_MESSAGE_TYPE:
933
* If there's a maximum message size option, we
934
* should pay attention to it
936
case DHO_DHCP_MAX_MESSAGE_SIZE:
937
mms = ntohs(*(op + 2));
938
if (mms < dhcp_max_agent_option_packet_length &&
940
max = ((u_int8_t *)packet) + mms;
943
/* Quit immediately if we hit an End option. */
947
case DHO_DHCP_AGENT_OPTIONS:
948
/* We shouldn't see a relay agent option in a
949
packet before we've seen the DHCP packet type,
950
but if we do, we have to leave it alone. */
956
/* There's already a Relay Agent Information option
957
in this packet. How embarrassing. Decide what
958
to do based on the mode the user specified. */
960
switch(agent_relay_mode) {
961
case forward_and_append:
963
case forward_untouched:
967
case forward_and_replace:
972
/* Skip over the agent option and start copying
973
if we aren't copying already. */
978
/* Skip over other options. */
980
/* Fail if processing this option will exceed the
981
* buffer(op[1] is malformed).
983
nextop = op + op[1] + 2;
990
memmove(sp, op, op[1] + 2);
1001
/* If it's not a DHCP packet, we're not supposed to touch it. */
1005
/* If the packet was padded out, we can store the agent option
1006
at the beginning of the padding. */
1008
if (end_pad != NULL)
1011
/* Remember where the end of the packet was after parsing
1015
/* Sanity check. Had better not ever happen. */
1016
if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
1017
log_fatal("Circuit ID length %d out of range [1-255] on "
1018
"%s\n", ip->circuit_id_len, ip->name);
1019
optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */
1021
if (ip->remote_id) {
1022
if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
1023
log_fatal("Remote ID length %d out of range [1-255] "
1024
"on %s\n", ip->circuit_id_len, ip->name);
1025
optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */
1028
/* We do not support relay option fragmenting(multiple options to
1029
* support an option data exceeding 255 bytes).
1031
if ((optlen < 3) ||(optlen > 255))
1032
log_fatal("Total agent option length(%u) out of range "
1033
"[3 - 255] on %s\n", optlen, ip->name);
1036
* Is there room for the option, its code+len, and DHO_END?
1037
* If not, forward without adding the option.
1039
if (max - sp >= optlen + 3) {
1040
log_debug("Adding %d-byte relay agent option", optlen + 3);
1042
/* Okay, cons up *our* Relay Agent Information option. */
1043
*sp++ = DHO_DHCP_AGENT_OPTIONS;
1046
/* Copy in the circuit id... */
1047
*sp++ = RAI_CIRCUIT_ID;
1048
*sp++ = ip->circuit_id_len;
1049
memcpy(sp, ip->circuit_id, ip->circuit_id_len);
1050
sp += ip->circuit_id_len;
1052
/* Copy in remote ID... */
1053
if (ip->remote_id) {
1054
*sp++ = RAI_REMOTE_ID;
1055
*sp++ = ip->remote_id_len;
1056
memcpy(sp, ip->remote_id, ip->remote_id_len);
1057
sp += ip->remote_id_len;
1060
++agent_option_errors;
1061
log_error("No room in packet (used %d of %d) "
1062
"for %d-byte relay agent option: omitted",
1063
(int) (sp - ((u_int8_t *) packet)),
1064
(int) (max - ((u_int8_t *) packet)),
1069
* Deposit an END option unless the packet is full (shouldn't
1075
/* Recalculate total packet length. */
1076
length = sp -((u_int8_t *)packet);
1078
/* Make sure the packet isn't short(this is unlikely, but WTH) */
1079
if (length < BOOTP_MIN_LEN) {
1080
memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
1081
return (BOOTP_MIN_LEN);
1089
* Parse a downstream argument: [address%]interface[#index].
1091
static struct stream_list *
1092
parse_downstream(char *arg) {
1093
struct stream_list *dp, *up;
1094
struct interface_info *ifp = NULL;
1095
char *ifname, *addr, *iid;
1096
isc_result_t status;
1098
if (!supports_multiple_interfaces(ifp) &&
1099
(downstreams != NULL))
1100
log_fatal("No support for multiple interfaces.");
1102
/* Decode the argument. */
1103
ifname = strchr(arg, '%');
1104
if (ifname == NULL) {
1111
iid = strchr(ifname, '#');
1115
if (strlen(ifname) >= sizeof(ifp->name)) {
1116
log_error("Interface name '%s' too long", ifname);
1120
/* Don't declare twice. */
1121
for (dp = downstreams; dp; dp = dp->next) {
1122
if (strcmp(ifname, dp->ifp->name) == 0)
1123
log_fatal("Down interface '%s' declared twice.",
1127
/* Share with up side? */
1128
for (up = upstreams; up; up = up->next) {
1129
if (strcmp(ifname, up->ifp->name) == 0) {
1130
log_info("Interface '%s' is both down and up.",
1137
/* New interface. */
1139
status = interface_allocate(&ifp, MDL);
1140
if (status != ISC_R_SUCCESS)
1141
log_fatal("%s: interface_allocate: %s",
1142
arg, isc_result_totext(status));
1143
strcpy(ifp->name, ifname);
1145
interface_reference(&ifp->next, interfaces, MDL);
1146
interface_dereference(&interfaces, MDL);
1148
interface_reference(&interfaces, ifp, MDL);
1149
ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
1152
/* New downstream. */
1153
dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
1155
log_fatal("No memory for downstream.");
1162
/* !addr case handled by setup. */
1163
if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
1164
log_fatal("Bad link address '%s'", addr);
1170
* Parse an upstream argument: [address]%interface.
1172
static struct stream_list *
1173
parse_upstream(char *arg) {
1174
struct stream_list *up, *dp;
1175
struct interface_info *ifp = NULL;
1176
char *ifname, *addr;
1177
isc_result_t status;
1179
/* Decode the argument. */
1180
ifname = strchr(arg, '%');
1181
if (ifname == NULL) {
1183
addr = All_DHCP_Servers;
1188
if (strlen(ifname) >= sizeof(ifp->name)) {
1189
log_fatal("Interface name '%s' too long", ifname);
1192
/* Shared up interface? */
1193
for (up = upstreams; up; up = up->next) {
1194
if (strcmp(ifname, up->ifp->name) == 0) {
1199
for (dp = downstreams; dp; dp = dp->next) {
1200
if (strcmp(ifname, dp->ifp->name) == 0) {
1206
/* New interface. */
1208
status = interface_allocate(&ifp, MDL);
1209
if (status != ISC_R_SUCCESS)
1210
log_fatal("%s: interface_allocate: %s",
1211
arg, isc_result_totext(status));
1212
strcpy(ifp->name, ifname);
1214
interface_reference(&ifp->next, interfaces, MDL);
1215
interface_dereference(&interfaces, MDL);
1217
interface_reference(&interfaces, ifp, MDL);
1218
ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
1222
up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
1224
log_fatal("No memory for upstream.");
1228
if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
1229
log_fatal("Bad address %s", addr);
1235
* Setup downstream interfaces.
1238
setup_streams(void) {
1239
struct stream_list *dp, *up;
1241
isc_boolean_t link_is_set;
1243
for (dp = downstreams; dp; dp = dp->next) {
1244
/* Check interface */
1245
if (dp->ifp->v6address_count == 0)
1246
log_fatal("Interface '%s' has no IPv6 addresses.",
1249
/* Check/set link. */
1250
if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
1251
link_is_set = ISC_FALSE;
1253
link_is_set = ISC_TRUE;
1254
for (i = 0; i < dp->ifp->v6address_count; i++) {
1255
if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
1259
if (!memcmp(&dp->ifp->v6addresses[i],
1260
&dp->link.sin6_addr,
1261
sizeof(dp->link.sin6_addr)))
1264
if (i == dp->ifp->v6address_count)
1265
log_fatal("Can't find link address for interface '%s'.",
1268
memcpy(&dp->link.sin6_addr,
1269
&dp->ifp->v6addresses[i],
1270
sizeof(dp->link.sin6_addr));
1272
/* Set interface-id. */
1274
dp->id = dp->ifp->index;
1277
for (up = upstreams; up; up = up->next) {
1278
up->link.sin6_port = local_port;
1279
up->link.sin6_family = AF_INET6;
1281
up->link.sin6_len = sizeof(up->link);
1284
if (up->ifp->v6address_count == 0)
1285
log_fatal("Interface '%s' has no IPv6 addresses.",
1291
* Add DHCPv6 agent options here.
1293
static const int required_forw_opts[] = {
1300
* Process a packet upwards, i.e., from client to server.
1303
process_up6(struct packet *packet, struct stream_list *dp) {
1304
char forw_data[65535];
1306
struct dhcpv6_relay_packet *relay;
1307
struct option_state *opts;
1308
struct stream_list *up;
1310
/* Check if the message should be relayed to the server. */
1311
switch (packet->dhcpv6_msg_type) {
1312
case DHCPV6_SOLICIT:
1313
case DHCPV6_REQUEST:
1314
case DHCPV6_CONFIRM:
1317
case DHCPV6_RELEASE:
1318
case DHCPV6_DECLINE:
1319
case DHCPV6_INFORMATION_REQUEST:
1320
case DHCPV6_RELAY_FORW:
1321
case DHCPV6_LEASEQUERY:
1322
log_info("Relaying %s from %s port %d going up.",
1323
dhcpv6_type_names[packet->dhcpv6_msg_type],
1324
piaddr(packet->client_addr),
1325
ntohs(packet->client_port));
1328
case DHCPV6_ADVERTISE:
1330
case DHCPV6_RECONFIGURE:
1331
case DHCPV6_RELAY_REPL:
1332
case DHCPV6_LEASEQUERY_REPLY:
1333
log_info("Discarding %s from %s port %d going up.",
1334
dhcpv6_type_names[packet->dhcpv6_msg_type],
1335
piaddr(packet->client_addr),
1336
ntohs(packet->client_port));
1340
log_info("Unknown %d type from %s port %d going up.",
1341
packet->dhcpv6_msg_type,
1342
piaddr(packet->client_addr),
1343
ntohs(packet->client_port));
1347
/* Build the relay-forward header. */
1348
relay = (struct dhcpv6_relay_packet *) forw_data;
1349
cursor = sizeof(*relay);
1350
relay->msg_type = DHCPV6_RELAY_FORW;
1351
if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1352
if (packet->dhcpv6_hop_count >= max_hop_count) {
1353
log_info("Hop count exceeded,");
1356
relay->hop_count = packet->dhcpv6_hop_count + 1;
1358
memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1360
/* On smart relay add: && !global. */
1361
if (!use_if_id && downstreams->next) {
1362
log_info("Shan't get back the interface.");
1365
memset(&relay->link_address, 0, 16);
1368
relay->hop_count = 0;
1371
memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1373
memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
1375
/* Get an option state. */
1377
if (!option_state_allocate(&opts, MDL)) {
1378
log_fatal("No memory for upwards options.");
1381
/* Add an interface-id (if used). */
1387
} else if (!downstreams->next) {
1388
if_id = downstreams->id;
1390
log_info("Don't know the interface.");
1391
option_state_dereference(&opts, MDL);
1395
if (!save_option_buffer(&dhcpv6_universe, opts,
1396
NULL, (unsigned char *) &if_id,
1398
D6O_INTERFACE_ID, 0)) {
1399
log_error("Can't save interface-id.");
1400
option_state_dereference(&opts, MDL);
1405
/* Add the relay-msg carrying the packet. */
1406
if (!save_option_buffer(&dhcpv6_universe, opts,
1407
NULL, (unsigned char *) packet->raw,
1408
packet->packet_length,
1409
D6O_RELAY_MSG, 0)) {
1410
log_error("Can't save relay-msg.");
1411
option_state_dereference(&opts, MDL);
1415
/* Finish the relay-forward message. */
1416
cursor += store_options6(forw_data + cursor,
1417
sizeof(forw_data) - cursor,
1419
required_forw_opts, NULL);
1420
option_state_dereference(&opts, MDL);
1422
/* Send it to all upstreams. */
1423
for (up = upstreams; up; up = up->next) {
1424
send_packet6(up->ifp, (unsigned char *) forw_data,
1425
(size_t) cursor, &up->link);
1430
* Process a packet downwards, i.e., from server to client.
1433
process_down6(struct packet *packet) {
1434
struct stream_list *dp;
1435
struct option_cache *oc;
1436
struct data_string relay_msg;
1437
const struct dhcpv6_packet *msg;
1438
struct data_string if_id;
1439
struct sockaddr_in6 to;
1442
/* The packet must be a relay-reply message. */
1443
if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
1444
if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
1445
log_info("Discarding %s from %s port %d going down.",
1446
dhcpv6_type_names[packet->dhcpv6_msg_type],
1447
piaddr(packet->client_addr),
1448
ntohs(packet->client_port));
1450
log_info("Unknown %d type from %s port %d going down.",
1451
packet->dhcpv6_msg_type,
1452
piaddr(packet->client_addr),
1453
ntohs(packet->client_port));
1458
memset(&relay_msg, 0, sizeof(relay_msg));
1459
memset(&if_id, 0, sizeof(if_id));
1460
memset(&to, 0, sizeof(to));
1461
to.sin6_family = AF_INET6;
1463
to.sin6_len = sizeof(to);
1465
to.sin6_port = remote_port;
1468
/* Get the relay-msg option (carrying the message to relay). */
1469
oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
1471
log_info("No relay-msg.");
1474
if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
1475
packet->options, NULL,
1476
&global_scope, oc, MDL) ||
1477
(relay_msg.len < sizeof(struct dhcpv6_packet))) {
1478
log_error("Can't evaluate relay-msg.");
1481
msg = (const struct dhcpv6_packet *) relay_msg.data;
1483
/* Get the interface-id (if exists) and the downstream. */
1484
oc = lookup_option(&dhcpv6_universe, packet->options,
1489
if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
1490
packet->options, NULL,
1491
&global_scope, oc, MDL) ||
1492
(if_id.len != sizeof(int))) {
1493
log_info("Can't evaluate interface-id.");
1496
memcpy(&if_index, if_id.data, sizeof(int));
1497
for (dp = downstreams; dp; dp = dp->next) {
1498
if (dp->id == if_index)
1503
/* Require an interface-id. */
1504
log_info("No interface-id.");
1507
for (dp = downstreams; dp; dp = dp->next) {
1508
/* Get the first matching one. */
1509
if (!memcmp(&dp->link.sin6_addr,
1510
&packet->dhcpv6_link_address,
1511
sizeof(struct in6_addr)))
1515
/* Why bother when there is no choice. */
1516
if (!dp && !downstreams->next)
1519
log_info("Can't find the down interface.");
1522
memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
1523
to.sin6_addr = packet->dhcpv6_peer_address;
1525
/* Check if we should relay the carried message. */
1526
switch (msg->msg_type) {
1527
/* Relay-Reply of for another relay, not a client. */
1528
case DHCPV6_RELAY_REPL:
1529
to.sin6_port = local_port;
1532
case DHCPV6_ADVERTISE:
1534
case DHCPV6_RECONFIGURE:
1535
case DHCPV6_RELAY_FORW:
1536
case DHCPV6_LEASEQUERY_REPLY:
1537
log_info("Relaying %s to %s port %d down.",
1538
dhcpv6_type_names[msg->msg_type],
1540
ntohs(to.sin6_port));
1543
case DHCPV6_SOLICIT:
1544
case DHCPV6_REQUEST:
1545
case DHCPV6_CONFIRM:
1548
case DHCPV6_RELEASE:
1549
case DHCPV6_DECLINE:
1550
case DHCPV6_INFORMATION_REQUEST:
1551
case DHCPV6_LEASEQUERY:
1552
log_info("Discarding %s to %s port %d down.",
1553
dhcpv6_type_names[msg->msg_type],
1555
ntohs(to.sin6_port));
1559
log_info("Unknown %d type to %s port %d down.",
1562
ntohs(to.sin6_port));
1566
/* Send the message to the downstream. */
1567
send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
1568
(size_t) relay_msg.len, &to);
1571
if (relay_msg.data != NULL)
1572
data_string_forget(&relay_msg, MDL);
1573
if (if_id.data != NULL)
1574
data_string_forget(&if_id, MDL);
1578
* Called by the dispatch packet handler with a decoded packet.
1581
dhcpv6(struct packet *packet) {
1582
struct stream_list *dp;
1584
/* Try all relay-replies downwards. */
1585
if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
1586
process_down6(packet);
1589
/* Others are candidates to go up if they come from down. */
1590
for (dp = downstreams; dp; dp = dp->next) {
1591
if (packet->interface != dp->ifp)
1593
process_up6(packet, dp);
1596
/* Relay-forward could work from an unknown interface. */
1597
if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1598
process_up6(packet, NULL);
1602
log_info("Can't process packet from interface '%s'.",
1603
packet->interface->name);
1607
/* Stub routines needed for linking with DHCP libraries. */
1609
bootp(struct packet *packet) {
1614
dhcp(struct packet *packet) {
1619
classify(struct packet *p, struct class *c) {
1624
check_collection(struct packet *p, struct lease *l, struct collection *c) {
1629
find_class(struct class **class, const char *c1, const char *c2, int i) {
1630
return ISC_R_NOTFOUND;
1634
parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
1639
dhcp_set_control_state(control_object_state_t oldstate,
1640
control_object_state_t newstate) {
1641
return ISC_R_SUCCESS;