From 6e7e6637ac8d94a0f230a3dd37e1a83f7ea2847b Mon Sep 17 00:00:00 2001 From: Thomas Markwalder Date: Wed, 10 Feb 2016 07:20:03 -0500 Subject: [master] Add link selection suboption support to dhcrelay (RFC 3527) Merges in rt34875. --- relay/dhcrelay.8 | 35 +++++++++++++++++++++++++++++++++-- relay/dhcrelay.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 3 deletions(-) (limited to 'relay') diff --git a/relay/dhcrelay.8 b/relay/dhcrelay.8 index d3f4593b..3c2a81e4 100644 --- a/relay/dhcrelay.8 +++ b/relay/dhcrelay.8 @@ -1,6 +1,6 @@ .\" dhcrelay.8 .\" -.\" Copyright (c) 2009-2013 by Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (c) 2009-2013,2016 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1997-2003 by Internet Software Consortium .\" @@ -78,6 +78,8 @@ dhcrelay - Dynamic Host Configuration Protocol Relay Agent .B -i .I interfaceN ] +.B -l +.I interface ] .I server0 [ @@ -215,8 +217,37 @@ in four ways: It may \fIappend\fR its own set of relay options to the packet, leaving the supplied option field intact; it may \fIreplace\fR the existing agent option field; it may \fIforward\fR the packet unchanged; or, it may \fIdiscard\fR it. +.TP +-u \fIifname\fR +Enables the addition of a RFC 3527 compliant link selection suboption for +clients directly connected to the relay. This RFC allows a relay to +specify two different IP addresses: one for the server to use when +communicating with the relay (giaddr) the other for choosing the subnet +for the client (the suboption). This can be useful if the server is +unable to send packets to the relay via the address used for the subnet. + +When enabled, dhcrelay will add an agent option (as per \fB-a\fR above) that +includes the link selection suboption to the forwarded packet. This will only +be done to packets received from clients that are directly connected to the +relay (i.e. giaddr is zero). The address used in the suboption will be that +of the link upon which the inbound packet was received (which would otherwise +be used for giaddr). The value of giaddr will be set to that of interface +\fIifname\fR. + +Only one interface should be marked in this fashion. Currently enabling +this option on an interface causes the relay to process all DHCP traffic +similar to the \fI-i\fR option, in the future we may split the two more +completely. + +This option is off by default. Note that enabling this option automatically +enables the \fB-a\fR option. + +Keep in mind that using options such as \fB-m replace\fR or \fB-m discard\fR +on relays upstream from one using \fB-u\fR can pose problems. The upstream +relay will wipe out the initial agent option containing the link selection +while leaving the re-purposed giaddr value in place, causing packets to go +astray. -To use this option you must also enable the \fB-a\fR option. .PP \fIOptions available in DHCPv6 mode only:\fR .TP diff --git a/relay/dhcrelay.c b/relay/dhcrelay.c index e50032b8..88a0b47a 100644 --- a/relay/dhcrelay.c +++ b/relay/dhcrelay.c @@ -61,6 +61,7 @@ int server_packets_relayed = 0; /* Packets relayed from server to client. */ int client_packet_errors = 0; /* Errors sending packets to clients. */ int add_agent_options = 0; /* If nonzero, add relay agent options. */ +int add_rfc3527_suboption = 0; /* If nonzero, add RFC3527 link selection sub-option. */ int agent_option_errors = 0; /* Number of packets forwarded without agent options because there was no room. */ @@ -100,6 +101,8 @@ struct server_list { struct sockaddr_in to; } *servers; +struct interface_info *uplink; + #ifdef DHCPv6 struct stream_list { struct stream_list *next; @@ -150,6 +153,7 @@ char *progname; " [-pf ] [--no-pid]\n"\ " [-m append|replace|forward|discard]\n" \ " [-i interface0 [ ... -i interfaceN]\n" \ +" [-u interface]\n" \ " server0 [ ... serverN]\n\n" \ " %s -6 [-d] [-q] [-I] [-c ] [-p ]\n" \ " [-pf ] [--no-pid]\n" \ @@ -164,6 +168,7 @@ char *progname; " [-pf ] [--no-pid]\n" \ " [-m append|replace|forward|discard]\n" \ " [-i interface0 [ ... -i interfaceN]\n" \ +" [-u interface]\n" \ " server0 [ ... serverN]\n\n" #endif @@ -368,6 +373,23 @@ main(int argc, char **argv) { agent_relay_mode = discard; } else usage("Unknown argument to -m: %s", argv[i]); + } else if (!strcmp(argv [i], "-u")) { + if (++i == argc) + usage(use_noarg, argv[i-1]); + + /* Allocate the uplink interface */ + status = interface_allocate(&uplink, MDL); + if (status != ISC_R_SUCCESS) { + log_fatal("%s: uplink interface_allocate: %s", + argv[i], isc_result_totext(status)); + } + + strcpy(uplink->name, argv[i]); + interface_snorf(uplink, INTERFACE_REQUESTED); + + /* Turn on -a, in case they don't do so explicitly */ + add_agent_options = 1; + add_rfc3527_suboption = 1; } else if (!strcmp(argv[i], "-D")) { #ifdef DHCPv6 if (local_family_set && (local_family == AF_INET6)) { @@ -753,7 +775,8 @@ do_relay4(struct interface_info *ip, struct dhcp_packet *packet, return; /* Add relay agent options if indicated. If something goes wrong, - drop the packet. */ + * drop the packet. Note this may set packet->giaddr if RFC3527 + * is enabled. */ if (!(length = add_relay_agent_options(ip, packet, length, ip->addresses[0]))) return; @@ -995,6 +1018,7 @@ add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet, int is_dhcp = 0, mms; unsigned optlen; u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL; + int adding_link_select; /* If we're not adding agent options to packets, we can skip this. */ @@ -1008,6 +1032,10 @@ add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet, max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length; + /* Add link selection suboption if enabled and we're the first relay */ + adding_link_select = (add_rfc3527_suboption + && (packet->giaddr.s_addr == 0)); + /* Commence processing after the cookie. */ sp = op = &packet->options[4]; @@ -1137,6 +1165,10 @@ add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet, optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */ } + if (adding_link_select) { + optlen += 6; + } + /* We do not support relay option fragmenting(multiple options to * support an option data exceeding 255 bytes). */ @@ -1168,6 +1200,19 @@ add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet, memcpy(sp, ip->remote_id, ip->remote_id_len); sp += ip->remote_id_len; } + + /* RFC3527: Use the inbound packet's interface address in + * the link selection suboption and set the outbound giaddr + * to the uplink address. */ + if (adding_link_select) { + *sp++ = RAI_LINK_SELECT; + *sp++ = 4u; + memcpy(sp, &giaddr.s_addr, 4); + sp += 4; + packet->giaddr = uplink->addresses[0]; + log_debug ("Adding link selection suboption" + " with addr: %s", inet_ntoa(giaddr)); + } } else { ++agent_option_errors; log_error("No room in packet (used %d of %d) " -- cgit v1.2.1