ISC DHCP  4.3.2
A reference DHCPv4 and DHCPv6 implementation
dhcrelay.c
Go to the documentation of this file.
1 /* dhcrelay.c
2 
3  DHCP/BOOTP Relay Agent. */
4 
5 /*
6  * Copyright(c) 2004-2015 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-2015 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("parse_downstream: Interface '%s' is "
1237  "both down and up.", 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);
1255  }
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  log_info("parse_upstream: Interface '%s' is "
1308  "both down and up.", ifname);
1309  ifp = dp->ifp;
1310  break;
1311  }
1312  }
1313 
1314  /* New interface. */
1315  if (ifp == NULL) {
1316  status = interface_allocate(&ifp, MDL);
1317  if (status != ISC_R_SUCCESS)
1318  log_fatal("%s: interface_allocate: %s",
1319  arg, isc_result_totext(status));
1320  strcpy(ifp->name, ifname);
1321  if (interfaces) {
1322  interface_reference(&ifp->next, interfaces, MDL);
1323  interface_dereference(&interfaces, MDL);
1324  }
1325  interface_reference(&interfaces, ifp, MDL);
1326  }
1328 
1329  /* New upstream. */
1330  up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
1331  if (up == NULL)
1332  log_fatal("No memory for upstream.");
1333 
1334  up->ifp = ifp;
1335 
1336  if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
1337  log_fatal("Bad address %s", addr);
1338 
1339  return up;
1340 }
1341 
1342 /*
1343  * Setup downstream interfaces.
1344  */
1345 static void
1346 setup_streams(void) {
1347  struct stream_list *dp, *up;
1348  int i;
1349  isc_boolean_t link_is_set;
1350 
1351  for (dp = downstreams; dp; dp = dp->next) {
1352  /* Check interface */
1353  if (dp->ifp->v6address_count == 0)
1354  log_fatal("Interface '%s' has no IPv6 addresses.",
1355  dp->ifp->name);
1356 
1357  /* Check/set link. */
1358  if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
1359  link_is_set = ISC_FALSE;
1360  else
1361  link_is_set = ISC_TRUE;
1362  for (i = 0; i < dp->ifp->v6address_count; i++) {
1363  if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
1364  continue;
1365  if (!link_is_set)
1366  break;
1367  if (!memcmp(&dp->ifp->v6addresses[i],
1368  &dp->link.sin6_addr,
1369  sizeof(dp->link.sin6_addr)))
1370  break;
1371  }
1372  if (i == dp->ifp->v6address_count)
1373  log_fatal("Interface %s does not have global IPv6 "
1374  "address assigned.", dp->ifp->name);
1375  if (!link_is_set)
1376  memcpy(&dp->link.sin6_addr,
1377  &dp->ifp->v6addresses[i],
1378  sizeof(dp->link.sin6_addr));
1379 
1380  /* Set interface-id. */
1381  if (dp->id == -1)
1382  dp->id = dp->ifp->index;
1383  }
1384 
1385  for (up = upstreams; up; up = up->next) {
1386  up->link.sin6_port = local_port;
1387  up->link.sin6_family = AF_INET6;
1388 #ifdef HAVE_SA_LEN
1389  up->link.sin6_len = sizeof(up->link);
1390 #endif
1391 
1392  if (up->ifp->v6address_count == 0)
1393  log_fatal("Interface '%s' has no IPv6 addresses.",
1394  up->ifp->name);
1395 
1396  /* RFC 3315 Sec 20 - "If the relay agent relays messages to
1397  * the All_DHCP_Servers address or other multicast addresses,
1398  * it sets the Hop Limit field to 32." */
1399  if (IN6_IS_ADDR_MULTICAST(&up->link.sin6_addr)) {
1401  }
1402  }
1403 }
1404 
1405 /*
1406  * Add DHCPv6 agent options here.
1407  */
1408 static const int required_forw_opts[] = {
1411  D6O_RELAY_MSG,
1412  0
1413 };
1414 
1415 /*
1416  * Process a packet upwards, i.e., from client to server.
1417  */
1418 static void
1419 process_up6(struct packet *packet, struct stream_list *dp) {
1420  char forw_data[65535];
1421  unsigned cursor;
1422  struct dhcpv6_relay_packet *relay;
1423  struct option_state *opts;
1424  struct stream_list *up;
1425 
1426  /* Check if the message should be relayed to the server. */
1427  switch (packet->dhcpv6_msg_type) {
1428  case DHCPV6_SOLICIT:
1429  case DHCPV6_REQUEST:
1430  case DHCPV6_CONFIRM:
1431  case DHCPV6_RENEW:
1432  case DHCPV6_REBIND:
1433  case DHCPV6_RELEASE:
1434  case DHCPV6_DECLINE:
1436  case DHCPV6_RELAY_FORW:
1437  case DHCPV6_LEASEQUERY:
1438  log_info("Relaying %s from %s port %d going up.",
1440  piaddr(packet->client_addr),
1441  ntohs(packet->client_port));
1442  break;
1443 
1444  case DHCPV6_ADVERTISE:
1445  case DHCPV6_REPLY:
1446  case DHCPV6_RECONFIGURE:
1447  case DHCPV6_RELAY_REPL:
1449  log_info("Discarding %s from %s port %d going up.",
1451  piaddr(packet->client_addr),
1452  ntohs(packet->client_port));
1453  return;
1454 
1455  default:
1456  log_info("Unknown %d type from %s port %d going up.",
1457  packet->dhcpv6_msg_type,
1458  piaddr(packet->client_addr),
1459  ntohs(packet->client_port));
1460  return;
1461  }
1462 
1463  /* Build the relay-forward header. */
1464  relay = (struct dhcpv6_relay_packet *) forw_data;
1465  cursor = offsetof(struct dhcpv6_relay_packet, options);
1466  relay->msg_type = DHCPV6_RELAY_FORW;
1467  if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1468  if (packet->dhcpv6_hop_count >= max_hop_count) {
1469  log_info("Hop count exceeded,");
1470  return;
1471  }
1472  relay->hop_count = packet->dhcpv6_hop_count + 1;
1473  if (dp) {
1474  memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1475  } else {
1476  /* On smart relay add: && !global. */
1477  if (!use_if_id && downstreams->next) {
1478  log_info("Shan't get back the interface.");
1479  return;
1480  }
1481  memset(&relay->link_address, 0, 16);
1482  }
1483  } else {
1484  relay->hop_count = 0;
1485  if (!dp)
1486  return;
1487  memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1488  }
1489  memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
1490 
1491  /* Get an option state. */
1492  opts = NULL;
1493  if (!option_state_allocate(&opts, MDL)) {
1494  log_fatal("No memory for upwards options.");
1495  }
1496 
1497  /* Add an interface-id (if used). */
1498  if (use_if_id) {
1499  int if_id;
1500 
1501  if (dp) {
1502  if_id = dp->id;
1503  } else if (!downstreams->next) {
1504  if_id = downstreams->id;
1505  } else {
1506  log_info("Don't know the interface.");
1507  option_state_dereference(&opts, MDL);
1508  return;
1509  }
1510 
1511  if (!save_option_buffer(&dhcpv6_universe, opts,
1512  NULL, (unsigned char *) &if_id,
1513  sizeof(int),
1514  D6O_INTERFACE_ID, 0)) {
1515  log_error("Can't save interface-id.");
1516  option_state_dereference(&opts, MDL);
1517  return;
1518  }
1519  }
1520 
1521  /* Add a subscriber-id if desired. */
1522  /* This is for testing rather than general use */
1523  if (dhcrelay_sub_id != NULL) {
1524  if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
1525  (unsigned char *) dhcrelay_sub_id,
1526  strlen(dhcrelay_sub_id),
1527  D6O_SUBSCRIBER_ID, 0)) {
1528  log_error("Can't save subsriber-id.");
1529  option_state_dereference(&opts, MDL);
1530  return;
1531  }
1532  }
1533 
1534 
1535  /* Add the relay-msg carrying the packet. */
1536  if (!save_option_buffer(&dhcpv6_universe, opts,
1537  NULL, (unsigned char *) packet->raw,
1538  packet->packet_length,
1539  D6O_RELAY_MSG, 0)) {
1540  log_error("Can't save relay-msg.");
1541  option_state_dereference(&opts, MDL);
1542  return;
1543  }
1544 
1545  /* Finish the relay-forward message. */
1546  cursor += store_options6(forw_data + cursor,
1547  sizeof(forw_data) - cursor,
1548  opts, packet,
1549  required_forw_opts, NULL);
1550  option_state_dereference(&opts, MDL);
1551 
1552  /* Send it to all upstreams. */
1553  for (up = upstreams; up; up = up->next) {
1554  send_packet6(up->ifp, (unsigned char *) forw_data,
1555  (size_t) cursor, &up->link);
1556  }
1557 }
1558 
1559 /*
1560  * Process a packet downwards, i.e., from server to client.
1561  */
1562 static void
1563 process_down6(struct packet *packet) {
1564  struct stream_list *dp;
1565  struct option_cache *oc;
1566  struct data_string relay_msg;
1567  const struct dhcpv6_packet *msg;
1568  struct data_string if_id;
1569  struct sockaddr_in6 to;
1570  struct iaddr peer;
1571 
1572  /* The packet must be a relay-reply message. */
1573  if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
1574  if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
1575  log_info("Discarding %s from %s port %d going down.",
1577  piaddr(packet->client_addr),
1578  ntohs(packet->client_port));
1579  else
1580  log_info("Unknown %d type from %s port %d going down.",
1581  packet->dhcpv6_msg_type,
1582  piaddr(packet->client_addr),
1583  ntohs(packet->client_port));
1584  return;
1585  }
1586 
1587  /* Inits. */
1588  memset(&relay_msg, 0, sizeof(relay_msg));
1589  memset(&if_id, 0, sizeof(if_id));
1590  memset(&to, 0, sizeof(to));
1591  to.sin6_family = AF_INET6;
1592 #ifdef HAVE_SA_LEN
1593  to.sin6_len = sizeof(to);
1594 #endif
1595  to.sin6_port = remote_port;
1596  peer.len = 16;
1597 
1598  /* Get the relay-msg option (carrying the message to relay). */
1600  if (oc == NULL) {
1601  log_info("No relay-msg.");
1602  return;
1603  }
1604  if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
1605  packet->options, NULL,
1606  &global_scope, oc, MDL) ||
1607  (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
1608  log_error("Can't evaluate relay-msg.");
1609  return;
1610  }
1611  msg = (const struct dhcpv6_packet *) relay_msg.data;
1612 
1613  /* Get the interface-id (if exists) and the downstream. */
1614  oc = lookup_option(&dhcpv6_universe, packet->options,
1616  if (oc != NULL) {
1617  int if_index;
1618 
1619  if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
1620  packet->options, NULL,
1621  &global_scope, oc, MDL) ||
1622  (if_id.len != sizeof(int))) {
1623  log_info("Can't evaluate interface-id.");
1624  goto cleanup;
1625  }
1626  memcpy(&if_index, if_id.data, sizeof(int));
1627  for (dp = downstreams; dp; dp = dp->next) {
1628  if (dp->id == if_index)
1629  break;
1630  }
1631  } else {
1632  if (use_if_id) {
1633  /* Require an interface-id. */
1634  log_info("No interface-id.");
1635  goto cleanup;
1636  }
1637  for (dp = downstreams; dp; dp = dp->next) {
1638  /* Get the first matching one. */
1639  if (!memcmp(&dp->link.sin6_addr,
1640  &packet->dhcpv6_link_address,
1641  sizeof(struct in6_addr)))
1642  break;
1643  }
1644  }
1645  /* Why bother when there is no choice. */
1646  if (!dp && downstreams && !downstreams->next)
1647  dp = downstreams;
1648  if (!dp) {
1649  log_info("Can't find the down interface.");
1650  goto cleanup;
1651  }
1652  memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
1653  to.sin6_addr = packet->dhcpv6_peer_address;
1654 
1655  /* Check if we should relay the carried message. */
1656  switch (msg->msg_type) {
1657  /* Relay-Reply of for another relay, not a client. */
1658  case DHCPV6_RELAY_REPL:
1659  to.sin6_port = local_port;
1660  /* Fall into: */
1661 
1662  case DHCPV6_ADVERTISE:
1663  case DHCPV6_REPLY:
1664  case DHCPV6_RECONFIGURE:
1665  case DHCPV6_RELAY_FORW:
1667  log_info("Relaying %s to %s port %d down.",
1669  piaddr(peer),
1670  ntohs(to.sin6_port));
1671  break;
1672 
1673  case DHCPV6_SOLICIT:
1674  case DHCPV6_REQUEST:
1675  case DHCPV6_CONFIRM:
1676  case DHCPV6_RENEW:
1677  case DHCPV6_REBIND:
1678  case DHCPV6_RELEASE:
1679  case DHCPV6_DECLINE:
1681  case DHCPV6_LEASEQUERY:
1682  log_info("Discarding %s to %s port %d down.",
1684  piaddr(peer),
1685  ntohs(to.sin6_port));
1686  goto cleanup;
1687 
1688  default:
1689  log_info("Unknown %d type to %s port %d down.",
1690  msg->msg_type,
1691  piaddr(peer),
1692  ntohs(to.sin6_port));
1693  goto cleanup;
1694  }
1695 
1696  /* Send the message to the downstream. */
1697  send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
1698  (size_t) relay_msg.len, &to);
1699 
1700  cleanup:
1701  if (relay_msg.data != NULL)
1702  data_string_forget(&relay_msg, MDL);
1703  if (if_id.data != NULL)
1704  data_string_forget(&if_id, MDL);
1705 }
1706 
1707 /*
1708  * Called by the dispatch packet handler with a decoded packet.
1709  */
1710 void
1711 dhcpv6(struct packet *packet) {
1712  struct stream_list *dp;
1713 
1714  /* Try all relay-replies downwards. */
1715  if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
1716  process_down6(packet);
1717  return;
1718  }
1719  /* Others are candidates to go up if they come from down. */
1720  for (dp = downstreams; dp; dp = dp->next) {
1721  if (packet->interface != dp->ifp)
1722  continue;
1723  process_up6(packet, dp);
1724  return;
1725  }
1726  /* Relay-forward could work from an unknown interface. */
1727  if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1728  process_up6(packet, NULL);
1729  return;
1730  }
1731 
1732  log_info("Can't process packet from interface '%s'.",
1733  packet->interface->name);
1734 }
1735 #endif
1736 
1737 /* Stub routines needed for linking with DHCP libraries. */
1738 void
1739 bootp(struct packet *packet) {
1740  return;
1741 }
1742 
1743 void
1744 dhcp(struct packet *packet) {
1745  return;
1746 }
1747 
1748 void
1749 classify(struct packet *p, struct class *c) {
1750  return;
1751 }
1752 
1753 int
1754 check_collection(struct packet *p, struct lease *l, struct collection *c) {
1755  return 0;
1756 }
1757 
1758 isc_result_t
1759 find_class(struct class **class, const char *c1, const char *c2, int i) {
1760  return ISC_R_NOTFOUND;
1761 }
1762 
1763 int
1764 parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
1765  return 0;
1766 }
1767 
1768 isc_result_t
1770  control_object_state_t newstate) {
1771  if (newstate != server_shutdown)
1772  return ISC_R_SUCCESS;
1773 
1774  if (no_pid_file == ISC_FALSE)
1775  (void) unlink(path_dhcrelay_pid);
1776 
1777  exit(0);
1778 }
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)
void(* dhcpv6_packet_handler)(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
Definition: dhcpd.h:521
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:1739
char name[IFNAMSIZ]
Definition: dhcpd.h:1305
unsigned char options[FLEXIBLE_ARRAY_MEMBER]
Definition: dhcp6.h:195
void(* bootp_packet_handler)(struct interface_info *, struct dhcp_packet *, unsigned, unsigned int, struct iaddr, struct hardware *)
Definition: discover.c:58
int check_collection(struct packet *p, struct lease *l, struct collection *c)
Definition: dhcrelay.c:1754
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:619
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:1769
#define DHCP_CONTEXT_PRE_DB
Definition: isclib.h:126
#define DHCRELAY_USAGE
Definition: dhcrelay.c:168
struct in_addr * addresses
Definition: dhcpd.h:1285
int bad_circuit_id
Definition: dhcrelay.c:81
void data_string_forget(struct data_string *data, const char *file, int line)
Definition: alloc.c:1340
#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:414
#define BOOTP_MIN_LEN
Definition: dhcp.h:40
Definition: dhcpd.h:259
unsigned char dhcpv6_hop_count
Definition: dhcpd.h:388
void dispatch(void)
Definition: dispatch.c:109
unsigned char link_address[16]
Definition: dhcp6.h:193
#define DHCP_LOG_OPTIONS
Definition: dhcpd.h:1531
unsigned char dhcpv6_msg_type
Definition: dhcpd.h:382
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:396
#define DHCPV6_LEASEQUERY
Definition: dhcp6.h:111
#define DHCP_CONTEXT_POST_DB
Definition: isclib.h:127
#define DISCOVER_RELAY
Definition: dhcpd.h:653
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:377
u_int16_t validate_port(char *port)
Definition: inet.c:661
void dhcp_signal_handler(int signal)
Definition: isclib.c:347
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:138
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:2688
Definition: tree.h:346
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:1519
struct interface_info * interface
Definition: dhcpd.h:398
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:1299
#define DHCPV6_REBIND
Definition: dhcp6.h:103
Definition: dhcpd.h:376
#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:1764
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:2390
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:2348
#define DHO_END
Definition: dhcp.h:169
control_object_state_t
Definition: dhcpd.h:484
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:1319
void interface_snorf(struct interface_info *tmp, int ir)
Definition: discover.c:1485
void dhcp(struct packet *packet)
Definition: dhcrelay.c:1744
#define DHO_DHCP_MAX_MESSAGE_SIZE
Definition: dhcp.h:149
#define RAI_CIRCUIT_ID
Definition: dhcp.h:187
void cleanup(void)
#define DHCPV6_LEASEQUERY_REPLY
Definition: dhcp6.h:112
Definition: inet.h:31
u_int8_t * circuit_id
Definition: dhcpd.h:1297
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:931
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:1004
void dhcpv6(struct packet *)
struct timeval cur_tv
Definition: dispatch.c:35
#define HOP_COUNT_LIMIT
Definition: dhcp6.h:170
const int dhcpv6_type_name_max
Definition: tables.c:637
unsigned char hop_count
Definition: dhcp6.h:192
int missing_agent_option
Definition: dhcrelay.c:79
struct interface_info * next
Definition: dhcpd.h:1280
struct universe dhcpv6_universe
Definition: tables.c:329
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:1749
int supports_multiple_interfaces(struct interface_info *)
u_int8_t * remote_id
Definition: dhcpd.h:1301
isc_result_t interface_setup()
Definition: discover.c:83
int address_count
Definition: dhcpd.h:1288
u_int8_t hops
Definition: dhcp.h:52
int server_packet_errors
Definition: dhcrelay.c:67
#define INTERFACE_UPSTREAM
Definition: dhcpd.h:1324
#define DHCPV6_CONFIRM
Definition: dhcp6.h:101
unsigned remote_id_len
Definition: dhcpd.h:1303
struct server_list * servers
struct iaddr client_addr
Definition: dhcpd.h:397
void set_multicast_hop_limit(struct interface_info *info, int hop_limit)
#define PACKAGE_VERSION
Definition: config.h:154
#define INTERFACE_DOWNSTREAM
Definition: dhcpd.h:1323
option_code_hash_t * code_hash
Definition: tree.h:338
#define _PATH_DHCRELAY_PID
Definition: config.h:251
struct in6_addr dhcpv6_peer_address
Definition: dhcpd.h:390
#define RAI_REMOTE_ID
Definition: dhcp.h:188
int can_unicast_without_arp(struct interface_info *)
#define DHO_DHCP_MESSAGE_TYPE
Definition: dhcp.h:145
unsigned packet_length
Definition: dhcpd.h:379
#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:1315
struct in6_addr dhcpv6_link_address
Definition: dhcpd.h:389
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:1320
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:1759
int log_perror
Definition: errwarn.c:44