1 1.5 christos /* $NetBSD: dhcrelay.c,v 1.6 2022/04/03 01:10:59 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* dhcrelay.c 4 1.1 christos 5 1.1 christos DHCP/BOOTP Relay Agent. */ 6 1.1 christos 7 1.1 christos /* 8 1.6 christos * Copyright(c) 2004-2022 by Internet Systems Consortium, Inc.("ISC") 9 1.1 christos * Copyright(c) 1997-2003 by Internet Software Consortium 10 1.1 christos * 11 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 12 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 13 1.1 christos * file, You can obtain one at http://mozilla.org/MPL/2.0/. 14 1.1 christos * 15 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 16 1.1 christos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 1.1 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 18 1.1 christos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 1.1 christos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 1.1 christos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 21 1.1 christos * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 1.1 christos * 23 1.1 christos * Internet Systems Consortium, Inc. 24 1.6 christos * PO Box 360 25 1.6 christos * Newmarket, NH 03857 USA 26 1.1 christos * <info (at) isc.org> 27 1.1 christos * https://www.isc.org/ 28 1.1 christos * 29 1.1 christos */ 30 1.1 christos 31 1.1 christos #include <sys/cdefs.h> 32 1.5 christos __RCSID("$NetBSD: dhcrelay.c,v 1.6 2022/04/03 01:10:59 christos Exp $"); 33 1.1 christos 34 1.1 christos #include "dhcpd.h" 35 1.1 christos #include <syslog.h> 36 1.1 christos #include <signal.h> 37 1.1 christos #include <sys/time.h> 38 1.1 christos #include <isc/file.h> 39 1.1 christos 40 1.1 christos TIME default_lease_time = 43200; /* 12 hours... */ 41 1.1 christos TIME max_lease_time = 86400; /* 24 hours... */ 42 1.1 christos struct tree_cache *global_options[256]; 43 1.1 christos 44 1.1 christos struct option *requested_opts[2]; 45 1.1 christos 46 1.1 christos /* Needed to prevent linking against conflex.c. */ 47 1.1 christos int lexline; 48 1.1 christos int lexchar; 49 1.1 christos char *token_line; 50 1.1 christos char *tlname; 51 1.1 christos 52 1.1 christos const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID; 53 1.1 christos isc_boolean_t no_dhcrelay_pid = ISC_FALSE; 54 1.1 christos /* False (default) => we write and use a pid file */ 55 1.1 christos isc_boolean_t no_pid_file = ISC_FALSE; 56 1.1 christos 57 1.1 christos int bogus_agent_drops = 0; /* Packets dropped because agent option 58 1.1 christos field was specified and we're not relaying 59 1.1 christos packets that already have an agent option 60 1.1 christos specified. */ 61 1.1 christos int bogus_giaddr_drops = 0; /* Packets sent to us to relay back to a 62 1.1 christos client, but with a bogus giaddr. */ 63 1.1 christos int client_packets_relayed = 0; /* Packets relayed from client to server. */ 64 1.1 christos int server_packet_errors = 0; /* Errors sending packets to servers. */ 65 1.1 christos int server_packets_relayed = 0; /* Packets relayed from server to client. */ 66 1.1 christos int client_packet_errors = 0; /* Errors sending packets to clients. */ 67 1.1 christos 68 1.1 christos int add_agent_options = 0; /* If nonzero, add relay agent options. */ 69 1.1 christos int add_rfc3527_suboption = 0; /* If nonzero, add RFC3527 link selection sub-option. */ 70 1.1 christos 71 1.1 christos int agent_option_errors = 0; /* Number of packets forwarded without 72 1.1 christos agent options because there was no room. */ 73 1.1 christos int drop_agent_mismatches = 0; /* If nonzero, drop server replies that 74 1.1 christos don't have matching circuit-id's. */ 75 1.1 christos int corrupt_agent_options = 0; /* Number of packets dropped because 76 1.1 christos relay agent information option was bad. */ 77 1.1 christos int missing_agent_option = 0; /* Number of packets dropped because no 78 1.1 christos RAI option matching our ID was found. */ 79 1.1 christos int bad_circuit_id = 0; /* Circuit ID option in matching RAI option 80 1.1 christos did not match any known circuit ID. */ 81 1.1 christos int missing_circuit_id = 0; /* Circuit ID option in matching RAI option 82 1.1 christos was missing. */ 83 1.1 christos int max_hop_count = 10; /* Maximum hop count */ 84 1.1 christos 85 1.1 christos int no_daemon = 0; 86 1.1 christos int dfd[2] = { -1, -1 }; 87 1.1 christos 88 1.1 christos #ifdef DHCPv6 89 1.1 christos /* Force use of DHCPv6 interface-id option. */ 90 1.1 christos isc_boolean_t use_if_id = ISC_FALSE; 91 1.1 christos #endif 92 1.1 christos 93 1.1 christos /* Maximum size of a packet with agent options added. */ 94 1.1 christos int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN; 95 1.1 christos 96 1.1 christos /* What to do about packets we're asked to relay that 97 1.1 christos already have a relay option: */ 98 1.1 christos enum { forward_and_append, /* Forward and append our own relay option. */ 99 1.1 christos forward_and_replace, /* Forward, but replace theirs with ours. */ 100 1.1 christos forward_untouched, /* Forward without changes. */ 101 1.1 christos discard } agent_relay_mode = forward_and_replace; 102 1.1 christos 103 1.6 christos extern u_int16_t local_port; 104 1.6 christos extern u_int16_t remote_port; 105 1.1 christos 106 1.1 christos /* Relay agent server list. */ 107 1.1 christos struct server_list { 108 1.1 christos struct server_list *next; 109 1.1 christos struct sockaddr_in to; 110 1.1 christos } *servers; 111 1.1 christos 112 1.1 christos struct interface_info *uplink = NULL; 113 1.6 christos isc_boolean_t use_fake_gw = ISC_FALSE; 114 1.6 christos struct in_addr gw = {0}; 115 1.1 christos 116 1.1 christos #ifdef DHCPv6 117 1.1 christos struct stream_list { 118 1.1 christos struct stream_list *next; 119 1.1 christos struct interface_info *ifp; 120 1.1 christos struct sockaddr_in6 link; 121 1.1 christos int id; 122 1.1 christos } *downstreams, *upstreams; 123 1.1 christos 124 1.3 christos #ifndef UNIT_TEST 125 1.1 christos static struct stream_list *parse_downstream(char *); 126 1.1 christos static struct stream_list *parse_upstream(char *); 127 1.1 christos static void setup_streams(void); 128 1.3 christos #endif /* UNIT_TEST */ 129 1.1 christos 130 1.1 christos /* 131 1.1 christos * A pointer to a subscriber id to add to the message we forward. 132 1.1 christos * This is primarily for testing purposes as we only have one id 133 1.1 christos * for the entire relay and don't determine one per client which 134 1.1 christos * would be more useful. 135 1.1 christos */ 136 1.1 christos char *dhcrelay_sub_id = NULL; 137 1.1 christos #endif 138 1.1 christos 139 1.2 christos libdhcp_callbacks_t dhcrelay_callbacks = { 140 1.2 christos &local_port, 141 1.2 christos &remote_port, 142 1.2 christos classify, 143 1.2 christos check_collection, 144 1.2 christos dhcp, 145 1.2 christos #ifdef DHCPv6 146 1.2 christos dhcpv6, 147 1.2 christos #endif /* DHCPv6 */ 148 1.2 christos bootp, 149 1.2 christos find_class, 150 1.2 christos parse_allow_deny, 151 1.2 christos dhcp_set_control_state, 152 1.2 christos }; 153 1.2 christos 154 1.3 christos #ifndef UNIT_TEST 155 1.1 christos static void do_relay4(struct interface_info *, struct dhcp_packet *, 156 1.1 christos unsigned int, unsigned int, struct iaddr, 157 1.1 christos struct hardware *); 158 1.3 christos #endif /* UNIT_TEST */ 159 1.1 christos 160 1.3 christos extern int add_relay_agent_options(struct interface_info *, 161 1.3 christos struct dhcp_packet *, unsigned, 162 1.3 christos struct in_addr); 163 1.3 christos extern int find_interface_by_agent_option(struct dhcp_packet *, 164 1.3 christos struct interface_info **, u_int8_t *, int); 165 1.3 christos 166 1.3 christos extern int strip_relay_agent_options(struct interface_info *, 167 1.3 christos struct interface_info **, 168 1.3 christos struct dhcp_packet *, unsigned); 169 1.3 christos 170 1.3 christos #ifndef UNIT_TEST 171 1.1 christos static void request_v4_interface(const char* name, int flags); 172 1.1 christos 173 1.1 christos static const char copyright[] = 174 1.6 christos "Copyright 2004-2022 Internet Systems Consortium."; 175 1.1 christos static const char arr[] = "All rights reserved."; 176 1.1 christos static const char message[] = 177 1.1 christos "Internet Systems Consortium DHCP Relay Agent"; 178 1.1 christos static const char url[] = 179 1.1 christos "For info, please visit https://www.isc.org/software/dhcp/"; 180 1.1 christos 181 1.1 christos char *progname; 182 1.1 christos 183 1.1 christos #ifdef DHCPv6 184 1.1 christos #ifdef RELAY_PORT 185 1.1 christos #define DHCRELAY_USAGE \ 186 1.1 christos "Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \ 187 1.1 christos " [-A <length>] [-c <hops>]\n" \ 188 1.1 christos " [-p <port> | -rp <relay-port>]\n" \ 189 1.1 christos " [-pf <pid-file>] [--no-pid]\n"\ 190 1.1 christos " [-m append|replace|forward|discard]\n" \ 191 1.1 christos " [-i interface0 [ ... -i interfaceN]\n" \ 192 1.1 christos " [-iu interface0 [ ... -iu interfaceN]\n" \ 193 1.1 christos " [-id interface0 [ ... -id interfaceN]\n" \ 194 1.6 christos " [-U interface] [-g <ip-address>]\n" \ 195 1.1 christos " server0 [ ... serverN]\n\n" \ 196 1.1 christos " %s -6 [-d] [-q] [-I] [-c <hops>]\n" \ 197 1.1 christos " [-p <port> | -rp <relay-port>]\n" \ 198 1.1 christos " [-pf <pid-file>] [--no-pid]\n" \ 199 1.1 christos " [-s <subscriber-id>]\n" \ 200 1.1 christos " -l lower0 [ ... -l lowerN]\n" \ 201 1.1 christos " -u upper0 [ ... -u upperN]\n" \ 202 1.1 christos " lower (client link): [address%%]interface[#index]\n" \ 203 1.1 christos " upper (server link): [address%%]interface\n\n" \ 204 1.1 christos " %s {--version|--help|-h}" 205 1.1 christos #else 206 1.1 christos #define DHCRELAY_USAGE \ 207 1.1 christos "Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \ 208 1.1 christos " [-A <length>] [-c <hops>] [-p <port>]\n" \ 209 1.1 christos " [-pf <pid-file>] [--no-pid]\n"\ 210 1.1 christos " [-m append|replace|forward|discard]\n" \ 211 1.1 christos " [-i interface0 [ ... -i interfaceN]\n" \ 212 1.1 christos " [-iu interface0 [ ... -iu interfaceN]\n" \ 213 1.1 christos " [-id interface0 [ ... -id interfaceN]\n" \ 214 1.6 christos " [-U interface] [-g <ip-address>]\n" \ 215 1.1 christos " server0 [ ... serverN]\n\n" \ 216 1.1 christos " %s -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \ 217 1.1 christos " [-pf <pid-file>] [--no-pid]\n" \ 218 1.1 christos " [-s <subscriber-id>]\n" \ 219 1.1 christos " -l lower0 [ ... -l lowerN]\n" \ 220 1.1 christos " -u upper0 [ ... -u upperN]\n" \ 221 1.1 christos " lower (client link): [address%%]interface[#index]\n" \ 222 1.1 christos " upper (server link): [address%%]interface\n\n" \ 223 1.1 christos " %s {--version|--help|-h}" 224 1.1 christos #endif 225 1.1 christos #else /* !DHCPv6 */ 226 1.1 christos #ifdef RELAY_PORT 227 1.1 christos #define DHCRELAY_USAGE \ 228 1.1 christos "Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>]\n" \ 229 1.1 christos " [-p <port> | -rp <relay-port>]\n" \ 230 1.1 christos " [-pf <pid-file>] [--no-pid]\n" \ 231 1.1 christos " [-m append|replace|forward|discard]\n" \ 232 1.1 christos " [-i interface0 [ ... -i interfaceN]\n" \ 233 1.1 christos " [-iu interface0 [ ... -iu interfaceN]\n" \ 234 1.1 christos " [-id interface0 [ ... -id interfaceN]\n" \ 235 1.6 christos " [-U interface] [-g <ip-address>]\n" \ 236 1.1 christos " server0 [ ... serverN]\n\n" \ 237 1.1 christos " %s {--version|--help|-h}" 238 1.1 christos #else 239 1.1 christos #define DHCRELAY_USAGE \ 240 1.1 christos "Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \ 241 1.1 christos " [-pf <pid-file>] [--no-pid]\n" \ 242 1.1 christos " [-m append|replace|forward|discard]\n" \ 243 1.1 christos " [-i interface0 [ ... -i interfaceN]\n" \ 244 1.1 christos " [-iu interface0 [ ... -iu interfaceN]\n" \ 245 1.1 christos " [-id interface0 [ ... -id interfaceN]\n" \ 246 1.6 christos " [-U interface] [-g <ip-address>]\n" \ 247 1.1 christos " server0 [ ... serverN]\n\n" \ 248 1.1 christos " %s {--version|--help|-h}" 249 1.1 christos #endif 250 1.1 christos #endif 251 1.1 christos 252 1.1 christos /*! 253 1.1 christos * 254 1.1 christos * \brief Print the generic usage message 255 1.1 christos * 256 1.1 christos * If the user has provided an incorrect command line print out 257 1.1 christos * the description of the command line. The arguments provide 258 1.1 christos * a way for the caller to request more specific information about 259 1.1 christos * the error be printed as well. Mostly this will be that some 260 1.6 christos * command doesn't include its argument. 261 1.1 christos * 262 1.1 christos * \param sfmt - The basic string and format for the specific error 263 1.6 christos * \param sarg - Generally the offending argument from the command line. 264 1.1 christos * 265 1.1 christos * \return Nothing 266 1.1 christos */ 267 1.1 christos 268 1.1 christos #include <sys/cdefs.h> 269 1.5 christos __RCSID("$NetBSD: dhcrelay.c,v 1.6 2022/04/03 01:10:59 christos Exp $"); 270 1.1 christos static const char use_noarg[] = "No argument for command: %s"; 271 1.1 christos #ifdef RELAY_PORT 272 1.1 christos static const char use_port_defined[] = "Port already set, %s inappropriate"; 273 1.1 christos #if !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE) 274 1.1 christos static const char bpf_sock_support[] = "Only LPF and BPF are supported: %s"; 275 1.1 christos #endif 276 1.1 christos #endif 277 1.1 christos #ifdef DHCPv6 278 1.1 christos static const char use_badproto[] = "Protocol already set, %s inappropriate"; 279 1.1 christos static const char use_v4command[] = "Command not used for DHCPv6: %s"; 280 1.1 christos static const char use_v6command[] = "Command not used for DHCPv4: %s"; 281 1.1 christos #endif 282 1.1 christos 283 1.1 christos static void 284 1.1 christos usage(const char *sfmt, const char *sarg) { 285 1.1 christos log_info("%s %s", message, PACKAGE_VERSION); 286 1.1 christos log_info(copyright); 287 1.1 christos log_info(arr); 288 1.1 christos log_info(url); 289 1.1 christos 290 1.1 christos /* If desired print out the specific error message */ 291 1.1 christos #ifdef PRINT_SPECIFIC_CL_ERRORS 292 1.1 christos if (sfmt != NULL) 293 1.1 christos log_error(sfmt, sarg); 294 1.1 christos #endif 295 1.1 christos 296 1.1 christos log_fatal(DHCRELAY_USAGE, 297 1.1 christos #ifdef DHCPv6 298 1.1 christos isc_file_basename(progname), 299 1.1 christos #endif 300 1.1 christos isc_file_basename(progname), 301 1.1 christos isc_file_basename(progname)); 302 1.1 christos } 303 1.1 christos 304 1.3 christos int 305 1.1 christos main(int argc, char **argv) { 306 1.1 christos isc_result_t status; 307 1.1 christos struct servent *ent; 308 1.1 christos struct server_list *sp = NULL; 309 1.1 christos char *service_local = NULL, *service_remote = NULL; 310 1.1 christos u_int16_t port_local = 0, port_remote = 0; 311 1.1 christos int quiet = 0; 312 1.1 christos int fd; 313 1.1 christos int i; 314 1.1 christos #ifdef RELAY_PORT 315 1.1 christos int port_defined = 0; 316 1.1 christos #endif 317 1.1 christos #ifdef DHCPv6 318 1.1 christos struct stream_list *sl = NULL; 319 1.1 christos int local_family_set = 0; 320 1.1 christos #endif 321 1.1 christos 322 1.4 christos libdhcp_callbacks_register(&dhcrelay_callbacks); 323 1.4 christos 324 1.1 christos #ifdef OLD_LOG_NAME 325 1.1 christos progname = "dhcrelay"; 326 1.1 christos #else 327 1.1 christos progname = argv[0]; 328 1.1 christos #endif 329 1.1 christos 330 1.1 christos /* Make sure that file descriptors 0(stdin), 1,(stdout), and 331 1.1 christos 2(stderr) are open. To do this, we assume that when we 332 1.1 christos open a file the lowest available file descriptor is used. */ 333 1.1 christos fd = open("/dev/null", O_RDWR); 334 1.1 christos if (fd == 0) 335 1.1 christos fd = open("/dev/null", O_RDWR); 336 1.1 christos if (fd == 1) 337 1.1 christos fd = open("/dev/null", O_RDWR); 338 1.1 christos if (fd == 2) 339 1.1 christos log_perror = 0; /* No sense logging to /dev/null. */ 340 1.1 christos else if (fd != -1) 341 1.1 christos close(fd); 342 1.1 christos 343 1.1 christos openlog(isc_file_basename(progname), DHCP_LOG_OPTIONS, LOG_DAEMON); 344 1.1 christos 345 1.1 christos #if !defined(DEBUG) 346 1.1 christos setlogmask(LOG_UPTO(LOG_INFO)); 347 1.3 christos #endif 348 1.1 christos 349 1.1 christos /* Parse arguments changing no_daemon */ 350 1.1 christos for (i = 1; i < argc; i++) { 351 1.1 christos if (!strcmp(argv[i], "-d")) { 352 1.1 christos no_daemon = 1; 353 1.1 christos } else if (!strcmp(argv[i], "--version")) { 354 1.1 christos log_info("isc-dhcrelay-%s", PACKAGE_VERSION); 355 1.1 christos exit(0); 356 1.1 christos } else if (!strcmp(argv[i], "--help") || 357 1.1 christos !strcmp(argv[i], "-h")) { 358 1.1 christos log_info(DHCRELAY_USAGE, 359 1.1 christos #ifdef DHCPv6 360 1.1 christos isc_file_basename(progname), 361 1.1 christos #endif 362 1.1 christos isc_file_basename(progname), 363 1.1 christos isc_file_basename(progname)); 364 1.1 christos exit(0); 365 1.1 christos } 366 1.1 christos } 367 1.1 christos /* When not forbidden prepare to become a daemon */ 368 1.1 christos if (!no_daemon) { 369 1.1 christos int pid; 370 1.1 christos 371 1.1 christos if (pipe(dfd) == -1) 372 1.1 christos log_fatal("Can't get pipe: %m"); 373 1.1 christos if ((pid = fork ()) < 0) 374 1.1 christos log_fatal("Can't fork daemon: %m"); 375 1.1 christos if (pid != 0) { 376 1.1 christos /* Parent: wait for the child to start */ 377 1.1 christos int n; 378 1.1 christos 379 1.1 christos (void) close(dfd[1]); 380 1.1 christos do { 381 1.1 christos char buf; 382 1.1 christos 383 1.1 christos n = read(dfd[0], &buf, 1); 384 1.1 christos if (n == 1) 385 1.1 christos _exit(0); 386 1.1 christos } while (n == -1 && errno == EINTR); 387 1.1 christos _exit(1); 388 1.1 christos } 389 1.1 christos /* Child */ 390 1.1 christos (void) close(dfd[0]); 391 1.1 christos } 392 1.1 christos 393 1.1 christos 394 1.1 christos /* Set up the isc and dns library managers */ 395 1.1 christos status = dhcp_context_create(DHCP_CONTEXT_PRE_DB, NULL, NULL); 396 1.1 christos if (status != ISC_R_SUCCESS) 397 1.1 christos log_fatal("Can't initialize context: %s", 398 1.1 christos isc_result_totext(status)); 399 1.1 christos 400 1.1 christos /* Set up the OMAPI. */ 401 1.1 christos status = omapi_init(); 402 1.1 christos if (status != ISC_R_SUCCESS) 403 1.1 christos log_fatal("Can't initialize OMAPI: %s", 404 1.1 christos isc_result_totext(status)); 405 1.1 christos 406 1.1 christos /* Set up the OMAPI wrappers for the interface object. */ 407 1.1 christos interface_setup(); 408 1.1 christos 409 1.1 christos for (i = 1; i < argc; i++) { 410 1.1 christos if (!strcmp(argv[i], "-4")) { 411 1.1 christos #ifdef DHCPv6 412 1.1 christos if (local_family_set && (local_family == AF_INET6)) { 413 1.1 christos usage(use_badproto, "-4"); 414 1.1 christos } 415 1.1 christos local_family_set = 1; 416 1.1 christos local_family = AF_INET; 417 1.1 christos } else if (!strcmp(argv[i], "-6")) { 418 1.1 christos if (local_family_set && (local_family == AF_INET)) { 419 1.1 christos usage(use_badproto, "-6"); 420 1.1 christos } 421 1.1 christos local_family_set = 1; 422 1.1 christos local_family = AF_INET6; 423 1.1 christos #endif 424 1.1 christos } else if (!strcmp(argv[i], "-d")) { 425 1.1 christos /* no_daemon = 1; */ 426 1.1 christos } else if (!strcmp(argv[i], "-q")) { 427 1.1 christos quiet = 1; 428 1.1 christos quiet_interface_discovery = 1; 429 1.1 christos } else if (!strcmp(argv[i], "-p")) { 430 1.1 christos if (++i == argc) 431 1.1 christos usage(use_noarg, argv[i-1]); 432 1.1 christos #ifdef RELAY_PORT 433 1.1 christos if (port_defined) 434 1.1 christos usage(use_port_defined, argv[i-1]); 435 1.1 christos port_defined = 1; 436 1.1 christos #endif 437 1.1 christos local_port = validate_port(argv[i]); 438 1.1 christos log_debug("binding to user-specified port %d", 439 1.1 christos ntohs(local_port)); 440 1.1 christos #ifdef RELAY_PORT 441 1.1 christos } else if (!strcmp(argv[i], "-rp")) { 442 1.1 christos if (++i == argc) 443 1.1 christos usage(use_noarg, argv[i-1]); 444 1.1 christos if (port_defined) 445 1.1 christos usage(use_port_defined, argv[i-1]); 446 1.1 christos port_defined = 1; 447 1.1 christos relay_port = validate_port(argv[i]); 448 1.1 christos log_debug("binding to user-specified relay port %d", 449 1.1 christos ntohs(relay_port)); 450 1.1 christos add_agent_options = 1; 451 1.1 christos #endif 452 1.1 christos } else if (!strcmp(argv[i], "-c")) { 453 1.1 christos int hcount; 454 1.1 christos if (++i == argc) 455 1.1 christos usage(use_noarg, argv[i-1]); 456 1.1 christos hcount = atoi(argv[i]); 457 1.1 christos if (hcount <= 255) 458 1.1 christos max_hop_count= hcount; 459 1.1 christos else 460 1.1 christos usage("Bad hop count to -c: %s", argv[i]); 461 1.1 christos } else if (!strcmp(argv[i], "-i")) { 462 1.1 christos #ifdef DHCPv6 463 1.1 christos if (local_family_set && (local_family == AF_INET6)) { 464 1.1 christos usage(use_v4command, argv[i]); 465 1.1 christos } 466 1.1 christos local_family_set = 1; 467 1.1 christos local_family = AF_INET; 468 1.1 christos #endif 469 1.1 christos if (++i == argc) { 470 1.1 christos usage(use_noarg, argv[i-1]); 471 1.1 christos } 472 1.1 christos 473 1.1 christos request_v4_interface(argv[i], INTERFACE_STREAMS); 474 1.1 christos } else if (!strcmp(argv[i], "-iu")) { 475 1.1 christos #ifdef DHCPv6 476 1.1 christos if (local_family_set && (local_family == AF_INET6)) { 477 1.1 christos usage(use_v4command, argv[i]); 478 1.1 christos } 479 1.1 christos local_family_set = 1; 480 1.1 christos local_family = AF_INET; 481 1.1 christos #endif 482 1.1 christos if (++i == argc) { 483 1.1 christos usage(use_noarg, argv[i-1]); 484 1.1 christos } 485 1.1 christos 486 1.1 christos request_v4_interface(argv[i], INTERFACE_UPSTREAM); 487 1.1 christos } else if (!strcmp(argv[i], "-id")) { 488 1.1 christos #ifdef DHCPv6 489 1.1 christos if (local_family_set && (local_family == AF_INET6)) { 490 1.1 christos usage(use_v4command, argv[i]); 491 1.1 christos } 492 1.1 christos local_family_set = 1; 493 1.1 christos local_family = AF_INET; 494 1.1 christos #endif 495 1.1 christos if (++i == argc) { 496 1.1 christos usage(use_noarg, argv[i-1]); 497 1.1 christos } 498 1.1 christos 499 1.1 christos request_v4_interface(argv[i], INTERFACE_DOWNSTREAM); 500 1.1 christos } else if (!strcmp(argv[i], "-a")) { 501 1.1 christos #ifdef DHCPv6 502 1.1 christos if (local_family_set && (local_family == AF_INET6)) { 503 1.1 christos usage(use_v4command, argv[i]); 504 1.1 christos } 505 1.1 christos local_family_set = 1; 506 1.1 christos local_family = AF_INET; 507 1.1 christos #endif 508 1.1 christos add_agent_options = 1; 509 1.1 christos } else if (!strcmp(argv[i], "-A")) { 510 1.1 christos #ifdef DHCPv6 511 1.1 christos if (local_family_set && (local_family == AF_INET6)) { 512 1.1 christos usage(use_v4command, argv[i]); 513 1.1 christos } 514 1.1 christos local_family_set = 1; 515 1.1 christos local_family = AF_INET; 516 1.1 christos #endif 517 1.1 christos if (++i == argc) 518 1.1 christos usage(use_noarg, argv[i-1]); 519 1.1 christos 520 1.1 christos dhcp_max_agent_option_packet_length = atoi(argv[i]); 521 1.1 christos 522 1.1 christos if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX) 523 1.1 christos log_fatal("%s: packet length exceeds " 524 1.1 christos "longest possible MTU\n", 525 1.1 christos argv[i]); 526 1.1 christos } else if (!strcmp(argv[i], "-m")) { 527 1.1 christos #ifdef DHCPv6 528 1.1 christos if (local_family_set && (local_family == AF_INET6)) { 529 1.1 christos usage(use_v4command, argv[i]); 530 1.1 christos } 531 1.1 christos local_family_set = 1; 532 1.1 christos local_family = AF_INET; 533 1.1 christos #endif 534 1.1 christos if (++i == argc) 535 1.1 christos usage(use_noarg, argv[i-1]); 536 1.1 christos if (!strcasecmp(argv[i], "append")) { 537 1.1 christos agent_relay_mode = forward_and_append; 538 1.1 christos } else if (!strcasecmp(argv[i], "replace")) { 539 1.1 christos agent_relay_mode = forward_and_replace; 540 1.1 christos } else if (!strcasecmp(argv[i], "forward")) { 541 1.1 christos agent_relay_mode = forward_untouched; 542 1.1 christos } else if (!strcasecmp(argv[i], "discard")) { 543 1.1 christos agent_relay_mode = discard; 544 1.1 christos } else 545 1.1 christos usage("Unknown argument to -m: %s", argv[i]); 546 1.1 christos } else if (!strcmp(argv [i], "-U")) { 547 1.1 christos if (++i == argc) 548 1.1 christos usage(use_noarg, argv[i-1]); 549 1.1 christos 550 1.1 christos if (uplink) { 551 1.1 christos usage("more than one uplink (-U) specified: %s" 552 1.1 christos ,argv[i]); 553 1.1 christos } 554 1.1 christos 555 1.1 christos /* Allocate the uplink interface */ 556 1.1 christos status = interface_allocate(&uplink, MDL); 557 1.1 christos if (status != ISC_R_SUCCESS) { 558 1.1 christos log_fatal("%s: uplink interface_allocate: %s", 559 1.1 christos argv[i], isc_result_totext(status)); 560 1.1 christos } 561 1.3 christos 562 1.1 christos if (strlen(argv[i]) >= sizeof(uplink->name)) { 563 1.1 christos log_fatal("%s: uplink name too long," 564 1.1 christos " it cannot exceed: %ld characters", 565 1.1 christos argv[i], (long)(sizeof(uplink->name) - 1)); 566 1.1 christos } 567 1.1 christos 568 1.1 christos uplink->name[sizeof(uplink->name) - 1] = 0x00; 569 1.1 christos strncpy(uplink->name, argv[i], 570 1.1 christos sizeof(uplink->name) - 1); 571 1.1 christos interface_snorf(uplink, (INTERFACE_REQUESTED | 572 1.1 christos INTERFACE_STREAMS)); 573 1.1 christos 574 1.1 christos /* Turn on -a, in case they don't do so explicitly */ 575 1.1 christos add_agent_options = 1; 576 1.1 christos add_rfc3527_suboption = 1; 577 1.6 christos } else if (!strcmp(argv[i], "-g")) { 578 1.6 christos if (++i == argc) 579 1.6 christos usage(use_noarg, argv[i-1]); 580 1.6 christos #ifdef DHCPv6 581 1.6 christos if (local_family_set && (local_family == AF_INET6)) { 582 1.6 christos usage(use_v4command, argv[i]); 583 1.6 christos } 584 1.6 christos local_family_set = 1; 585 1.6 christos local_family = AF_INET; 586 1.6 christos #endif 587 1.6 christos if (inet_pton(AF_INET, argv[i], &gw) <= 0) { 588 1.6 christos usage("Invalid gateway address '%s'", argv[i]); 589 1.6 christos } else { 590 1.6 christos use_fake_gw = ISC_TRUE; 591 1.6 christos } 592 1.1 christos } else if (!strcmp(argv[i], "-D")) { 593 1.1 christos #ifdef DHCPv6 594 1.1 christos if (local_family_set && (local_family == AF_INET6)) { 595 1.1 christos usage(use_v4command, argv[i]); 596 1.1 christos } 597 1.1 christos local_family_set = 1; 598 1.1 christos local_family = AF_INET; 599 1.1 christos #endif 600 1.1 christos drop_agent_mismatches = 1; 601 1.1 christos #ifdef DHCPv6 602 1.1 christos } else if (!strcmp(argv[i], "-I")) { 603 1.1 christos if (local_family_set && (local_family == AF_INET)) { 604 1.1 christos usage(use_v6command, argv[i]); 605 1.1 christos } 606 1.1 christos local_family_set = 1; 607 1.1 christos local_family = AF_INET6; 608 1.1 christos use_if_id = ISC_TRUE; 609 1.1 christos } else if (!strcmp(argv[i], "-l")) { 610 1.1 christos if (local_family_set && (local_family == AF_INET)) { 611 1.1 christos usage(use_v6command, argv[i]); 612 1.1 christos } 613 1.1 christos local_family_set = 1; 614 1.1 christos local_family = AF_INET6; 615 1.1 christos if (downstreams != NULL) 616 1.1 christos use_if_id = ISC_TRUE; 617 1.1 christos if (++i == argc) 618 1.1 christos usage(use_noarg, argv[i-1]); 619 1.1 christos sl = parse_downstream(argv[i]); 620 1.1 christos sl->next = downstreams; 621 1.1 christos downstreams = sl; 622 1.1 christos } else if (!strcmp(argv[i], "-u")) { 623 1.1 christos if (local_family_set && (local_family == AF_INET)) { 624 1.1 christos usage(use_v6command, argv[i]); 625 1.1 christos } 626 1.1 christos local_family_set = 1; 627 1.1 christos local_family = AF_INET6; 628 1.1 christos if (++i == argc) 629 1.1 christos usage(use_noarg, argv[i-1]); 630 1.1 christos sl = parse_upstream(argv[i]); 631 1.1 christos sl->next = upstreams; 632 1.1 christos upstreams = sl; 633 1.1 christos } else if (!strcmp(argv[i], "-s")) { 634 1.1 christos if (local_family_set && (local_family == AF_INET)) { 635 1.1 christos usage(use_v6command, argv[i]); 636 1.1 christos } 637 1.1 christos local_family_set = 1; 638 1.1 christos local_family = AF_INET6; 639 1.1 christos if (++i == argc) 640 1.1 christos usage(use_noarg, argv[i-1]); 641 1.1 christos dhcrelay_sub_id = argv[i]; 642 1.1 christos #endif 643 1.1 christos } else if (!strcmp(argv[i], "-pf")) { 644 1.1 christos if (++i == argc) 645 1.1 christos usage(use_noarg, argv[i-1]); 646 1.1 christos path_dhcrelay_pid = argv[i]; 647 1.1 christos no_dhcrelay_pid = ISC_TRUE; 648 1.1 christos } else if (!strcmp(argv[i], "--no-pid")) { 649 1.1 christos no_pid_file = ISC_TRUE; 650 1.1 christos } else if (argv[i][0] == '-') { 651 1.1 christos usage("Unknown command: %s", argv[i]); 652 1.1 christos } else { 653 1.1 christos struct hostent *he; 654 1.1 christos struct in_addr ia, *iap = NULL; 655 1.1 christos 656 1.1 christos #ifdef DHCPv6 657 1.1 christos if (local_family_set && (local_family == AF_INET6)) { 658 1.1 christos usage(use_v4command, argv[i]); 659 1.1 christos } 660 1.1 christos local_family_set = 1; 661 1.1 christos local_family = AF_INET; 662 1.1 christos #endif 663 1.1 christos if (inet_aton(argv[i], &ia)) { 664 1.1 christos iap = &ia; 665 1.1 christos } else { 666 1.1 christos he = gethostbyname(argv[i]); 667 1.1 christos if (!he) { 668 1.1 christos log_error("%s: host unknown", argv[i]); 669 1.1 christos } else { 670 1.1 christos iap = ((struct in_addr *) 671 1.1 christos he->h_addr_list[0]); 672 1.1 christos } 673 1.1 christos } 674 1.1 christos 675 1.1 christos if (iap) { 676 1.1 christos sp = ((struct server_list *) 677 1.1 christos dmalloc(sizeof *sp, MDL)); 678 1.1 christos if (!sp) 679 1.1 christos log_fatal("no memory for server.\n"); 680 1.1 christos sp->next = servers; 681 1.1 christos servers = sp; 682 1.1 christos memcpy(&sp->to.sin_addr, iap, sizeof *iap); 683 1.1 christos } 684 1.1 christos } 685 1.1 christos } 686 1.1 christos 687 1.1 christos #if defined(RELAY_PORT) && \ 688 1.1 christos !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE) 689 1.1 christos if (relay_port && (local_family == AF_INET)) 690 1.1 christos usage(bpf_sock_support, "-rp"); 691 1.1 christos #endif 692 1.1 christos 693 1.1 christos /* 694 1.1 christos * If the user didn't specify a pid file directly 695 1.1 christos * find one from environment variables or defaults 696 1.1 christos */ 697 1.1 christos if (no_dhcrelay_pid == ISC_FALSE) { 698 1.1 christos if (local_family == AF_INET) { 699 1.1 christos path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID"); 700 1.1 christos if (path_dhcrelay_pid == NULL) 701 1.1 christos path_dhcrelay_pid = _PATH_DHCRELAY_PID; 702 1.1 christos } 703 1.1 christos #ifdef DHCPv6 704 1.1 christos else { 705 1.1 christos path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID"); 706 1.1 christos if (path_dhcrelay_pid == NULL) 707 1.1 christos path_dhcrelay_pid = _PATH_DHCRELAY6_PID; 708 1.1 christos } 709 1.1 christos #endif 710 1.1 christos } 711 1.1 christos 712 1.1 christos if (!quiet) { 713 1.1 christos log_info("%s %s", message, PACKAGE_VERSION); 714 1.1 christos log_info(copyright); 715 1.1 christos log_info(arr); 716 1.1 christos log_info(url); 717 1.3 christos } else 718 1.1 christos log_perror = 0; 719 1.1 christos 720 1.1 christos /* Set default port */ 721 1.1 christos if (local_family == AF_INET) { 722 1.1 christos service_local = "bootps"; 723 1.1 christos service_remote = "bootpc"; 724 1.1 christos port_local = htons(67); 725 1.1 christos port_remote = htons(68); 726 1.1 christos } 727 1.1 christos #ifdef DHCPv6 728 1.1 christos else { 729 1.1 christos service_local = "dhcpv6-server"; 730 1.1 christos service_remote = "dhcpv6-client"; 731 1.1 christos port_local = htons(547); 732 1.1 christos port_remote = htons(546); 733 1.1 christos } 734 1.1 christos #endif 735 1.1 christos 736 1.1 christos if (!local_port) { 737 1.1 christos ent = getservbyname(service_local, "udp"); 738 1.1 christos if (ent) 739 1.1 christos local_port = ent->s_port; 740 1.1 christos else 741 1.1 christos local_port = port_local; 742 1.1 christos 743 1.1 christos ent = getservbyname(service_remote, "udp"); 744 1.1 christos if (ent) 745 1.1 christos remote_port = ent->s_port; 746 1.1 christos else 747 1.1 christos remote_port = port_remote; 748 1.1 christos 749 1.1 christos endservent(); 750 1.1 christos } 751 1.1 christos 752 1.1 christos if (local_family == AF_INET) { 753 1.1 christos /* We need at least one server */ 754 1.1 christos if (servers == NULL) { 755 1.1 christos log_fatal("No servers specified."); 756 1.1 christos } 757 1.1 christos 758 1.1 christos 759 1.1 christos /* Set up the server sockaddrs. */ 760 1.1 christos for (sp = servers; sp; sp = sp->next) { 761 1.1 christos sp->to.sin_port = local_port; 762 1.1 christos sp->to.sin_family = AF_INET; 763 1.1 christos #ifdef HAVE_SA_LEN 764 1.1 christos sp->to.sin_len = sizeof sp->to; 765 1.1 christos #endif 766 1.1 christos } 767 1.1 christos } 768 1.1 christos #ifdef DHCPv6 769 1.1 christos else { 770 1.1 christos unsigned code; 771 1.1 christos 772 1.1 christos /* We need at least one upstream and one downstream interface */ 773 1.1 christos if (upstreams == NULL || downstreams == NULL) { 774 1.1 christos log_info("Must specify at least one lower " 775 1.1 christos "and one upper interface.\n"); 776 1.1 christos usage(NULL, NULL); 777 1.1 christos } 778 1.1 christos 779 1.1 christos /* Set up the initial dhcp option universe. */ 780 1.1 christos initialize_common_option_spaces(); 781 1.1 christos 782 1.1 christos /* Check requested options. */ 783 1.1 christos code = D6O_RELAY_MSG; 784 1.1 christos if (!option_code_hash_lookup(&requested_opts[0], 785 1.1 christos dhcpv6_universe.code_hash, 786 1.1 christos &code, 0, MDL)) 787 1.1 christos log_fatal("Unable to find the RELAY_MSG " 788 1.1 christos "option definition."); 789 1.1 christos code = D6O_INTERFACE_ID; 790 1.1 christos if (!option_code_hash_lookup(&requested_opts[1], 791 1.1 christos dhcpv6_universe.code_hash, 792 1.1 christos &code, 0, MDL)) 793 1.1 christos log_fatal("Unable to find the INTERFACE_ID " 794 1.1 christos "option definition."); 795 1.1 christos } 796 1.1 christos #endif 797 1.1 christos 798 1.1 christos /* Become a daemon... */ 799 1.1 christos if (!no_daemon) { 800 1.1 christos char buf = 0; 801 1.1 christos FILE *pf; 802 1.1 christos int pfdesc; 803 1.1 christos 804 1.1 christos log_perror = 0; 805 1.1 christos 806 1.1 christos /* Signal parent we started successfully. */ 807 1.1 christos if (dfd[0] != -1 && dfd[1] != -1) { 808 1.1 christos if (write(dfd[1], &buf, 1) != 1) 809 1.1 christos log_fatal("write to parent: %m"); 810 1.1 christos (void) close(dfd[1]); 811 1.1 christos dfd[0] = dfd[1] = -1; 812 1.1 christos } 813 1.1 christos 814 1.1 christos /* Create the pid file. */ 815 1.1 christos if (no_pid_file == ISC_FALSE) { 816 1.1 christos pfdesc = open(path_dhcrelay_pid, 817 1.1 christos O_CREAT | O_TRUNC | O_WRONLY, 0644); 818 1.1 christos 819 1.1 christos if (pfdesc < 0) { 820 1.1 christos log_error("Can't create %s: %m", 821 1.1 christos path_dhcrelay_pid); 822 1.1 christos } else { 823 1.1 christos pf = fdopen(pfdesc, "w"); 824 1.1 christos if (!pf) 825 1.1 christos log_error("Can't fdopen %s: %m", 826 1.1 christos path_dhcrelay_pid); 827 1.1 christos else { 828 1.1 christos fprintf(pf, "%ld\n",(long)getpid()); 829 1.1 christos fclose(pf); 830 1.3 christos } 831 1.1 christos } 832 1.1 christos } 833 1.1 christos 834 1.1 christos (void) close(0); 835 1.1 christos (void) close(1); 836 1.1 christos (void) close(2); 837 1.1 christos (void) setsid(); 838 1.1 christos 839 1.1 christos IGNORE_RET (chdir("/")); 840 1.1 christos } 841 1.1 christos 842 1.2 christos /* Set up the isc and dns library managers */ 843 1.2 christos status = dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, 844 1.2 christos NULL, NULL); 845 1.2 christos if (status != ISC_R_SUCCESS) 846 1.2 christos log_fatal("Can't initialize context: %s", 847 1.2 christos isc_result_totext(status)); 848 1.2 christos 849 1.2 christos /* Get the current time... */ 850 1.2 christos gettimeofday(&cur_tv, NULL); 851 1.2 christos 852 1.2 christos /* Discover all the network interfaces. */ 853 1.2 christos discover_interfaces(DISCOVER_RELAY); 854 1.2 christos 855 1.2 christos #ifdef DHCPv6 856 1.2 christos if (local_family == AF_INET6) 857 1.2 christos setup_streams(); 858 1.2 christos #endif 859 1.2 christos 860 1.1 christos /* Set up the packet handler... */ 861 1.1 christos if (local_family == AF_INET) 862 1.1 christos bootp_packet_handler = do_relay4; 863 1.1 christos #ifdef DHCPv6 864 1.1 christos else 865 1.1 christos dhcpv6_packet_handler = do_packet6; 866 1.1 christos #endif 867 1.1 christos 868 1.1 christos #if defined(ENABLE_GENTLE_SHUTDOWN) 869 1.1 christos /* no signal handlers until we deal with the side effects */ 870 1.1 christos /* install signal handlers */ 871 1.1 christos signal(SIGINT, dhcp_signal_handler); /* control-c */ 872 1.1 christos signal(SIGTERM, dhcp_signal_handler); /* kill */ 873 1.1 christos #endif 874 1.1 christos 875 1.1 christos /* Start dispatching packets and timeouts... */ 876 1.1 christos dispatch(); 877 1.1 christos 878 1.1 christos /* In fact dispatch() never returns. */ 879 1.1 christos return (0); 880 1.1 christos } 881 1.1 christos 882 1.1 christos static void 883 1.1 christos do_relay4(struct interface_info *ip, struct dhcp_packet *packet, 884 1.1 christos unsigned int length, unsigned int from_port, struct iaddr from, 885 1.1 christos struct hardware *hfrom) { 886 1.1 christos struct server_list *sp; 887 1.1 christos struct sockaddr_in to; 888 1.1 christos struct interface_info *out; 889 1.1 christos struct hardware hto, *htop; 890 1.1 christos 891 1.1 christos if (packet->hlen > sizeof packet->chaddr) { 892 1.1 christos log_info("Discarding packet with invalid hlen, received on " 893 1.1 christos "%s interface.", ip->name); 894 1.1 christos return; 895 1.1 christos } 896 1.1 christos if (ip->address_count < 1 || ip->addresses == NULL) { 897 1.1 christos log_info("Discarding packet received on %s interface that " 898 1.1 christos "has no IPv4 address assigned.", ip->name); 899 1.1 christos return; 900 1.1 christos } 901 1.1 christos 902 1.1 christos /* Find the interface that corresponds to the giaddr 903 1.1 christos in the packet. */ 904 1.1 christos if (packet->giaddr.s_addr) { 905 1.1 christos for (out = interfaces; out; out = out->next) { 906 1.1 christos int i; 907 1.1 christos 908 1.1 christos for (i = 0 ; i < out->address_count ; i++ ) { 909 1.1 christos if (out->addresses[i].s_addr == 910 1.1 christos packet->giaddr.s_addr) { 911 1.1 christos i = -1; 912 1.1 christos break; 913 1.1 christos } 914 1.1 christos } 915 1.1 christos 916 1.1 christos if (i == -1) 917 1.1 christos break; 918 1.1 christos } 919 1.1 christos } else { 920 1.1 christos out = NULL; 921 1.1 christos } 922 1.1 christos 923 1.1 christos /* If it's a bootreply, forward it to the client. */ 924 1.1 christos if (packet->op == BOOTREPLY) { 925 1.1 christos if (!(ip->flags & INTERFACE_UPSTREAM)) { 926 1.1 christos log_debug("Dropping reply received on %s", ip->name); 927 1.1 christos return; 928 1.1 christos } 929 1.1 christos 930 1.6 christos log_debug("BOOTREPLY giaddr: %s\n", inet_ntoa(packet->giaddr)); 931 1.1 christos if (!(packet->flags & htons(BOOTP_BROADCAST)) && 932 1.1 christos can_unicast_without_arp(out)) { 933 1.1 christos to.sin_addr = packet->yiaddr; 934 1.1 christos to.sin_port = remote_port; 935 1.1 christos 936 1.1 christos /* and hardware address is not broadcast */ 937 1.1 christos htop = &hto; 938 1.1 christos } else { 939 1.1 christos to.sin_addr.s_addr = htonl(INADDR_BROADCAST); 940 1.1 christos to.sin_port = remote_port; 941 1.1 christos 942 1.1 christos /* hardware address is broadcast */ 943 1.1 christos htop = NULL; 944 1.1 christos } 945 1.1 christos to.sin_family = AF_INET; 946 1.1 christos #ifdef HAVE_SA_LEN 947 1.1 christos to.sin_len = sizeof to; 948 1.1 christos #endif 949 1.1 christos 950 1.1 christos memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen); 951 1.1 christos hto.hbuf[0] = packet->htype; 952 1.1 christos hto.hlen = packet->hlen + 1; 953 1.1 christos 954 1.1 christos /* Wipe out the agent relay options and, if possible, figure 955 1.1 christos out which interface to use based on the contents of the 956 1.1 christos option that we put on the request to which the server is 957 1.1 christos replying. */ 958 1.1 christos if (!(length = 959 1.1 christos strip_relay_agent_options(ip, &out, packet, length))) 960 1.1 christos return; 961 1.1 christos 962 1.1 christos if (!out) { 963 1.1 christos log_error("Packet to bogus giaddr %s.\n", 964 1.1 christos inet_ntoa(packet->giaddr)); 965 1.1 christos ++bogus_giaddr_drops; 966 1.1 christos return; 967 1.1 christos } 968 1.1 christos 969 1.6 christos if (use_fake_gw) { 970 1.6 christos packet->giaddr = gw; 971 1.6 christos } 972 1.6 christos 973 1.1 christos if (send_packet(out, NULL, packet, length, out->addresses[0], 974 1.1 christos &to, htop) < 0) { 975 1.1 christos ++server_packet_errors; 976 1.1 christos } else { 977 1.1 christos log_debug("Forwarded BOOTREPLY for %s to %s", 978 1.1 christos print_hw_addr(packet->htype, packet->hlen, 979 1.1 christos packet->chaddr), 980 1.1 christos inet_ntoa(to.sin_addr)); 981 1.1 christos 982 1.1 christos ++server_packets_relayed; 983 1.1 christos } 984 1.1 christos return; 985 1.1 christos } 986 1.1 christos 987 1.1 christos /* If giaddr matches one of our addresses, ignore the packet - 988 1.1 christos we just sent it. */ 989 1.1 christos if (out) 990 1.1 christos return; 991 1.1 christos 992 1.1 christos if (!(ip->flags & INTERFACE_DOWNSTREAM)) { 993 1.1 christos log_debug("Dropping request received on %s", ip->name); 994 1.1 christos return; 995 1.1 christos } 996 1.1 christos 997 1.1 christos /* Add relay agent options if indicated. If something goes wrong, 998 1.1 christos * drop the packet. Note this may set packet->giaddr if RFC3527 999 1.1 christos * is enabled. */ 1000 1.1 christos if (!(length = add_relay_agent_options(ip, packet, length, 1001 1.1 christos ip->addresses[0]))) 1002 1.1 christos return; 1003 1.1 christos 1004 1.1 christos /* If giaddr is not already set, Set it so the server can 1005 1.1 christos figure out what net it's from and so that we can later 1006 1.1 christos forward the response to the correct net. If it's already 1007 1.1 christos set, the response will be sent directly to the relay agent 1008 1.1 christos that set giaddr, so we won't see it. */ 1009 1.1 christos if (!packet->giaddr.s_addr) 1010 1.1 christos packet->giaddr = ip->addresses[0]; 1011 1.1 christos if (packet->hops < max_hop_count) 1012 1.1 christos packet->hops = packet->hops + 1; 1013 1.1 christos else 1014 1.1 christos return; 1015 1.1 christos 1016 1.1 christos /* Otherwise, it's a BOOTREQUEST, so forward it to all the 1017 1.1 christos servers. */ 1018 1.1 christos for (sp = servers; sp; sp = sp->next) { 1019 1.1 christos if (send_packet((fallback_interface 1020 1.1 christos ? fallback_interface : interfaces), 1021 1.1 christos NULL, packet, length, ip->addresses[0], 1022 1.1 christos &sp->to, NULL) < 0) { 1023 1.1 christos ++client_packet_errors; 1024 1.1 christos } else { 1025 1.1 christos log_debug("Forwarded BOOTREQUEST for %s to %s", 1026 1.1 christos print_hw_addr(packet->htype, packet->hlen, 1027 1.1 christos packet->chaddr), 1028 1.1 christos inet_ntoa(sp->to.sin_addr)); 1029 1.1 christos ++client_packets_relayed; 1030 1.1 christos } 1031 1.1 christos } 1032 1.3 christos 1033 1.1 christos } 1034 1.1 christos 1035 1.3 christos #endif /* UNIT_TEST */ 1036 1.3 christos 1037 1.1 christos /* Strip any Relay Agent Information options from the DHCP packet 1038 1.1 christos option buffer. If there is a circuit ID suboption, look up the 1039 1.1 christos outgoing interface based upon it. */ 1040 1.1 christos 1041 1.3 christos int 1042 1.1 christos strip_relay_agent_options(struct interface_info *in, 1043 1.1 christos struct interface_info **out, 1044 1.1 christos struct dhcp_packet *packet, 1045 1.1 christos unsigned length) { 1046 1.1 christos int is_dhcp = 0; 1047 1.1 christos u_int8_t *op, *nextop, *sp, *max; 1048 1.1 christos int good_agent_option = 0; 1049 1.1 christos int status; 1050 1.1 christos 1051 1.1 christos /* If we're not adding agent options to packets, we're not taking 1052 1.1 christos them out either. */ 1053 1.1 christos if (!add_agent_options) 1054 1.1 christos return (length); 1055 1.1 christos 1056 1.1 christos /* If there's no cookie, it's a bootp packet, so we should just 1057 1.1 christos forward it unchanged. */ 1058 1.1 christos if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4)) 1059 1.1 christos return (length); 1060 1.1 christos 1061 1.1 christos max = ((u_int8_t *)packet) + length; 1062 1.1 christos sp = op = &packet->options[4]; 1063 1.1 christos 1064 1.1 christos while (op < max) { 1065 1.1 christos switch(*op) { 1066 1.1 christos /* Skip padding... */ 1067 1.1 christos case DHO_PAD: 1068 1.1 christos if (sp != op) 1069 1.1 christos *sp = *op; 1070 1.1 christos ++op; 1071 1.1 christos ++sp; 1072 1.1 christos continue; 1073 1.1 christos 1074 1.1 christos /* If we see a message type, it's a DHCP packet. */ 1075 1.1 christos case DHO_DHCP_MESSAGE_TYPE: 1076 1.1 christos is_dhcp = 1; 1077 1.1 christos goto skip; 1078 1.1 christos break; 1079 1.1 christos 1080 1.1 christos /* Quit immediately if we hit an End option. */ 1081 1.1 christos case DHO_END: 1082 1.1 christos if (sp != op) 1083 1.1 christos *sp++ = *op++; 1084 1.1 christos goto out; 1085 1.1 christos 1086 1.1 christos case DHO_DHCP_AGENT_OPTIONS: 1087 1.1 christos /* We shouldn't see a relay agent option in a 1088 1.1 christos packet before we've seen the DHCP packet type, 1089 1.1 christos but if we do, we have to leave it alone. */ 1090 1.1 christos if (!is_dhcp) 1091 1.1 christos goto skip; 1092 1.1 christos 1093 1.1 christos /* Do not process an agent option if it exceeds the 1094 1.1 christos * buffer. Fail this packet. 1095 1.1 christos */ 1096 1.1 christos nextop = op + op[1] + 2; 1097 1.1 christos if (nextop > max) 1098 1.1 christos return (0); 1099 1.1 christos 1100 1.1 christos status = find_interface_by_agent_option(packet, 1101 1.1 christos out, op + 2, 1102 1.1 christos op[1]); 1103 1.1 christos if (status == -1 && drop_agent_mismatches) 1104 1.1 christos return (0); 1105 1.1 christos if (status) 1106 1.1 christos good_agent_option = 1; 1107 1.1 christos op = nextop; 1108 1.1 christos break; 1109 1.1 christos 1110 1.1 christos skip: 1111 1.1 christos /* Skip over other options. */ 1112 1.1 christos default: 1113 1.1 christos /* Fail if processing this option will exceed the 1114 1.1 christos * buffer(op[1] is malformed). 1115 1.1 christos */ 1116 1.1 christos nextop = op + op[1] + 2; 1117 1.1 christos if (nextop > max) 1118 1.1 christos return (0); 1119 1.1 christos 1120 1.1 christos if (sp != op) { 1121 1.3 christos size_t mlen = op[1] + 2; 1122 1.3 christos memmove(sp, op, mlen); 1123 1.3 christos sp += mlen; 1124 1.3 christos if (sp > max) { 1125 1.3 christos return (0); 1126 1.3 christos } 1127 1.3 christos 1128 1.1 christos op = nextop; 1129 1.1 christos } else 1130 1.1 christos op = sp = nextop; 1131 1.1 christos 1132 1.1 christos break; 1133 1.1 christos } 1134 1.1 christos } 1135 1.1 christos out: 1136 1.1 christos 1137 1.1 christos /* If it's not a DHCP packet, we're not supposed to touch it. */ 1138 1.1 christos if (!is_dhcp) 1139 1.1 christos return (length); 1140 1.1 christos 1141 1.1 christos /* If none of the agent options we found matched, or if we didn't 1142 1.1 christos find any agent options, count this packet as not having any 1143 1.1 christos matching agent options, and if we're relying on agent options 1144 1.1 christos to determine the outgoing interface, drop the packet. */ 1145 1.1 christos 1146 1.1 christos if (!good_agent_option) { 1147 1.1 christos ++missing_agent_option; 1148 1.1 christos if (drop_agent_mismatches) 1149 1.1 christos return (0); 1150 1.1 christos } 1151 1.1 christos 1152 1.1 christos /* Adjust the length... */ 1153 1.1 christos if (sp != op) { 1154 1.1 christos length = sp -((u_int8_t *)packet); 1155 1.1 christos 1156 1.1 christos /* Make sure the packet isn't short(this is unlikely, 1157 1.1 christos but WTH) */ 1158 1.1 christos if (length < BOOTP_MIN_LEN) { 1159 1.1 christos memset(sp, DHO_PAD, BOOTP_MIN_LEN - length); 1160 1.1 christos length = BOOTP_MIN_LEN; 1161 1.1 christos } 1162 1.1 christos } 1163 1.1 christos return (length); 1164 1.1 christos } 1165 1.1 christos 1166 1.1 christos 1167 1.1 christos /* Find an interface that matches the circuit ID specified in the 1168 1.1 christos Relay Agent Information option. If one is found, store it through 1169 1.1 christos the pointer given; otherwise, leave the existing pointer alone. 1170 1.1 christos 1171 1.1 christos We actually deviate somewhat from the current specification here: 1172 1.1 christos if the option buffer is corrupt, we suggest that the caller not 1173 1.1 christos respond to this packet. If the circuit ID doesn't match any known 1174 1.1 christos interface, we suggest that the caller to drop the packet. Only if 1175 1.1 christos we find a circuit ID that matches an existing interface do we tell 1176 1.1 christos the caller to go ahead and process the packet. */ 1177 1.1 christos 1178 1.3 christos int 1179 1.1 christos find_interface_by_agent_option(struct dhcp_packet *packet, 1180 1.1 christos struct interface_info **out, 1181 1.1 christos u_int8_t *buf, int len) { 1182 1.1 christos int i = 0; 1183 1.1 christos u_int8_t *circuit_id = 0; 1184 1.1 christos unsigned circuit_id_len = 0; 1185 1.1 christos struct interface_info *ip; 1186 1.1 christos 1187 1.1 christos while (i < len) { 1188 1.1 christos /* If the next agent option overflows the end of the 1189 1.1 christos packet, the agent option buffer is corrupt. */ 1190 1.1 christos if (i + 1 == len || 1191 1.1 christos i + buf[i + 1] + 2 > len) { 1192 1.1 christos ++corrupt_agent_options; 1193 1.1 christos return (-1); 1194 1.1 christos } 1195 1.1 christos switch(buf[i]) { 1196 1.1 christos /* Remember where the circuit ID is... */ 1197 1.1 christos case RAI_CIRCUIT_ID: 1198 1.1 christos circuit_id = &buf[i + 2]; 1199 1.1 christos circuit_id_len = buf[i + 1]; 1200 1.1 christos i += circuit_id_len + 2; 1201 1.1 christos continue; 1202 1.1 christos 1203 1.1 christos default: 1204 1.1 christos i += buf[i + 1] + 2; 1205 1.1 christos break; 1206 1.1 christos } 1207 1.1 christos } 1208 1.1 christos 1209 1.1 christos /* If there's no circuit ID, it's not really ours, tell the caller 1210 1.1 christos it's no good. */ 1211 1.1 christos if (!circuit_id) { 1212 1.1 christos ++missing_circuit_id; 1213 1.1 christos return (-1); 1214 1.1 christos } 1215 1.1 christos 1216 1.1 christos /* Scan the interface list looking for an interface whose 1217 1.1 christos name matches the one specified in circuit_id. */ 1218 1.1 christos 1219 1.1 christos for (ip = interfaces; ip; ip = ip->next) { 1220 1.1 christos if (ip->circuit_id && 1221 1.1 christos ip->circuit_id_len == circuit_id_len && 1222 1.1 christos !memcmp(ip->circuit_id, circuit_id, circuit_id_len)) 1223 1.1 christos break; 1224 1.1 christos } 1225 1.1 christos 1226 1.1 christos /* If we got a match, use it. */ 1227 1.1 christos if (ip) { 1228 1.1 christos *out = ip; 1229 1.1 christos return (1); 1230 1.1 christos } 1231 1.1 christos 1232 1.1 christos /* If we didn't get a match, the circuit ID was bogus. */ 1233 1.1 christos ++bad_circuit_id; 1234 1.1 christos return (-1); 1235 1.1 christos } 1236 1.1 christos 1237 1.1 christos /* 1238 1.1 christos * Examine a packet to see if it's a candidate to have a Relay 1239 1.1 christos * Agent Information option tacked onto its tail. If it is, tack 1240 1.1 christos * the option on. 1241 1.1 christos */ 1242 1.3 christos int 1243 1.1 christos add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet, 1244 1.1 christos unsigned length, struct in_addr giaddr) { 1245 1.1 christos int is_dhcp = 0, mms; 1246 1.1 christos unsigned optlen; 1247 1.1 christos u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL; 1248 1.1 christos int adding_link_select; 1249 1.1 christos 1250 1.1 christos /* If we're not adding agent options to packets, we can skip 1251 1.1 christos this. */ 1252 1.1 christos if (!add_agent_options) 1253 1.1 christos return (length); 1254 1.1 christos 1255 1.1 christos /* If there's no cookie, it's a bootp packet, so we should just 1256 1.1 christos forward it unchanged. */ 1257 1.1 christos if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4)) 1258 1.1 christos return (length); 1259 1.1 christos 1260 1.1 christos max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length; 1261 1.1 christos 1262 1.1 christos /* Add link selection suboption if enabled and we're the first relay */ 1263 1.1 christos adding_link_select = (add_rfc3527_suboption 1264 1.1 christos && (packet->giaddr.s_addr == 0)); 1265 1.1 christos 1266 1.1 christos /* Commence processing after the cookie. */ 1267 1.1 christos sp = op = &packet->options[4]; 1268 1.1 christos 1269 1.1 christos while (op < max) { 1270 1.1 christos switch(*op) { 1271 1.1 christos /* Skip padding... */ 1272 1.1 christos case DHO_PAD: 1273 1.1 christos /* Remember the first pad byte so we can commandeer 1274 1.1 christos * padded space. 1275 1.1 christos * 1276 1.1 christos * XXX: Is this really a good idea? Sure, we can 1277 1.1 christos * seemingly reduce the packet while we're looking, 1278 1.1 christos * but if the packet was signed by the client then 1279 1.1 christos * this padding is part of the checksum(RFC3118), 1280 1.1 christos * and its nonpresence would break authentication. 1281 1.1 christos */ 1282 1.1 christos if (end_pad == NULL) 1283 1.1 christos end_pad = sp; 1284 1.1 christos 1285 1.1 christos if (sp != op) 1286 1.1 christos *sp++ = *op++; 1287 1.1 christos else 1288 1.1 christos sp = ++op; 1289 1.1 christos 1290 1.1 christos continue; 1291 1.1 christos 1292 1.1 christos /* If we see a message type, it's a DHCP packet. */ 1293 1.1 christos case DHO_DHCP_MESSAGE_TYPE: 1294 1.1 christos is_dhcp = 1; 1295 1.1 christos goto skip; 1296 1.1 christos 1297 1.1 christos /* 1298 1.1 christos * If there's a maximum message size option, we 1299 1.1 christos * should pay attention to it 1300 1.1 christos */ 1301 1.1 christos case DHO_DHCP_MAX_MESSAGE_SIZE: 1302 1.1 christos mms = ntohs(*(op + 2)); 1303 1.1 christos if (mms < dhcp_max_agent_option_packet_length && 1304 1.1 christos mms >= DHCP_MTU_MIN) 1305 1.1 christos max = ((u_int8_t *)packet) + mms; 1306 1.1 christos goto skip; 1307 1.1 christos 1308 1.1 christos /* Quit immediately if we hit an End option. */ 1309 1.1 christos case DHO_END: 1310 1.1 christos goto out; 1311 1.1 christos 1312 1.1 christos case DHO_DHCP_AGENT_OPTIONS: 1313 1.1 christos /* We shouldn't see a relay agent option in a 1314 1.1 christos packet before we've seen the DHCP packet type, 1315 1.1 christos but if we do, we have to leave it alone. */ 1316 1.1 christos if (!is_dhcp) 1317 1.1 christos goto skip; 1318 1.1 christos 1319 1.1 christos end_pad = NULL; 1320 1.1 christos 1321 1.1 christos /* There's already a Relay Agent Information option 1322 1.1 christos in this packet. How embarrassing. Decide what 1323 1.1 christos to do based on the mode the user specified. */ 1324 1.1 christos 1325 1.1 christos switch(agent_relay_mode) { 1326 1.1 christos case forward_and_append: 1327 1.1 christos goto skip; 1328 1.1 christos case forward_untouched: 1329 1.1 christos return (length); 1330 1.1 christos case discard: 1331 1.1 christos return (0); 1332 1.1 christos case forward_and_replace: 1333 1.1 christos default: 1334 1.1 christos break; 1335 1.1 christos } 1336 1.1 christos 1337 1.1 christos /* Skip over the agent option and start copying 1338 1.1 christos if we aren't copying already. */ 1339 1.1 christos op += op[1] + 2; 1340 1.1 christos break; 1341 1.1 christos 1342 1.1 christos skip: 1343 1.1 christos /* Skip over other options. */ 1344 1.1 christos default: 1345 1.1 christos /* Fail if processing this option will exceed the 1346 1.1 christos * buffer(op[1] is malformed). 1347 1.1 christos */ 1348 1.1 christos nextop = op + op[1] + 2; 1349 1.1 christos if (nextop > max) 1350 1.1 christos return (0); 1351 1.1 christos 1352 1.1 christos end_pad = NULL; 1353 1.1 christos 1354 1.1 christos if (sp != op) { 1355 1.3 christos size_t mlen = op[1] + 2; 1356 1.3 christos memmove(sp, op, mlen); 1357 1.3 christos sp += mlen; 1358 1.3 christos if (sp > max) { 1359 1.3 christos return (0); 1360 1.3 christos } 1361 1.3 christos 1362 1.1 christos op = nextop; 1363 1.1 christos } else 1364 1.1 christos op = sp = nextop; 1365 1.1 christos 1366 1.1 christos break; 1367 1.1 christos } 1368 1.1 christos } 1369 1.1 christos out: 1370 1.1 christos 1371 1.1 christos /* If it's not a DHCP packet, we're not supposed to touch it. */ 1372 1.1 christos if (!is_dhcp) 1373 1.1 christos return (length); 1374 1.1 christos 1375 1.1 christos /* If the packet was padded out, we can store the agent option 1376 1.1 christos at the beginning of the padding. */ 1377 1.1 christos 1378 1.1 christos if (end_pad != NULL) 1379 1.1 christos sp = end_pad; 1380 1.1 christos 1381 1.1 christos #if 0 1382 1.1 christos /* Remember where the end of the packet was after parsing 1383 1.1 christos it. */ 1384 1.1 christos op = sp; 1385 1.1 christos #endif 1386 1.1 christos 1387 1.1 christos /* Sanity check. Had better not ever happen. */ 1388 1.1 christos if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1)) 1389 1.1 christos log_fatal("Circuit ID length %d out of range [1-255] on " 1390 1.1 christos "%s\n", ip->circuit_id_len, ip->name); 1391 1.1 christos optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */ 1392 1.1 christos 1393 1.1 christos if (ip->remote_id) { 1394 1.1 christos if (ip->remote_id_len > 255 || ip->remote_id_len < 1) 1395 1.1 christos log_fatal("Remote ID length %d out of range [1-255] " 1396 1.1 christos "on %s\n", ip->remote_id_len, ip->name); 1397 1.1 christos optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */ 1398 1.1 christos } 1399 1.1 christos 1400 1.1 christos if (adding_link_select) { 1401 1.1 christos optlen += 6; 1402 1.1 christos } 1403 1.1 christos 1404 1.1 christos #ifdef RELAY_PORT 1405 1.1 christos if (relay_port) { 1406 1.1 christos optlen += 2; 1407 1.1 christos } 1408 1.1 christos #endif 1409 1.1 christos 1410 1.1 christos /* We do not support relay option fragmenting(multiple options to 1411 1.1 christos * support an option data exceeding 255 bytes). 1412 1.1 christos */ 1413 1.1 christos if ((optlen < 3) ||(optlen > 255)) 1414 1.1 christos log_fatal("Total agent option length(%u) out of range " 1415 1.1 christos "[3 - 255] on %s\n", optlen, ip->name); 1416 1.1 christos 1417 1.1 christos /* 1418 1.1 christos * Is there room for the option, its code+len, and DHO_END? 1419 1.1 christos * If not, forward without adding the option. 1420 1.1 christos */ 1421 1.1 christos if (max - sp >= optlen + 3) { 1422 1.1 christos log_debug("Adding %d-byte relay agent option", optlen + 3); 1423 1.1 christos 1424 1.1 christos /* Okay, cons up *our* Relay Agent Information option. */ 1425 1.1 christos *sp++ = DHO_DHCP_AGENT_OPTIONS; 1426 1.1 christos *sp++ = optlen; 1427 1.1 christos 1428 1.1 christos /* Copy in the circuit id... */ 1429 1.1 christos *sp++ = RAI_CIRCUIT_ID; 1430 1.1 christos *sp++ = ip->circuit_id_len; 1431 1.1 christos memcpy(sp, ip->circuit_id, ip->circuit_id_len); 1432 1.1 christos sp += ip->circuit_id_len; 1433 1.1 christos 1434 1.1 christos /* Copy in remote ID... */ 1435 1.1 christos if (ip->remote_id) { 1436 1.1 christos *sp++ = RAI_REMOTE_ID; 1437 1.1 christos *sp++ = ip->remote_id_len; 1438 1.1 christos memcpy(sp, ip->remote_id, ip->remote_id_len); 1439 1.1 christos sp += ip->remote_id_len; 1440 1.1 christos } 1441 1.1 christos 1442 1.1 christos /* RFC3527: Use the inbound packet's interface address in 1443 1.1 christos * the link selection suboption and set the outbound giaddr 1444 1.1 christos * to the uplink address. */ 1445 1.1 christos if (adding_link_select) { 1446 1.1 christos *sp++ = RAI_LINK_SELECT; 1447 1.1 christos *sp++ = 4u; 1448 1.1 christos memcpy(sp, &giaddr.s_addr, 4); 1449 1.1 christos sp += 4; 1450 1.1 christos packet->giaddr = uplink->addresses[0]; 1451 1.1 christos log_debug ("Adding link selection suboption" 1452 1.1 christos " with addr: %s", inet_ntoa(giaddr)); 1453 1.1 christos } 1454 1.1 christos 1455 1.1 christos #ifdef RELAY_PORT 1456 1.1 christos /* draft-ietf-dhc-relay-port-10.txt section 5.1 */ 1457 1.1 christos if (relay_port) { 1458 1.1 christos *sp++ = RAI_RELAY_PORT; 1459 1.1 christos *sp++ = 0u; 1460 1.1 christos } 1461 1.1 christos #endif 1462 1.1 christos } else { 1463 1.1 christos ++agent_option_errors; 1464 1.1 christos log_error("No room in packet (used %d of %d) " 1465 1.1 christos "for %d-byte relay agent option: omitted", 1466 1.1 christos (int) (sp - ((u_int8_t *) packet)), 1467 1.1 christos (int) (max - ((u_int8_t *) packet)), 1468 1.1 christos optlen + 3); 1469 1.1 christos } 1470 1.1 christos 1471 1.1 christos /* 1472 1.1 christos * Deposit an END option unless the packet is full (shouldn't 1473 1.1 christos * be possible). 1474 1.1 christos */ 1475 1.1 christos if (sp < max) 1476 1.1 christos *sp++ = DHO_END; 1477 1.1 christos 1478 1.1 christos /* Recalculate total packet length. */ 1479 1.1 christos length = sp -((u_int8_t *)packet); 1480 1.1 christos 1481 1.1 christos /* Make sure the packet isn't short(this is unlikely, but WTH) */ 1482 1.1 christos if (length < BOOTP_MIN_LEN) { 1483 1.1 christos memset(sp, DHO_PAD, BOOTP_MIN_LEN - length); 1484 1.1 christos return (BOOTP_MIN_LEN); 1485 1.1 christos } 1486 1.1 christos 1487 1.1 christos return (length); 1488 1.1 christos } 1489 1.1 christos 1490 1.6 christos #ifdef DHCPv6 1491 1.5 christos #ifndef UNIT_TEST 1492 1.1 christos /* 1493 1.1 christos * Parse a downstream argument: [address%]interface[#index]. 1494 1.1 christos */ 1495 1.1 christos static struct stream_list * 1496 1.1 christos parse_downstream(char *arg) { 1497 1.1 christos struct stream_list *dp, *up; 1498 1.1 christos struct interface_info *ifp = NULL; 1499 1.1 christos char *ifname, *addr, *iid; 1500 1.1 christos isc_result_t status; 1501 1.1 christos 1502 1.1 christos if (!supports_multiple_interfaces(ifp) && 1503 1.1 christos (downstreams != NULL)) 1504 1.1 christos log_fatal("No support for multiple interfaces."); 1505 1.1 christos 1506 1.1 christos /* Decode the argument. */ 1507 1.1 christos ifname = strchr(arg, '%'); 1508 1.1 christos if (ifname == NULL) { 1509 1.1 christos ifname = arg; 1510 1.1 christos addr = NULL; 1511 1.1 christos } else { 1512 1.1 christos *ifname++ = '\0'; 1513 1.1 christos addr = arg; 1514 1.1 christos } 1515 1.1 christos iid = strchr(ifname, '#'); 1516 1.1 christos if (iid != NULL) { 1517 1.1 christos *iid++ = '\0'; 1518 1.1 christos } 1519 1.1 christos if (strlen(ifname) >= sizeof(ifp->name)) { 1520 1.1 christos usage("Interface name '%s' too long", ifname); 1521 1.1 christos } 1522 1.1 christos 1523 1.1 christos /* Don't declare twice. */ 1524 1.1 christos for (dp = downstreams; dp; dp = dp->next) { 1525 1.1 christos if (strcmp(ifname, dp->ifp->name) == 0) 1526 1.1 christos log_fatal("Down interface '%s' declared twice.", 1527 1.1 christos ifname); 1528 1.1 christos } 1529 1.1 christos 1530 1.1 christos /* Share with up side? */ 1531 1.1 christos for (up = upstreams; up; up = up->next) { 1532 1.1 christos if (strcmp(ifname, up->ifp->name) == 0) { 1533 1.1 christos log_info("parse_downstream: Interface '%s' is " 1534 1.1 christos "both down and up.", ifname); 1535 1.1 christos ifp = up->ifp; 1536 1.1 christos break; 1537 1.1 christos } 1538 1.1 christos } 1539 1.1 christos 1540 1.1 christos /* New interface. */ 1541 1.1 christos if (ifp == NULL) { 1542 1.1 christos status = interface_allocate(&ifp, MDL); 1543 1.1 christos if (status != ISC_R_SUCCESS) 1544 1.1 christos log_fatal("%s: interface_allocate: %s", 1545 1.1 christos arg, isc_result_totext(status)); 1546 1.1 christos strcpy(ifp->name, ifname); 1547 1.1 christos if (interfaces) { 1548 1.1 christos interface_reference(&ifp->next, interfaces, MDL); 1549 1.1 christos interface_dereference(&interfaces, MDL); 1550 1.1 christos } 1551 1.1 christos interface_reference(&interfaces, ifp, MDL); 1552 1.1 christos } 1553 1.1 christos ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM; 1554 1.1 christos 1555 1.1 christos /* New downstream. */ 1556 1.1 christos dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL); 1557 1.1 christos if (!dp) 1558 1.1 christos log_fatal("No memory for downstream."); 1559 1.1 christos dp->ifp = ifp; 1560 1.1 christos if (iid != NULL) { 1561 1.1 christos dp->id = atoi(iid); 1562 1.1 christos } else { 1563 1.1 christos dp->id = -1; 1564 1.1 christos } 1565 1.1 christos /* !addr case handled by setup. */ 1566 1.1 christos if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0)) 1567 1.1 christos log_fatal("Bad link address '%s'", addr); 1568 1.1 christos 1569 1.1 christos return dp; 1570 1.1 christos } 1571 1.1 christos 1572 1.1 christos /* 1573 1.1 christos * Parse an upstream argument: [address]%interface. 1574 1.1 christos */ 1575 1.1 christos static struct stream_list * 1576 1.1 christos parse_upstream(char *arg) { 1577 1.1 christos struct stream_list *up, *dp; 1578 1.1 christos struct interface_info *ifp = NULL; 1579 1.1 christos char *ifname, *addr; 1580 1.1 christos isc_result_t status; 1581 1.1 christos 1582 1.1 christos /* Decode the argument. */ 1583 1.1 christos ifname = strchr(arg, '%'); 1584 1.1 christos if (ifname == NULL) { 1585 1.1 christos ifname = arg; 1586 1.1 christos addr = All_DHCP_Servers; 1587 1.1 christos } else { 1588 1.1 christos *ifname++ = '\0'; 1589 1.1 christos addr = arg; 1590 1.1 christos } 1591 1.1 christos if (strlen(ifname) >= sizeof(ifp->name)) { 1592 1.1 christos log_fatal("Interface name '%s' too long", ifname); 1593 1.1 christos } 1594 1.1 christos 1595 1.1 christos /* Shared up interface? */ 1596 1.1 christos for (up = upstreams; up; up = up->next) { 1597 1.1 christos if (strcmp(ifname, up->ifp->name) == 0) { 1598 1.1 christos ifp = up->ifp; 1599 1.1 christos break; 1600 1.1 christos } 1601 1.1 christos } 1602 1.1 christos for (dp = downstreams; dp; dp = dp->next) { 1603 1.1 christos if (strcmp(ifname, dp->ifp->name) == 0) { 1604 1.1 christos log_info("parse_upstream: Interface '%s' is " 1605 1.1 christos "both down and up.", ifname); 1606 1.1 christos ifp = dp->ifp; 1607 1.1 christos break; 1608 1.1 christos } 1609 1.1 christos } 1610 1.1 christos 1611 1.1 christos /* New interface. */ 1612 1.1 christos if (ifp == NULL) { 1613 1.1 christos status = interface_allocate(&ifp, MDL); 1614 1.1 christos if (status != ISC_R_SUCCESS) 1615 1.1 christos log_fatal("%s: interface_allocate: %s", 1616 1.1 christos arg, isc_result_totext(status)); 1617 1.1 christos strcpy(ifp->name, ifname); 1618 1.1 christos if (interfaces) { 1619 1.1 christos interface_reference(&ifp->next, interfaces, MDL); 1620 1.1 christos interface_dereference(&interfaces, MDL); 1621 1.1 christos } 1622 1.1 christos interface_reference(&interfaces, ifp, MDL); 1623 1.1 christos } 1624 1.1 christos ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM; 1625 1.1 christos 1626 1.1 christos /* New upstream. */ 1627 1.1 christos up = (struct stream_list *) dmalloc(sizeof(*up), MDL); 1628 1.1 christos if (up == NULL) 1629 1.1 christos log_fatal("No memory for upstream."); 1630 1.1 christos 1631 1.1 christos up->ifp = ifp; 1632 1.1 christos 1633 1.1 christos if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0) 1634 1.1 christos log_fatal("Bad address %s", addr); 1635 1.1 christos 1636 1.1 christos return up; 1637 1.1 christos } 1638 1.1 christos 1639 1.1 christos /* 1640 1.1 christos * Setup downstream interfaces. 1641 1.1 christos */ 1642 1.1 christos static void 1643 1.1 christos setup_streams(void) { 1644 1.1 christos struct stream_list *dp, *up; 1645 1.1 christos int i; 1646 1.1 christos isc_boolean_t link_is_set; 1647 1.1 christos 1648 1.1 christos for (dp = downstreams; dp; dp = dp->next) { 1649 1.1 christos /* Check interface */ 1650 1.1 christos if (dp->ifp->v6address_count == 0) 1651 1.1 christos log_fatal("Interface '%s' has no IPv6 addresses.", 1652 1.1 christos dp->ifp->name); 1653 1.1 christos 1654 1.1 christos /* Check/set link. */ 1655 1.1 christos if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr)) 1656 1.1 christos link_is_set = ISC_FALSE; 1657 1.1 christos else 1658 1.1 christos link_is_set = ISC_TRUE; 1659 1.1 christos for (i = 0; i < dp->ifp->v6address_count; i++) { 1660 1.1 christos if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i])) 1661 1.1 christos continue; 1662 1.1 christos if (!link_is_set) 1663 1.1 christos break; 1664 1.1 christos if (!memcmp(&dp->ifp->v6addresses[i], 1665 1.1 christos &dp->link.sin6_addr, 1666 1.1 christos sizeof(dp->link.sin6_addr))) 1667 1.1 christos break; 1668 1.1 christos } 1669 1.1 christos if (i == dp->ifp->v6address_count) 1670 1.1 christos log_fatal("Interface %s does not have global IPv6 " 1671 1.1 christos "address assigned.", dp->ifp->name); 1672 1.1 christos if (!link_is_set) 1673 1.1 christos memcpy(&dp->link.sin6_addr, 1674 1.1 christos &dp->ifp->v6addresses[i], 1675 1.1 christos sizeof(dp->link.sin6_addr)); 1676 1.1 christos 1677 1.1 christos /* Set interface-id. */ 1678 1.1 christos if (dp->id == -1) 1679 1.1 christos dp->id = dp->ifp->index; 1680 1.1 christos } 1681 1.1 christos 1682 1.1 christos for (up = upstreams; up; up = up->next) { 1683 1.1 christos up->link.sin6_port = local_port; 1684 1.1 christos up->link.sin6_family = AF_INET6; 1685 1.1 christos #ifdef HAVE_SA_LEN 1686 1.1 christos up->link.sin6_len = sizeof(up->link); 1687 1.1 christos #endif 1688 1.1 christos 1689 1.1 christos if (up->ifp->v6address_count == 0) 1690 1.1 christos log_fatal("Interface '%s' has no IPv6 addresses.", 1691 1.1 christos up->ifp->name); 1692 1.1 christos 1693 1.1 christos /* RFC 3315 Sec 20 - "If the relay agent relays messages to 1694 1.1 christos * the All_DHCP_Servers address or other multicast addresses, 1695 1.1 christos * it sets the Hop Limit field to 32." */ 1696 1.1 christos if (IN6_IS_ADDR_MULTICAST(&up->link.sin6_addr)) { 1697 1.1 christos set_multicast_hop_limit(up->ifp, HOP_COUNT_LIMIT); 1698 1.1 christos } 1699 1.1 christos } 1700 1.1 christos } 1701 1.1 christos 1702 1.1 christos /* 1703 1.1 christos * Add DHCPv6 agent options here. 1704 1.1 christos */ 1705 1.1 christos static const int required_forw_opts[] = { 1706 1.1 christos D6O_INTERFACE_ID, 1707 1.1 christos D6O_SUBSCRIBER_ID, 1708 1.1 christos #if defined(RELAY_PORT) 1709 1.1 christos D6O_RELAY_SOURCE_PORT, 1710 1.1 christos #endif 1711 1.1 christos D6O_RELAY_MSG, 1712 1.1 christos 0 1713 1.1 christos }; 1714 1.1 christos 1715 1.1 christos /* 1716 1.1 christos * Process a packet upwards, i.e., from client to server. 1717 1.1 christos */ 1718 1.1 christos static void 1719 1.1 christos process_up6(struct packet *packet, struct stream_list *dp) { 1720 1.1 christos char forw_data[65535]; 1721 1.1 christos unsigned cursor; 1722 1.1 christos struct dhcpv6_relay_packet *relay; 1723 1.1 christos struct option_state *opts; 1724 1.1 christos struct stream_list *up; 1725 1.1 christos u_int16_t relay_client_port = 0; 1726 1.1 christos 1727 1.1 christos /* Check if the message should be relayed to the server. */ 1728 1.1 christos switch (packet->dhcpv6_msg_type) { 1729 1.1 christos case DHCPV6_SOLICIT: 1730 1.1 christos case DHCPV6_REQUEST: 1731 1.1 christos case DHCPV6_CONFIRM: 1732 1.1 christos case DHCPV6_RENEW: 1733 1.1 christos case DHCPV6_REBIND: 1734 1.1 christos case DHCPV6_RELEASE: 1735 1.1 christos case DHCPV6_DECLINE: 1736 1.1 christos case DHCPV6_INFORMATION_REQUEST: 1737 1.1 christos case DHCPV6_RELAY_FORW: 1738 1.1 christos case DHCPV6_LEASEQUERY: 1739 1.1 christos case DHCPV6_DHCPV4_QUERY: 1740 1.1 christos log_info("Relaying %s from %s port %d going up.", 1741 1.1 christos dhcpv6_type_names[packet->dhcpv6_msg_type], 1742 1.1 christos piaddr(packet->client_addr), 1743 1.1 christos ntohs(packet->client_port)); 1744 1.1 christos break; 1745 1.1 christos 1746 1.1 christos case DHCPV6_ADVERTISE: 1747 1.1 christos case DHCPV6_REPLY: 1748 1.1 christos case DHCPV6_RECONFIGURE: 1749 1.1 christos case DHCPV6_RELAY_REPL: 1750 1.1 christos case DHCPV6_LEASEQUERY_REPLY: 1751 1.1 christos case DHCPV6_DHCPV4_RESPONSE: 1752 1.1 christos log_info("Discarding %s from %s port %d going up.", 1753 1.1 christos dhcpv6_type_names[packet->dhcpv6_msg_type], 1754 1.1 christos piaddr(packet->client_addr), 1755 1.1 christos ntohs(packet->client_port)); 1756 1.1 christos return; 1757 1.1 christos 1758 1.1 christos default: 1759 1.1 christos log_info("Unknown %d type from %s port %d going up.", 1760 1.1 christos packet->dhcpv6_msg_type, 1761 1.1 christos piaddr(packet->client_addr), 1762 1.1 christos ntohs(packet->client_port)); 1763 1.1 christos return; 1764 1.1 christos } 1765 1.1 christos 1766 1.1 christos /* Build the relay-forward header. */ 1767 1.1 christos relay = (struct dhcpv6_relay_packet *) forw_data; 1768 1.1 christos cursor = offsetof(struct dhcpv6_relay_packet, options); 1769 1.1 christos relay->msg_type = DHCPV6_RELAY_FORW; 1770 1.1 christos if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) { 1771 1.1 christos if (packet->dhcpv6_hop_count >= max_hop_count) { 1772 1.1 christos log_info("Hop count exceeded,"); 1773 1.1 christos return; 1774 1.1 christos } 1775 1.1 christos relay->hop_count = packet->dhcpv6_hop_count + 1; 1776 1.1 christos if (dp) { 1777 1.1 christos memcpy(&relay->link_address, &dp->link.sin6_addr, 16); 1778 1.1 christos } else { 1779 1.1 christos /* On smart relay add: && !global. */ 1780 1.1 christos if (!use_if_id && downstreams->next) { 1781 1.1 christos log_info("Shan't get back the interface."); 1782 1.1 christos return; 1783 1.1 christos } 1784 1.1 christos memset(&relay->link_address, 0, 16); 1785 1.1 christos } 1786 1.1 christos 1787 1.1 christos if (packet->client_port != htons(547)) { 1788 1.1 christos relay_client_port = packet->client_port; 1789 1.1 christos } 1790 1.1 christos } else { 1791 1.1 christos relay->hop_count = 0; 1792 1.1 christos if (!dp) 1793 1.1 christos return; 1794 1.1 christos memcpy(&relay->link_address, &dp->link.sin6_addr, 16); 1795 1.1 christos } 1796 1.1 christos memcpy(&relay->peer_address, packet->client_addr.iabuf, 16); 1797 1.1 christos 1798 1.1 christos /* Get an option state. */ 1799 1.1 christos opts = NULL; 1800 1.1 christos if (!option_state_allocate(&opts, MDL)) { 1801 1.1 christos log_fatal("No memory for upwards options."); 1802 1.1 christos } 1803 1.3 christos 1804 1.1 christos /* Add an interface-id (if used). */ 1805 1.1 christos if (use_if_id) { 1806 1.1 christos int if_id; 1807 1.1 christos 1808 1.1 christos if (dp) { 1809 1.1 christos if_id = dp->id; 1810 1.1 christos } else if (!downstreams->next) { 1811 1.1 christos if_id = downstreams->id; 1812 1.1 christos } else { 1813 1.1 christos log_info("Don't know the interface."); 1814 1.1 christos option_state_dereference(&opts, MDL); 1815 1.1 christos return; 1816 1.1 christos } 1817 1.1 christos 1818 1.1 christos if (!save_option_buffer(&dhcpv6_universe, opts, 1819 1.1 christos NULL, (unsigned char *) &if_id, 1820 1.1 christos sizeof(int), 1821 1.1 christos D6O_INTERFACE_ID, 0)) { 1822 1.1 christos log_error("Can't save interface-id."); 1823 1.1 christos option_state_dereference(&opts, MDL); 1824 1.1 christos return; 1825 1.1 christos } 1826 1.1 christos } 1827 1.1 christos 1828 1.1 christos /* Add a subscriber-id if desired. */ 1829 1.1 christos /* This is for testing rather than general use */ 1830 1.1 christos if (dhcrelay_sub_id != NULL) { 1831 1.1 christos if (!save_option_buffer(&dhcpv6_universe, opts, NULL, 1832 1.1 christos (unsigned char *) dhcrelay_sub_id, 1833 1.1 christos strlen(dhcrelay_sub_id), 1834 1.1 christos D6O_SUBSCRIBER_ID, 0)) { 1835 1.1 christos log_error("Can't save subsriber-id."); 1836 1.1 christos option_state_dereference(&opts, MDL); 1837 1.1 christos return; 1838 1.1 christos } 1839 1.1 christos } 1840 1.3 christos 1841 1.1 christos 1842 1.1 christos #if defined(RELAY_PORT) 1843 1.1 christos /* 1844 1.1 christos * If we use a non-547 UDP source port or if we have received 1845 1.1 christos * from a downstream relay agent uses a non-547 port, we need 1846 1.1 christos * to include the RELAY-SOURCE-PORT option. The "Downstream 1847 1.1 christos * UDP Port" field value in the option allow us to send 1848 1.1 christos * relay-reply message back to the downstream relay agent 1849 1.1 christos * with the correct UDP source port. 1850 1.1 christos */ 1851 1.1 christos if (relay_port || relay_client_port) { 1852 1.1 christos if (!save_option_buffer(&dhcpv6_universe, opts, NULL, 1853 1.1 christos (unsigned char *) &relay_client_port, 1854 1.1 christos sizeof(u_int16_t), 1855 1.1 christos D6O_RELAY_SOURCE_PORT, 0)) { 1856 1.1 christos log_error("Can't save relay-source-port."); 1857 1.1 christos option_state_dereference(&opts, MDL); 1858 1.1 christos return; 1859 1.1 christos } 1860 1.1 christos } 1861 1.1 christos #else 1862 1.1 christos /* Avoid unused but set warning, */ 1863 1.1 christos (void)(relay_client_port); 1864 1.1 christos #endif 1865 1.1 christos 1866 1.1 christos /* Add the relay-msg carrying the packet. */ 1867 1.1 christos if (!save_option_buffer(&dhcpv6_universe, opts, 1868 1.1 christos NULL, (unsigned char *) packet->raw, 1869 1.1 christos packet->packet_length, 1870 1.1 christos D6O_RELAY_MSG, 0)) { 1871 1.1 christos log_error("Can't save relay-msg."); 1872 1.1 christos option_state_dereference(&opts, MDL); 1873 1.1 christos return; 1874 1.1 christos } 1875 1.1 christos 1876 1.1 christos /* Finish the relay-forward message. */ 1877 1.1 christos cursor += store_options6(forw_data + cursor, 1878 1.1 christos sizeof(forw_data) - cursor, 1879 1.3 christos opts, packet, 1880 1.1 christos required_forw_opts, NULL); 1881 1.1 christos option_state_dereference(&opts, MDL); 1882 1.1 christos 1883 1.1 christos /* Send it to all upstreams. */ 1884 1.1 christos for (up = upstreams; up; up = up->next) { 1885 1.1 christos send_packet6(up->ifp, (unsigned char *) forw_data, 1886 1.1 christos (size_t) cursor, &up->link); 1887 1.1 christos } 1888 1.1 christos } 1889 1.3 christos 1890 1.1 christos /* 1891 1.1 christos * Process a packet downwards, i.e., from server to client. 1892 1.1 christos */ 1893 1.1 christos static void 1894 1.1 christos process_down6(struct packet *packet) { 1895 1.1 christos struct stream_list *dp; 1896 1.1 christos struct option_cache *oc; 1897 1.1 christos struct data_string relay_msg; 1898 1.1 christos const struct dhcpv6_packet *msg; 1899 1.1 christos struct data_string if_id; 1900 1.1 christos #if defined(RELAY_PORT) 1901 1.1 christos struct data_string down_port; 1902 1.1 christos #endif 1903 1.1 christos struct sockaddr_in6 to; 1904 1.1 christos struct iaddr peer; 1905 1.1 christos 1906 1.1 christos /* The packet must be a relay-reply message. */ 1907 1.1 christos if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) { 1908 1.1 christos if (packet->dhcpv6_msg_type < dhcpv6_type_name_max) 1909 1.1 christos log_info("Discarding %s from %s port %d going down.", 1910 1.1 christos dhcpv6_type_names[packet->dhcpv6_msg_type], 1911 1.1 christos piaddr(packet->client_addr), 1912 1.1 christos ntohs(packet->client_port)); 1913 1.1 christos else 1914 1.1 christos log_info("Unknown %d type from %s port %d going down.", 1915 1.1 christos packet->dhcpv6_msg_type, 1916 1.1 christos piaddr(packet->client_addr), 1917 1.1 christos ntohs(packet->client_port)); 1918 1.1 christos return; 1919 1.1 christos } 1920 1.1 christos 1921 1.1 christos /* Inits. */ 1922 1.1 christos memset(&relay_msg, 0, sizeof(relay_msg)); 1923 1.1 christos memset(&if_id, 0, sizeof(if_id)); 1924 1.1 christos #if defined(RELAY_PORT) 1925 1.1 christos memset(&down_port, 0, sizeof(down_port)); 1926 1.1 christos #endif 1927 1.1 christos memset(&to, 0, sizeof(to)); 1928 1.1 christos to.sin6_family = AF_INET6; 1929 1.1 christos #ifdef HAVE_SA_LEN 1930 1.1 christos to.sin6_len = sizeof(to); 1931 1.1 christos #endif 1932 1.1 christos to.sin6_port = remote_port; 1933 1.1 christos peer.len = 16; 1934 1.1 christos 1935 1.1 christos /* Get the relay-msg option (carrying the message to relay). */ 1936 1.1 christos oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG); 1937 1.1 christos if (oc == NULL) { 1938 1.1 christos log_info("No relay-msg."); 1939 1.1 christos return; 1940 1.1 christos } 1941 1.1 christos if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL, 1942 1.1 christos packet->options, NULL, 1943 1.1 christos &global_scope, oc, MDL) || 1944 1.1 christos (relay_msg.len < offsetof(struct dhcpv6_packet, options))) { 1945 1.1 christos log_error("Can't evaluate relay-msg."); 1946 1.3 christos goto cleanup; 1947 1.1 christos } 1948 1.1 christos msg = (const struct dhcpv6_packet *) relay_msg.data; 1949 1.1 christos 1950 1.1 christos /* Get the interface-id (if exists) and the downstream. */ 1951 1.1 christos oc = lookup_option(&dhcpv6_universe, packet->options, 1952 1.1 christos D6O_INTERFACE_ID); 1953 1.1 christos if (oc != NULL) { 1954 1.1 christos int if_index; 1955 1.1 christos 1956 1.1 christos if (!evaluate_option_cache(&if_id, packet, NULL, NULL, 1957 1.1 christos packet->options, NULL, 1958 1.1 christos &global_scope, oc, MDL) || 1959 1.1 christos (if_id.len != sizeof(int))) { 1960 1.1 christos log_info("Can't evaluate interface-id."); 1961 1.1 christos goto cleanup; 1962 1.1 christos } 1963 1.1 christos memcpy(&if_index, if_id.data, sizeof(int)); 1964 1.1 christos for (dp = downstreams; dp; dp = dp->next) { 1965 1.1 christos if (dp->id == if_index) 1966 1.1 christos break; 1967 1.1 christos } 1968 1.1 christos } else { 1969 1.1 christos if (use_if_id) { 1970 1.1 christos /* Require an interface-id. */ 1971 1.1 christos log_info("No interface-id."); 1972 1.1 christos goto cleanup; 1973 1.1 christos } 1974 1.1 christos for (dp = downstreams; dp; dp = dp->next) { 1975 1.1 christos /* Get the first matching one. */ 1976 1.1 christos if (!memcmp(&dp->link.sin6_addr, 1977 1.1 christos &packet->dhcpv6_link_address, 1978 1.1 christos sizeof(struct in6_addr))) 1979 1.1 christos break; 1980 1.1 christos } 1981 1.1 christos } 1982 1.1 christos /* Why bother when there is no choice. */ 1983 1.1 christos if (!dp && downstreams && !downstreams->next) 1984 1.1 christos dp = downstreams; 1985 1.1 christos if (!dp) { 1986 1.1 christos log_info("Can't find the down interface."); 1987 1.1 christos goto cleanup; 1988 1.1 christos } 1989 1.1 christos memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len); 1990 1.1 christos to.sin6_addr = packet->dhcpv6_peer_address; 1991 1.1 christos 1992 1.1 christos /* Check if we should relay the carried message. */ 1993 1.1 christos switch (msg->msg_type) { 1994 1.1 christos /* Relay-Reply of for another relay, not a client. */ 1995 1.1 christos case DHCPV6_RELAY_REPL: 1996 1.1 christos to.sin6_port = local_port; 1997 1.1 christos 1998 1.1 christos #if defined(RELAY_PORT) 1999 1.1 christos oc = lookup_option(&dhcpv6_universe, packet->options, 2000 1.1 christos D6O_RELAY_SOURCE_PORT); 2001 1.1 christos if (oc != NULL) { 2002 1.1 christos u_int16_t down_relay_port; 2003 1.1 christos 2004 1.1 christos memset(&down_port, 0, sizeof(down_port)); 2005 1.1 christos if (!evaluate_option_cache(&down_port, packet, NULL, 2006 1.1 christos NULL, packet->options, NULL, 2007 1.1 christos &global_scope, oc, MDL) || 2008 1.1 christos (down_port.len != sizeof(u_int16_t))) { 2009 1.1 christos log_info("Can't evaluate down " 2010 1.1 christos "relay-source-port."); 2011 1.1 christos goto cleanup; 2012 1.1 christos } 2013 1.1 christos memcpy(&down_relay_port, down_port.data, 2014 1.1 christos sizeof(u_int16_t)); 2015 1.1 christos /* 2016 1.1 christos * If the down_relay_port value is non-zero, 2017 1.1 christos * that means our downstream relay agent uses 2018 1.1 christos * a non-547 UDP source port sending 2019 1.1 christos * relay-forw message to us. We need to use 2020 1.1 christos * the same UDP port sending reply back. 2021 1.1 christos */ 2022 1.1 christos if (down_relay_port) { 2023 1.1 christos to.sin6_port = down_relay_port; 2024 1.1 christos } 2025 1.1 christos } 2026 1.1 christos #endif 2027 1.1 christos 2028 1.1 christos /* Fall into: */ 2029 1.1 christos 2030 1.1 christos case DHCPV6_ADVERTISE: 2031 1.1 christos case DHCPV6_REPLY: 2032 1.1 christos case DHCPV6_RECONFIGURE: 2033 1.1 christos case DHCPV6_RELAY_FORW: 2034 1.1 christos case DHCPV6_LEASEQUERY_REPLY: 2035 1.1 christos case DHCPV6_DHCPV4_RESPONSE: 2036 1.1 christos log_info("Relaying %s to %s port %d down.", 2037 1.1 christos dhcpv6_type_names[msg->msg_type], 2038 1.1 christos piaddr(peer), 2039 1.1 christos ntohs(to.sin6_port)); 2040 1.1 christos break; 2041 1.1 christos 2042 1.1 christos case DHCPV6_SOLICIT: 2043 1.1 christos case DHCPV6_REQUEST: 2044 1.1 christos case DHCPV6_CONFIRM: 2045 1.1 christos case DHCPV6_RENEW: 2046 1.1 christos case DHCPV6_REBIND: 2047 1.1 christos case DHCPV6_RELEASE: 2048 1.1 christos case DHCPV6_DECLINE: 2049 1.1 christos case DHCPV6_INFORMATION_REQUEST: 2050 1.1 christos case DHCPV6_LEASEQUERY: 2051 1.1 christos case DHCPV6_DHCPV4_QUERY: 2052 1.1 christos log_info("Discarding %s to %s port %d down.", 2053 1.1 christos dhcpv6_type_names[msg->msg_type], 2054 1.1 christos piaddr(peer), 2055 1.1 christos ntohs(to.sin6_port)); 2056 1.1 christos goto cleanup; 2057 1.1 christos 2058 1.1 christos default: 2059 1.1 christos log_info("Unknown %d type to %s port %d down.", 2060 1.1 christos msg->msg_type, 2061 1.1 christos piaddr(peer), 2062 1.1 christos ntohs(to.sin6_port)); 2063 1.1 christos goto cleanup; 2064 1.1 christos } 2065 1.1 christos 2066 1.1 christos /* Send the message to the downstream. */ 2067 1.1 christos send_packet6(dp->ifp, (unsigned char *) relay_msg.data, 2068 1.1 christos (size_t) relay_msg.len, &to); 2069 1.1 christos 2070 1.1 christos cleanup: 2071 1.1 christos if (relay_msg.data != NULL) 2072 1.1 christos data_string_forget(&relay_msg, MDL); 2073 1.1 christos if (if_id.data != NULL) 2074 1.1 christos data_string_forget(&if_id, MDL); 2075 1.1 christos } 2076 1.6 christos #endif /* UNIT_TEST */ 2077 1.1 christos 2078 1.1 christos /* 2079 1.1 christos * Called by the dispatch packet handler with a decoded packet. 2080 1.1 christos */ 2081 1.1 christos void 2082 1.1 christos dhcpv6(struct packet *packet) { 2083 1.6 christos #ifndef UNIT_TEST 2084 1.1 christos struct stream_list *dp; 2085 1.1 christos 2086 1.1 christos /* Try all relay-replies downwards. */ 2087 1.1 christos if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) { 2088 1.1 christos process_down6(packet); 2089 1.1 christos return; 2090 1.1 christos } 2091 1.1 christos /* Others are candidates to go up if they come from down. */ 2092 1.1 christos for (dp = downstreams; dp; dp = dp->next) { 2093 1.1 christos if (packet->interface != dp->ifp) 2094 1.1 christos continue; 2095 1.1 christos process_up6(packet, dp); 2096 1.1 christos return; 2097 1.1 christos } 2098 1.1 christos /* Relay-forward could work from an unknown interface. */ 2099 1.1 christos if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) { 2100 1.1 christos process_up6(packet, NULL); 2101 1.1 christos return; 2102 1.1 christos } 2103 1.1 christos 2104 1.1 christos log_info("Can't process packet from interface '%s'.", 2105 1.1 christos packet->interface->name); 2106 1.6 christos #endif /* UNIT_TEST */ 2107 1.1 christos } 2108 1.6 christos #endif /* DHCPv6 */ 2109 1.1 christos 2110 1.1 christos /* Stub routines needed for linking with DHCP libraries. */ 2111 1.1 christos void 2112 1.1 christos bootp(struct packet *packet) { 2113 1.1 christos return; 2114 1.1 christos } 2115 1.1 christos 2116 1.1 christos void 2117 1.1 christos dhcp(struct packet *packet) { 2118 1.1 christos return; 2119 1.1 christos } 2120 1.1 christos 2121 1.1 christos #if defined(DHCPv6) && defined(DHCP4o6) 2122 1.1 christos isc_result_t dhcpv4o6_handler(omapi_object_t *h) 2123 1.1 christos { 2124 1.1 christos return ISC_R_NOTIMPLEMENTED; 2125 1.1 christos } 2126 1.1 christos #endif 2127 1.1 christos 2128 1.1 christos void 2129 1.1 christos classify(struct packet *p, struct class *c) { 2130 1.1 christos return; 2131 1.1 christos } 2132 1.1 christos 2133 1.1 christos int 2134 1.1 christos check_collection(struct packet *p, struct lease *l, struct collection *c) { 2135 1.1 christos return 0; 2136 1.1 christos } 2137 1.1 christos 2138 1.1 christos isc_result_t 2139 1.1 christos find_class(struct class **class, const char *c1, const char *c2, int i) { 2140 1.1 christos return ISC_R_NOTFOUND; 2141 1.1 christos } 2142 1.1 christos 2143 1.1 christos int 2144 1.1 christos parse_allow_deny(struct option_cache **oc, struct parse *p, int i) { 2145 1.1 christos return 0; 2146 1.1 christos } 2147 1.1 christos 2148 1.1 christos isc_result_t 2149 1.1 christos dhcp_set_control_state(control_object_state_t oldstate, 2150 1.1 christos control_object_state_t newstate) { 2151 1.1 christos char buf = 0; 2152 1.1 christos 2153 1.1 christos if (newstate != server_shutdown) 2154 1.1 christos return ISC_R_SUCCESS; 2155 1.1 christos 2156 1.3 christos /* Log shutdown on signal. */ 2157 1.3 christos log_info("Received signal %d, initiating shutdown.", shutdown_signal); 2158 1.3 christos 2159 1.1 christos if (no_pid_file == ISC_FALSE) 2160 1.1 christos (void) unlink(path_dhcrelay_pid); 2161 1.1 christos 2162 1.1 christos if (!no_daemon && dfd[0] != -1 && dfd[1] != -1) { 2163 1.1 christos IGNORE_RET(write(dfd[1], &buf, 1)); 2164 1.1 christos (void) close(dfd[1]); 2165 1.1 christos dfd[0] = dfd[1] = -1; 2166 1.1 christos } 2167 1.1 christos exit(0); 2168 1.1 christos } 2169 1.1 christos 2170 1.1 christos /*! 2171 1.1 christos * 2172 1.1 christos * \brief Allocate an interface as requested with a given set of flags 2173 1.1 christos * 2174 1.1 christos * The requested interface is allocated, its flags field is set to 2175 1.1 christos * INTERFACE_REQUESTED OR'd with the given flags, and then added to 2176 1.1 christos * the list of interfaces. 2177 1.1 christos * 2178 1.1 christos * \param name - name of the requested interface 2179 1.1 christos * \param flags - additional flags for the interface 2180 1.1 christos * 2181 1.1 christos * \return Nothing 2182 1.1 christos */ 2183 1.1 christos void request_v4_interface(const char* name, int flags) { 2184 1.1 christos struct interface_info *tmp = NULL; 2185 1.1 christos int len = strlen(name); 2186 1.1 christos isc_result_t status; 2187 1.1 christos 2188 1.1 christos if (len >= sizeof(tmp->name)) { 2189 1.1 christos log_fatal("%s: interface name too long (is %d)", name, len); 2190 1.1 christos } 2191 1.1 christos 2192 1.1 christos status = interface_allocate(&tmp, MDL); 2193 1.1 christos if (status != ISC_R_SUCCESS) { 2194 1.1 christos log_fatal("%s: interface_allocate: %s", name, 2195 1.1 christos isc_result_totext(status)); 2196 1.1 christos } 2197 1.1 christos 2198 1.1 christos log_debug("Requesting: %s as upstream: %c downstream: %c", name, 2199 1.1 christos (flags & INTERFACE_UPSTREAM ? 'Y' : 'N'), 2200 1.1 christos (flags & INTERFACE_DOWNSTREAM ? 'Y' : 'N')); 2201 1.1 christos 2202 1.3 christos memcpy(tmp->name, name, len); 2203 1.1 christos interface_snorf(tmp, (INTERFACE_REQUESTED | flags)); 2204 1.1 christos interface_dereference(&tmp, MDL); 2205 1.1 christos } 2206