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