ISC DHCP  4.3.0
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-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", LOG_NDELAY, 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  /* install signal handlers */
619  signal(SIGINT, dhcp_signal_handler); /* control-c */
620  signal(SIGTERM, dhcp_signal_handler); /* kill */
621 
622 #ifdef HAVE_LIBCAP_NG
623  /* Drop all capabilities */
624  if (!keep_capabilities) {
625  capng_clear(CAPNG_SELECT_BOTH);
626  capng_apply(CAPNG_SELECT_BOTH);
627  log_info ("Dropped all capabilities.");
628  }
629 #endif
630 
631 #ifdef HAVE_LIBSYSTEMD
632  /* We are ready to process incomming packets. Let's notify systemd */
633  sd_notifyf(0, "READY=1\n"
634  "STATUS=Dispatching packets...\n"
635  "MAINPID=%lu",
636  (unsigned long) getpid());
637 #endif
638 
639  /* Start dispatching packets and timeouts... */
640  dispatch();
641 
642  /* In fact dispatch() never returns. */
643  return (0);
644 }
645 
646 static void
647 do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
648  unsigned int length, unsigned int from_port, struct iaddr from,
649  struct hardware *hfrom) {
650  struct server_list *sp;
651  struct sockaddr_in to;
652  struct interface_info *out;
653  struct hardware hto, *htop;
654 
655  if (packet->hlen > sizeof packet->chaddr) {
656  log_info("Discarding packet with invalid hlen, received on "
657  "%s interface.", ip->name);
658  return;
659  }
660  if (ip->address_count < 1 || ip->addresses == NULL) {
661  log_info("Discarding packet received on %s interface that "
662  "has no IPv4 address assigned.", ip->name);
663  return;
664  }
665 
666  /* Find the interface that corresponds to the giaddr
667  in the packet. */
668  if (packet->giaddr.s_addr) {
669  for (out = interfaces; out; out = out->next) {
670  int i;
671 
672  for (i = 0 ; i < out->address_count ; i++ ) {
673  if (out->addresses[i].s_addr ==
674  packet->giaddr.s_addr) {
675  i = -1;
676  break;
677  }
678  }
679 
680  if (i == -1)
681  break;
682  }
683  } else {
684  out = NULL;
685  }
686 
687  /* If it's a bootreply, forward it to the client. */
688  if (packet->op == BOOTREPLY) {
689  if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
691  to.sin_addr = packet->yiaddr;
692  to.sin_port = remote_port;
693 
694  /* and hardware address is not broadcast */
695  htop = &hto;
696  } else {
697  to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
698  to.sin_port = remote_port;
699 
700  /* hardware address is broadcast */
701  htop = NULL;
702  }
703  to.sin_family = AF_INET;
704 #ifdef HAVE_SA_LEN
705  to.sin_len = sizeof to;
706 #endif
707 
708  memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
709  hto.hbuf[0] = packet->htype;
710  hto.hlen = packet->hlen + 1;
711 
712  /* Wipe out the agent relay options and, if possible, figure
713  out which interface to use based on the contents of the
714  option that we put on the request to which the server is
715  replying. */
716  if (!(length =
717  strip_relay_agent_options(ip, &out, packet, length)))
718  return;
719 
720  if (!out) {
721  log_error("Packet to bogus giaddr %s.\n",
722  inet_ntoa(packet->giaddr));
724  return;
725  }
726 
727  if (send_packet(out, NULL, packet, length, out->addresses[0],
728  &to, htop) < 0) {
730  } else {
731  log_debug("Forwarded BOOTREPLY for %s to %s",
732  print_hw_addr(packet->htype, packet->hlen,
733  packet->chaddr),
734  inet_ntoa(to.sin_addr));
735 
737  }
738  return;
739  }
740 
741  /* If giaddr matches one of our addresses, ignore the packet -
742  we just sent it. */
743  if (out)
744  return;
745 
746  /* Add relay agent options if indicated. If something goes wrong,
747  drop the packet. */
748  if (!(length = add_relay_agent_options(ip, packet, length,
749  ip->addresses[0])))
750  return;
751 
752  /* If giaddr is not already set, Set it so the server can
753  figure out what net it's from and so that we can later
754  forward the response to the correct net. If it's already
755  set, the response will be sent directly to the relay agent
756  that set giaddr, so we won't see it. */
757  if (!packet->giaddr.s_addr)
758  packet->giaddr = ip->addresses[0];
759  if (packet->hops < max_hop_count)
760  packet->hops = packet->hops + 1;
761  else
762  return;
763 
764  /* Otherwise, it's a BOOTREQUEST, so forward it to all the
765  servers. */
766  for (sp = servers; sp; sp = sp->next) {
769  NULL, packet, length, ip->addresses[0],
770  &sp->to, NULL) < 0) {
772  } else {
773  log_debug("Forwarded BOOTREQUEST for %s to %s",
774  print_hw_addr(packet->htype, packet->hlen,
775  packet->chaddr),
776  inet_ntoa(sp->to.sin_addr));
778  }
779  }
780 
781 }
782 
783 /* Strip any Relay Agent Information options from the DHCP packet
784  option buffer. If there is a circuit ID suboption, look up the
785  outgoing interface based upon it. */
786 
787 static int
788 strip_relay_agent_options(struct interface_info *in,
789  struct interface_info **out,
790  struct dhcp_packet *packet,
791  unsigned length) {
792  int is_dhcp = 0;
793  u_int8_t *op, *nextop, *sp, *max;
794  int good_agent_option = 0;
795  int status;
796 
797  /* If we're not adding agent options to packets, we're not taking
798  them out either. */
799  if (!add_agent_options)
800  return (length);
801 
802  /* If there's no cookie, it's a bootp packet, so we should just
803  forward it unchanged. */
804  if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
805  return (length);
806 
807  max = ((u_int8_t *)packet) + length;
808  sp = op = &packet->options[4];
809 
810  while (op < max) {
811  switch(*op) {
812  /* Skip padding... */
813  case DHO_PAD:
814  if (sp != op)
815  *sp = *op;
816  ++op;
817  ++sp;
818  continue;
819 
820  /* If we see a message type, it's a DHCP packet. */
822  is_dhcp = 1;
823  goto skip;
824  break;
825 
826  /* Quit immediately if we hit an End option. */
827  case DHO_END:
828  if (sp != op)
829  *sp++ = *op++;
830  goto out;
831 
833  /* We shouldn't see a relay agent option in a
834  packet before we've seen the DHCP packet type,
835  but if we do, we have to leave it alone. */
836  if (!is_dhcp)
837  goto skip;
838 
839  /* Do not process an agent option if it exceeds the
840  * buffer. Fail this packet.
841  */
842  nextop = op + op[1] + 2;
843  if (nextop > max)
844  return (0);
845 
846  status = find_interface_by_agent_option(packet,
847  out, op + 2,
848  op[1]);
849  if (status == -1 && drop_agent_mismatches)
850  return (0);
851  if (status)
852  good_agent_option = 1;
853  op = nextop;
854  break;
855 
856  skip:
857  /* Skip over other options. */
858  default:
859  /* Fail if processing this option will exceed the
860  * buffer(op[1] is malformed).
861  */
862  nextop = op + op[1] + 2;
863  if (nextop > max)
864  return (0);
865 
866  if (sp != op) {
867  memmove(sp, op, op[1] + 2);
868  sp += op[1] + 2;
869  op = nextop;
870  } else
871  op = sp = nextop;
872 
873  break;
874  }
875  }
876  out:
877 
878  /* If it's not a DHCP packet, we're not supposed to touch it. */
879  if (!is_dhcp)
880  return (length);
881 
882  /* If none of the agent options we found matched, or if we didn't
883  find any agent options, count this packet as not having any
884  matching agent options, and if we're relying on agent options
885  to determine the outgoing interface, drop the packet. */
886 
887  if (!good_agent_option) {
890  return (0);
891  }
892 
893  /* Adjust the length... */
894  if (sp != op) {
895  length = sp -((u_int8_t *)packet);
896 
897  /* Make sure the packet isn't short(this is unlikely,
898  but WTH) */
899  if (length < BOOTP_MIN_LEN) {
900  memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
901  length = BOOTP_MIN_LEN;
902  }
903  }
904  return (length);
905 }
906 
907 
908 /* Find an interface that matches the circuit ID specified in the
909  Relay Agent Information option. If one is found, store it through
910  the pointer given; otherwise, leave the existing pointer alone.
911 
912  We actually deviate somewhat from the current specification here:
913  if the option buffer is corrupt, we suggest that the caller not
914  respond to this packet. If the circuit ID doesn't match any known
915  interface, we suggest that the caller to drop the packet. Only if
916  we find a circuit ID that matches an existing interface do we tell
917  the caller to go ahead and process the packet. */
918 
919 static int
920 find_interface_by_agent_option(struct dhcp_packet *packet,
921  struct interface_info **out,
922  u_int8_t *buf, int len) {
923  int i = 0;
924  u_int8_t *circuit_id = 0;
925  unsigned circuit_id_len = 0;
926  struct interface_info *ip;
927 
928  while (i < len) {
929  /* If the next agent option overflows the end of the
930  packet, the agent option buffer is corrupt. */
931  if (i + 1 == len ||
932  i + buf[i + 1] + 2 > len) {
934  return (-1);
935  }
936  switch(buf[i]) {
937  /* Remember where the circuit ID is... */
938  case RAI_CIRCUIT_ID:
939  circuit_id = &buf[i + 2];
940  circuit_id_len = buf[i + 1];
941  i += circuit_id_len + 2;
942  continue;
943 
944  default:
945  i += buf[i + 1] + 2;
946  break;
947  }
948  }
949 
950  /* If there's no circuit ID, it's not really ours, tell the caller
951  it's no good. */
952  if (!circuit_id) {
954  return (-1);
955  }
956 
957  /* Scan the interface list looking for an interface whose
958  name matches the one specified in circuit_id. */
959 
960  for (ip = interfaces; ip; ip = ip->next) {
961  if (ip->circuit_id &&
962  ip->circuit_id_len == circuit_id_len &&
963  !memcmp(ip->circuit_id, circuit_id, circuit_id_len))
964  break;
965  }
966 
967  /* If we got a match, use it. */
968  if (ip) {
969  *out = ip;
970  return (1);
971  }
972 
973  /* If we didn't get a match, the circuit ID was bogus. */
974  ++bad_circuit_id;
975  return (-1);
976 }
977 
978 /*
979  * Examine a packet to see if it's a candidate to have a Relay
980  * Agent Information option tacked onto its tail. If it is, tack
981  * the option on.
982  */
983 static int
984 add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
985  unsigned length, struct in_addr giaddr) {
986  int is_dhcp = 0, mms;
987  unsigned optlen;
988  u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
989 
990  /* If we're not adding agent options to packets, we can skip
991  this. */
992  if (!add_agent_options)
993  return (length);
994 
995  /* If there's no cookie, it's a bootp packet, so we should just
996  forward it unchanged. */
997  if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
998  return (length);
999 
1000  max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
1001 
1002  /* Commence processing after the cookie. */
1003  sp = op = &packet->options[4];
1004 
1005  while (op < max) {
1006  switch(*op) {
1007  /* Skip padding... */
1008  case DHO_PAD:
1009  /* Remember the first pad byte so we can commandeer
1010  * padded space.
1011  *
1012  * XXX: Is this really a good idea? Sure, we can
1013  * seemingly reduce the packet while we're looking,
1014  * but if the packet was signed by the client then
1015  * this padding is part of the checksum(RFC3118),
1016  * and its nonpresence would break authentication.
1017  */
1018  if (end_pad == NULL)
1019  end_pad = sp;
1020 
1021  if (sp != op)
1022  *sp++ = *op++;
1023  else
1024  sp = ++op;
1025 
1026  continue;
1027 
1028  /* If we see a message type, it's a DHCP packet. */
1029  case DHO_DHCP_MESSAGE_TYPE:
1030  is_dhcp = 1;
1031  goto skip;
1032 
1033  /*
1034  * If there's a maximum message size option, we
1035  * should pay attention to it
1036  */
1038  mms = ntohs(*(op + 2));
1040  mms >= DHCP_MTU_MIN)
1041  max = ((u_int8_t *)packet) + mms;
1042  goto skip;
1043 
1044  /* Quit immediately if we hit an End option. */
1045  case DHO_END:
1046  goto out;
1047 
1049  /* We shouldn't see a relay agent option in a
1050  packet before we've seen the DHCP packet type,
1051  but if we do, we have to leave it alone. */
1052  if (!is_dhcp)
1053  goto skip;
1054 
1055  end_pad = NULL;
1056 
1057  /* There's already a Relay Agent Information option
1058  in this packet. How embarrassing. Decide what
1059  to do based on the mode the user specified. */
1060 
1061  switch(agent_relay_mode) {
1062  case forward_and_append:
1063  goto skip;
1064  case forward_untouched:
1065  return (length);
1066  case discard:
1067  return (0);
1068  case forward_and_replace:
1069  default:
1070  break;
1071  }
1072 
1073  /* Skip over the agent option and start copying
1074  if we aren't copying already. */
1075  op += op[1] + 2;
1076  break;
1077 
1078  skip:
1079  /* Skip over other options. */
1080  default:
1081  /* Fail if processing this option will exceed the
1082  * buffer(op[1] is malformed).
1083  */
1084  nextop = op + op[1] + 2;
1085  if (nextop > max)
1086  return (0);
1087 
1088  end_pad = NULL;
1089 
1090  if (sp != op) {
1091  memmove(sp, op, op[1] + 2);
1092  sp += op[1] + 2;
1093  op = nextop;
1094  } else
1095  op = sp = nextop;
1096 
1097  break;
1098  }
1099  }
1100  out:
1101 
1102  /* If it's not a DHCP packet, we're not supposed to touch it. */
1103  if (!is_dhcp)
1104  return (length);
1105 
1106  /* If the packet was padded out, we can store the agent option
1107  at the beginning of the padding. */
1108 
1109  if (end_pad != NULL)
1110  sp = end_pad;
1111 
1112 #if 0
1113  /* Remember where the end of the packet was after parsing
1114  it. */
1115  op = sp;
1116 #endif
1117 
1118  /* Sanity check. Had better not ever happen. */
1119  if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
1120  log_fatal("Circuit ID length %d out of range [1-255] on "
1121  "%s\n", ip->circuit_id_len, ip->name);
1122  optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */
1123 
1124  if (ip->remote_id) {
1125  if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
1126  log_fatal("Remote ID length %d out of range [1-255] "
1127  "on %s\n", ip->circuit_id_len, ip->name);
1128  optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */
1129  }
1130 
1131  /* We do not support relay option fragmenting(multiple options to
1132  * support an option data exceeding 255 bytes).
1133  */
1134  if ((optlen < 3) ||(optlen > 255))
1135  log_fatal("Total agent option length(%u) out of range "
1136  "[3 - 255] on %s\n", optlen, ip->name);
1137 
1138  /*
1139  * Is there room for the option, its code+len, and DHO_END?
1140  * If not, forward without adding the option.
1141  */
1142  if (max - sp >= optlen + 3) {
1143  log_debug("Adding %d-byte relay agent option", optlen + 3);
1144 
1145  /* Okay, cons up *our* Relay Agent Information option. */
1146  *sp++ = DHO_DHCP_AGENT_OPTIONS;
1147  *sp++ = optlen;
1148 
1149  /* Copy in the circuit id... */
1150  *sp++ = RAI_CIRCUIT_ID;
1151  *sp++ = ip->circuit_id_len;
1152  memcpy(sp, ip->circuit_id, ip->circuit_id_len);
1153  sp += ip->circuit_id_len;
1154 
1155  /* Copy in remote ID... */
1156  if (ip->remote_id) {
1157  *sp++ = RAI_REMOTE_ID;
1158  *sp++ = ip->remote_id_len;
1159  memcpy(sp, ip->remote_id, ip->remote_id_len);
1160  sp += ip->remote_id_len;
1161  }
1162  } else {
1164  log_error("No room in packet (used %d of %d) "
1165  "for %d-byte relay agent option: omitted",
1166  (int) (sp - ((u_int8_t *) packet)),
1167  (int) (max - ((u_int8_t *) packet)),
1168  optlen + 3);
1169  }
1170 
1171  /*
1172  * Deposit an END option unless the packet is full (shouldn't
1173  * be possible).
1174  */
1175  if (sp < max)
1176  *sp++ = DHO_END;
1177 
1178  /* Recalculate total packet length. */
1179  length = sp -((u_int8_t *)packet);
1180 
1181  /* Make sure the packet isn't short(this is unlikely, but WTH) */
1182  if (length < BOOTP_MIN_LEN) {
1183  memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
1184  return (BOOTP_MIN_LEN);
1185  }
1186 
1187  return (length);
1188 }
1189 
1190 #ifdef DHCPv6
1191 /*
1192  * Parse a downstream argument: [address%]interface[#index].
1193  */
1194 static struct stream_list *
1195 parse_downstream(char *arg) {
1196  struct stream_list *dp, *up;
1197  struct interface_info *ifp = NULL;
1198  char *ifname, *addr, *iid;
1199  isc_result_t status;
1200 
1201  if (!supports_multiple_interfaces(ifp) &&
1202  (downstreams != NULL))
1203  log_fatal("No support for multiple interfaces.");
1204 
1205  /* Decode the argument. */
1206  ifname = strchr(arg, '%');
1207  if (ifname == NULL) {
1208  ifname = arg;
1209  addr = NULL;
1210  } else {
1211  *ifname++ = '\0';
1212  addr = arg;
1213  }
1214  iid = strchr(ifname, '#');
1215  if (iid != NULL) {
1216  *iid++ = '\0';
1217  }
1218  if (strlen(ifname) >= sizeof(ifp->name)) {
1219  log_error("Interface name '%s' too long", ifname);
1220  usage();
1221  }
1222 
1223  /* Don't declare twice. */
1224  for (dp = downstreams; dp; dp = dp->next) {
1225  if (strcmp(ifname, dp->ifp->name) == 0)
1226  log_fatal("Down interface '%s' declared twice.",
1227  ifname);
1228  }
1229 
1230  /* Share with up side? */
1231  for (up = upstreams; up; up = up->next) {
1232  if (strcmp(ifname, up->ifp->name) == 0) {
1233  log_info("Interface '%s' is both down and up.",
1234  ifname);
1235  ifp = up->ifp;
1236  break;
1237  }
1238  }
1239 
1240  /* New interface. */
1241  if (ifp == NULL) {
1242  status = interface_allocate(&ifp, MDL);
1243  if (status != ISC_R_SUCCESS)
1244  log_fatal("%s: interface_allocate: %s",
1245  arg, isc_result_totext(status));
1246  strcpy(ifp->name, ifname);
1247  if (interfaces) {
1248  interface_reference(&ifp->next, interfaces, MDL);
1249  interface_dereference(&interfaces, MDL);
1250  }
1251  interface_reference(&interfaces, ifp, MDL);
1253  }
1254 
1255  /* New downstream. */
1256  dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
1257  if (!dp)
1258  log_fatal("No memory for downstream.");
1259  dp->ifp = ifp;
1260  if (iid != NULL) {
1261  dp->id = atoi(iid);
1262  } else {
1263  dp->id = -1;
1264  }
1265  /* !addr case handled by setup. */
1266  if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
1267  log_fatal("Bad link address '%s'", addr);
1268 
1269  return dp;
1270 }
1271 
1272 /*
1273  * Parse an upstream argument: [address]%interface.
1274  */
1275 static struct stream_list *
1276 parse_upstream(char *arg) {
1277  struct stream_list *up, *dp;
1278  struct interface_info *ifp = NULL;
1279  char *ifname, *addr;
1280  isc_result_t status;
1281 
1282  /* Decode the argument. */
1283  ifname = strchr(arg, '%');
1284  if (ifname == NULL) {
1285  ifname = arg;
1286  addr = All_DHCP_Servers;
1287  } else {
1288  *ifname++ = '\0';
1289  addr = arg;
1290  }
1291  if (strlen(ifname) >= sizeof(ifp->name)) {
1292  log_fatal("Interface name '%s' too long", ifname);
1293  }
1294 
1295  /* Shared up interface? */
1296  for (up = upstreams; up; up = up->next) {
1297  if (strcmp(ifname, up->ifp->name) == 0) {
1298  ifp = up->ifp;
1299  break;
1300  }
1301  }
1302  for (dp = downstreams; dp; dp = dp->next) {
1303  if (strcmp(ifname, dp->ifp->name) == 0) {
1304  ifp = dp->ifp;
1305  break;
1306  }
1307  }
1308 
1309  /* New interface. */
1310  if (ifp == NULL) {
1311  status = interface_allocate(&ifp, MDL);
1312  if (status != ISC_R_SUCCESS)
1313  log_fatal("%s: interface_allocate: %s",
1314  arg, isc_result_totext(status));
1315  strcpy(ifp->name, ifname);
1316  if (interfaces) {
1317  interface_reference(&ifp->next, interfaces, MDL);
1318  interface_dereference(&interfaces, MDL);
1319  }
1320  interface_reference(&interfaces, ifp, MDL);
1322  }
1323 
1324  /* New upstream. */
1325  up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
1326  if (up == NULL)
1327  log_fatal("No memory for upstream.");
1328 
1329  up->ifp = ifp;
1330 
1331  if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
1332  log_fatal("Bad address %s", addr);
1333 
1334  return up;
1335 }
1336 
1337 /*
1338  * Setup downstream interfaces.
1339  */
1340 static void
1341 setup_streams(void) {
1342  struct stream_list *dp, *up;
1343  int i;
1344  isc_boolean_t link_is_set;
1345 
1346  for (dp = downstreams; dp; dp = dp->next) {
1347  /* Check interface */
1348  if (dp->ifp->v6address_count == 0)
1349  log_fatal("Interface '%s' has no IPv6 addresses.",
1350  dp->ifp->name);
1351 
1352  /* Check/set link. */
1353  if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
1354  link_is_set = ISC_FALSE;
1355  else
1356  link_is_set = ISC_TRUE;
1357  for (i = 0; i < dp->ifp->v6address_count; i++) {
1358  if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
1359  continue;
1360  if (!link_is_set)
1361  break;
1362  if (!memcmp(&dp->ifp->v6addresses[i],
1363  &dp->link.sin6_addr,
1364  sizeof(dp->link.sin6_addr)))
1365  break;
1366  }
1367  if (i == dp->ifp->v6address_count)
1368  log_fatal("Interface %s does not have global IPv6 "
1369  "address assigned.", dp->ifp->name);
1370  if (!link_is_set)
1371  memcpy(&dp->link.sin6_addr,
1372  &dp->ifp->v6addresses[i],
1373  sizeof(dp->link.sin6_addr));
1374 
1375  /* Set interface-id. */
1376  if (dp->id == -1)
1377  dp->id = dp->ifp->index;
1378  }
1379 
1380  for (up = upstreams; up; up = up->next) {
1381  up->link.sin6_port = local_port;
1382  up->link.sin6_family = AF_INET6;
1383 #ifdef HAVE_SA_LEN
1384  up->link.sin6_len = sizeof(up->link);
1385 #endif
1386 
1387  if (up->ifp->v6address_count == 0)
1388  log_fatal("Interface '%s' has no IPv6 addresses.",
1389  up->ifp->name);
1390  }
1391 }
1392 
1393 /*
1394  * Add DHCPv6 agent options here.
1395  */
1396 static const int required_forw_opts[] = {
1399  D6O_RELAY_MSG,
1400  0
1401 };
1402 
1403 /*
1404  * Process a packet upwards, i.e., from client to server.
1405  */
1406 static void
1407 process_up6(struct packet *packet, struct stream_list *dp) {
1408  char forw_data[65535];
1409  unsigned cursor;
1410  struct dhcpv6_relay_packet *relay;
1411  struct option_state *opts;
1412  struct stream_list *up;
1413 
1414  /* Check if the message should be relayed to the server. */
1415  switch (packet->dhcpv6_msg_type) {
1416  case DHCPV6_SOLICIT:
1417  case DHCPV6_REQUEST:
1418  case DHCPV6_CONFIRM:
1419  case DHCPV6_RENEW:
1420  case DHCPV6_REBIND:
1421  case DHCPV6_RELEASE:
1422  case DHCPV6_DECLINE:
1424  case DHCPV6_RELAY_FORW:
1425  case DHCPV6_LEASEQUERY:
1426  log_info("Relaying %s from %s port %d going up.",
1428  piaddr(packet->client_addr),
1429  ntohs(packet->client_port));
1430  break;
1431 
1432  case DHCPV6_ADVERTISE:
1433  case DHCPV6_REPLY:
1434  case DHCPV6_RECONFIGURE:
1435  case DHCPV6_RELAY_REPL:
1437  log_info("Discarding %s from %s port %d going up.",
1439  piaddr(packet->client_addr),
1440  ntohs(packet->client_port));
1441  return;
1442 
1443  default:
1444  log_info("Unknown %d type from %s port %d going up.",
1445  packet->dhcpv6_msg_type,
1446  piaddr(packet->client_addr),
1447  ntohs(packet->client_port));
1448  return;
1449  }
1450 
1451  /* Build the relay-forward header. */
1452  relay = (struct dhcpv6_relay_packet *) forw_data;
1453  cursor = offsetof(struct dhcpv6_relay_packet, options);
1454  relay->msg_type = DHCPV6_RELAY_FORW;
1455  if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1456  if (packet->dhcpv6_hop_count >= max_hop_count) {
1457  log_info("Hop count exceeded,");
1458  return;
1459  }
1460  relay->hop_count = packet->dhcpv6_hop_count + 1;
1461  if (dp) {
1462  memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1463  } else {
1464  /* On smart relay add: && !global. */
1465  if (!use_if_id && downstreams->next) {
1466  log_info("Shan't get back the interface.");
1467  return;
1468  }
1469  memset(&relay->link_address, 0, 16);
1470  }
1471  } else {
1472  relay->hop_count = 0;
1473  if (!dp)
1474  return;
1475  memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1476  }
1477  memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
1478 
1479  /* Get an option state. */
1480  opts = NULL;
1481  if (!option_state_allocate(&opts, MDL)) {
1482  log_fatal("No memory for upwards options.");
1483  }
1484 
1485  /* Add an interface-id (if used). */
1486  if (use_if_id) {
1487  int if_id;
1488 
1489  if (dp) {
1490  if_id = dp->id;
1491  } else if (!downstreams->next) {
1492  if_id = downstreams->id;
1493  } else {
1494  log_info("Don't know the interface.");
1495  option_state_dereference(&opts, MDL);
1496  return;
1497  }
1498 
1499  if (!save_option_buffer(&dhcpv6_universe, opts,
1500  NULL, (unsigned char *) &if_id,
1501  sizeof(int),
1502  D6O_INTERFACE_ID, 0)) {
1503  log_error("Can't save interface-id.");
1504  option_state_dereference(&opts, MDL);
1505  return;
1506  }
1507  }
1508 
1509  /* Add a subscriber-id if desired. */
1510  /* This is for testing rather than general use */
1511  if (dhcrelay_sub_id != NULL) {
1512  if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
1513  (unsigned char *) dhcrelay_sub_id,
1514  strlen(dhcrelay_sub_id),
1515  D6O_SUBSCRIBER_ID, 0)) {
1516  log_error("Can't save subsriber-id.");
1517  option_state_dereference(&opts, MDL);
1518  return;
1519  }
1520  }
1521 
1522 
1523  /* Add the relay-msg carrying the packet. */
1524  if (!save_option_buffer(&dhcpv6_universe, opts,
1525  NULL, (unsigned char *) packet->raw,
1526  packet->packet_length,
1527  D6O_RELAY_MSG, 0)) {
1528  log_error("Can't save relay-msg.");
1529  option_state_dereference(&opts, MDL);
1530  return;
1531  }
1532 
1533  /* Finish the relay-forward message. */
1534  cursor += store_options6(forw_data + cursor,
1535  sizeof(forw_data) - cursor,
1536  opts, packet,
1537  required_forw_opts, NULL);
1538  option_state_dereference(&opts, MDL);
1539 
1540  /* Send it to all upstreams. */
1541  for (up = upstreams; up; up = up->next) {
1542  send_packet6(up->ifp, (unsigned char *) forw_data,
1543  (size_t) cursor, &up->link);
1544  }
1545 }
1546 
1547 /*
1548  * Process a packet downwards, i.e., from server to client.
1549  */
1550 static void
1551 process_down6(struct packet *packet) {
1552  struct stream_list *dp;
1553  struct option_cache *oc;
1554  struct data_string relay_msg;
1555  const struct dhcpv6_packet *msg;
1556  struct data_string if_id;
1557  struct sockaddr_in6 to;
1558  struct iaddr peer;
1559 
1560  /* The packet must be a relay-reply message. */
1561  if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
1562  if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
1563  log_info("Discarding %s from %s port %d going down.",
1565  piaddr(packet->client_addr),
1566  ntohs(packet->client_port));
1567  else
1568  log_info("Unknown %d type from %s port %d going down.",
1569  packet->dhcpv6_msg_type,
1570  piaddr(packet->client_addr),
1571  ntohs(packet->client_port));
1572  return;
1573  }
1574 
1575  /* Inits. */
1576  memset(&relay_msg, 0, sizeof(relay_msg));
1577  memset(&if_id, 0, sizeof(if_id));
1578  memset(&to, 0, sizeof(to));
1579  to.sin6_family = AF_INET6;
1580 #ifdef HAVE_SA_LEN
1581  to.sin6_len = sizeof(to);
1582 #endif
1583  to.sin6_port = remote_port;
1584  peer.len = 16;
1585 
1586  /* Get the relay-msg option (carrying the message to relay). */
1588  if (oc == NULL) {
1589  log_info("No relay-msg.");
1590  return;
1591  }
1592  if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
1593  packet->options, NULL,
1594  &global_scope, oc, MDL) ||
1595  (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
1596  log_error("Can't evaluate relay-msg.");
1597  return;
1598  }
1599  msg = (const struct dhcpv6_packet *) relay_msg.data;
1600 
1601  /* Get the interface-id (if exists) and the downstream. */
1602  oc = lookup_option(&dhcpv6_universe, packet->options,
1604  if (oc != NULL) {
1605  int if_index;
1606 
1607  if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
1608  packet->options, NULL,
1609  &global_scope, oc, MDL) ||
1610  (if_id.len != sizeof(int))) {
1611  log_info("Can't evaluate interface-id.");
1612  goto cleanup;
1613  }
1614  memcpy(&if_index, if_id.data, sizeof(int));
1615  for (dp = downstreams; dp; dp = dp->next) {
1616  if (dp->id == if_index)
1617  break;
1618  }
1619  } else {
1620  if (use_if_id) {
1621  /* Require an interface-id. */
1622  log_info("No interface-id.");
1623  goto cleanup;
1624  }
1625  for (dp = downstreams; dp; dp = dp->next) {
1626  /* Get the first matching one. */
1627  if (!memcmp(&dp->link.sin6_addr,
1628  &packet->dhcpv6_link_address,
1629  sizeof(struct in6_addr)))
1630  break;
1631  }
1632  }
1633  /* Why bother when there is no choice. */
1634  if (!dp && downstreams && !downstreams->next)
1635  dp = downstreams;
1636  if (!dp) {
1637  log_info("Can't find the down interface.");
1638  goto cleanup;
1639  }
1640  memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
1641  to.sin6_addr = packet->dhcpv6_peer_address;
1642 
1643  /* Check if we should relay the carried message. */
1644  switch (msg->msg_type) {
1645  /* Relay-Reply of for another relay, not a client. */
1646  case DHCPV6_RELAY_REPL:
1647  to.sin6_port = local_port;
1648  /* Fall into: */
1649 
1650  case DHCPV6_ADVERTISE:
1651  case DHCPV6_REPLY:
1652  case DHCPV6_RECONFIGURE:
1653  case DHCPV6_RELAY_FORW:
1655  log_info("Relaying %s to %s port %d down.",
1657  piaddr(peer),
1658  ntohs(to.sin6_port));
1659  break;
1660 
1661  case DHCPV6_SOLICIT:
1662  case DHCPV6_REQUEST:
1663  case DHCPV6_CONFIRM:
1664  case DHCPV6_RENEW:
1665  case DHCPV6_REBIND:
1666  case DHCPV6_RELEASE:
1667  case DHCPV6_DECLINE:
1669  case DHCPV6_LEASEQUERY:
1670  log_info("Discarding %s to %s port %d down.",
1672  piaddr(peer),
1673  ntohs(to.sin6_port));
1674  goto cleanup;
1675 
1676  default:
1677  log_info("Unknown %d type to %s port %d down.",
1678  msg->msg_type,
1679  piaddr(peer),
1680  ntohs(to.sin6_port));
1681  goto cleanup;
1682  }
1683 
1684  /* Send the message to the downstream. */
1685  send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
1686  (size_t) relay_msg.len, &to);
1687 
1688  cleanup:
1689  if (relay_msg.data != NULL)
1690  data_string_forget(&relay_msg, MDL);
1691  if (if_id.data != NULL)
1692  data_string_forget(&if_id, MDL);
1693 }
1694 
1695 /*
1696  * Called by the dispatch packet handler with a decoded packet.
1697  */
1698 void
1699 dhcpv6(struct packet *packet) {
1700  struct stream_list *dp;
1701 
1702  /* Try all relay-replies downwards. */
1703  if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
1704  process_down6(packet);
1705  return;
1706  }
1707  /* Others are candidates to go up if they come from down. */
1708  for (dp = downstreams; dp; dp = dp->next) {
1709  if (packet->interface != dp->ifp)
1710  continue;
1711  process_up6(packet, dp);
1712  return;
1713  }
1714  /* Relay-forward could work from an unknown interface. */
1715  if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1716  process_up6(packet, NULL);
1717  return;
1718  }
1719 
1720  log_info("Can't process packet from interface '%s'.",
1721  packet->interface->name);
1722 }
1723 #endif
1724 
1725 /* Stub routines needed for linking with DHCP libraries. */
1726 void
1727 bootp(struct packet *packet) {
1728  return;
1729 }
1730 
1731 void
1732 dhcp(struct packet *packet) {
1733  return;
1734 }
1735 
1736 void
1737 classify(struct packet *p, struct class *c) {
1738  return;
1739 }
1740 
1741 int
1742 check_collection(struct packet *p, struct lease *l, struct collection *c) {
1743  return 0;
1744 }
1745 
1746 isc_result_t
1747 find_class(struct class **class, const char *c1, const char *c2, int i) {
1748  return ISC_R_NOTFOUND;
1749 }
1750 
1751 int
1752 parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
1753  return 0;
1754 }
1755 
1756 isc_result_t
1758  control_object_state_t newstate) {
1759  if (newstate != server_shutdown)
1760  return ISC_R_SUCCESS;
1761  exit(0);
1762 }
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:507
unsigned len
Definition: tree.h:80
const char * piaddr(const struct iaddr addr)
Definition: inet.c:581
u_int8_t hlen
Definition: dhcpd.h:440
int no_daemon
Definition: dhclient.c:89
void bootp(struct packet *packet)
Definition: dhcrelay.c:1727
char name[IFNAMSIZ]
Definition: dhcpd.h:1267
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:55
int check_collection(struct packet *p, struct lease *l, struct collection *c)
Definition: dhcrelay.c:1742
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:1757
#define DHCP_CONTEXT_PRE_DB
Definition: isclib.h:121
#define DHCRELAY_USAGE
Definition: dhcrelay.c:168
struct in_addr * addresses
Definition: dhcpd.h:1247
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
unsigned len
Definition: inet.h:32
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
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:302
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:40
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:1481
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:1261
#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:93
Definition: ip.h:47
int parse_allow_deny(struct option_cache **oc, struct parse *p, int i)
Definition: dhcrelay.c:1752
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:2291
struct in_addr giaddr
Definition: dhclient.c:72
struct option_cache * lookup_option(struct universe *universe, struct option_state *options, unsigned code)
Definition: options.c:2249
#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:40
u_int32_t flags
Definition: dhcpd.h:1281
void interface_snorf(struct interface_info *tmp, int ir)
Definition: discover.c:1482
void dhcp(struct packet *packet)
Definition: dhcrelay.c:1732
#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:1259
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:925
int local_family
Definition: discover.c:52
int quiet_interface_discovery
Definition: discover.c:42
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:1242
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:1737
int supports_multiple_interfaces(struct interface_info *)
u_int8_t * remote_id
Definition: dhcpd.h:1263
isc_result_t interface_setup()
Definition: discover.c:80
u_int8_t hbuf[HARDWARE_ADDR_LEN+1]
Definition: dhcpd.h:441
int address_count
Definition: dhcpd.h:1250
u_int8_t hops
Definition: dhcp.h:52
int server_packet_errors
Definition: dhcrelay.c:67
#define INTERFACE_UPSTREAM
Definition: dhcpd.h:1286
#define DHCPV6_CONFIRM
Definition: dhcp6.h:101
unsigned remote_id_len
Definition: dhcpd.h:1265
struct server_list * servers
struct iaddr client_addr
Definition: dhcpd.h:390
#define PACKAGE_VERSION
Definition: config.h:150
#define INTERFACE_DOWNSTREAM
Definition: dhcpd.h:1285
option_code_hash_t * code_hash
Definition: tree.h:337
#define _PATH_DHCRELAY_PID
Definition: config.h:241
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 *)
const unsigned char * data
Definition: tree.h:79
#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:1277
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:552
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:1282
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:1747
int log_perror
Definition: errwarn.c:44