2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-22 09:57:20 +00:00

Fix handling of -A and -a flags in dhcrelay; it was failing to expand

packet size as needed to add relay agent options. [rt18296]
This commit is contained in:
Evan Hunt 2008-07-16 16:18:22 +00:00
parent 4408ba6133
commit d352732ea7
2 changed files with 167 additions and 130 deletions

View File

@ -96,6 +96,9 @@ work on other platforms. Please report any problems and suggested fixes to
- Merge DHCPv6-only "dhcrelay6" into general-purpose "dhcrelay" (use - Merge DHCPv6-only "dhcrelay6" into general-purpose "dhcrelay" (use
"-6" option to select DHCPv6 mode). "-6" option to select DHCPv6 mode).
- Fix handling of -A and -a flags in dhcrelay; it was failing to expand
packet size as needed to add relay agent options.
Changes since 4.0.0 (new features) Changes since 4.0.0 (new features)
- Added DHCPv6 rapid commit support. - Added DHCPv6 rapid commit support.

View File

@ -62,6 +62,9 @@ int server_packets_relayed = 0; /* Packets relayed from server to client. */
int client_packet_errors = 0; /* Errors sending packets to clients. */ int client_packet_errors = 0; /* Errors sending packets to clients. */
int add_agent_options = 0; /* If nonzero, add relay agent options. */ int add_agent_options = 0; /* If nonzero, add relay agent options. */
int agent_option_errors = 0; /* Number of packets forwarded without
agent options because there was no room. */
int drop_agent_mismatches = 0; /* If nonzero, drop server replies that int drop_agent_mismatches = 0; /* If nonzero, drop server replies that
don't have matching circuit-id's. */ don't have matching circuit-id's. */
int corrupt_agent_options = 0; /* Number of packets dropped because int corrupt_agent_options = 0; /* Number of packets dropped because
@ -80,7 +83,7 @@ isc_boolean_t use_if_id = ISC_FALSE;
#endif #endif
/* Maximum size of a packet with agent options added. */ /* Maximum size of a packet with agent options added. */
int dhcp_max_agent_option_packet_length = 576; int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
/* What to do about packets we're asked to relay that /* What to do about packets we're asked to relay that
already have a relay option: */ already have a relay option: */
@ -113,15 +116,15 @@ static void setup_streams(void);
static void do_relay4(struct interface_info *, struct dhcp_packet *, static void do_relay4(struct interface_info *, struct dhcp_packet *,
unsigned int, unsigned int, struct iaddr, unsigned int, unsigned int, struct iaddr,
struct hardware *); struct hardware *);
static int add_relay_agent_options(struct interface_info *, static int add_relay_agent_options(struct interface_info *,
struct dhcp_packet *, unsigned, struct dhcp_packet *, unsigned,
struct in_addr); struct in_addr);
static int find_interface_by_agent_option(struct dhcp_packet *, static int find_interface_by_agent_option(struct dhcp_packet *,
struct interface_info **, u_int8_t *, int); struct interface_info **, u_int8_t *, int);
static int strip_relay_agent_options(struct interface_info *, static int strip_relay_agent_options(struct interface_info *,
struct interface_info **, struct interface_info **,
struct dhcp_packet *, unsigned); struct dhcp_packet *, unsigned);
static char copyright[] = "Copyright 2004-2008 Internet Systems Consortium."; static char copyright[] = "Copyright 2004-2008 Internet Systems Consortium.";
static char arr[] = "All rights reserved."; static char arr[] = "All rights reserved.";
@ -157,29 +160,29 @@ main(int argc, char **argv) {
isc_result_t status; isc_result_t status;
struct servent *ent; struct servent *ent;
struct server_list *sp = NULL; struct server_list *sp = NULL;
struct interface_info *tmp = NULL; struct interface_info *tmp = NULL;
char *service_local, *service_remote; char *service_local, *service_remote;
u_int16_t port_local, port_remote; u_int16_t port_local, port_remote;
int no_daemon = 0, quiet = 0; int no_daemon = 0, quiet = 0;
int fd; int fd;
int i; int i;
#ifdef DHCPv6 #ifdef DHCPv6
struct stream_list *sl = NULL; struct stream_list *sl = NULL;
int local_family_set = 0; int local_family_set = 0;
#endif #endif
/* Make sure that file descriptors 0(stdin), 1,(stdout), and /* Make sure that file descriptors 0(stdin), 1,(stdout), and
2(stderr) are open. To do this, we assume that when we 2(stderr) are open. To do this, we assume that when we
open a file the lowest available file descriptor is used. */ open a file the lowest available file descriptor is used. */
fd = open("/dev/null", O_RDWR); fd = open("/dev/null", O_RDWR);
if (fd == 0) if (fd == 0)
fd = open("/dev/null", O_RDWR); fd = open("/dev/null", O_RDWR);
if (fd == 1) if (fd == 1)
fd = open("/dev/null", O_RDWR); fd = open("/dev/null", O_RDWR);
if (fd == 2) if (fd == 2)
log_perror = 0; /* No sense logging to /dev/null. */ log_perror = 0; /* No sense logging to /dev/null. */
else if (fd != -1) else if (fd != -1)
close(fd); close(fd);
openlog("dhcrelay", LOG_NDELAY, LOG_DAEMON); openlog("dhcrelay", LOG_NDELAY, LOG_DAEMON);
@ -269,7 +272,13 @@ main(int argc, char **argv) {
#endif #endif
if (++i == argc) if (++i == argc)
usage(); usage();
dhcp_max_agent_option_packet_length = atoi(argv[i]); dhcp_max_agent_option_packet_length = atoi(argv[i]);
if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
log_fatal("%s: packet length exceeds "
"longest possible MTU\n",
argv[i]);
} else if (!strcmp(argv[i], "-m")) { } else if (!strcmp(argv[i], "-m")) {
#ifdef DHCPv6 #ifdef DHCPv6
if (local_family_set && (local_family == AF_INET6)) { if (local_family_set && (local_family == AF_INET6)) {
@ -313,13 +322,13 @@ main(int argc, char **argv) {
} }
local_family_set = 1; local_family_set = 1;
local_family = AF_INET6; local_family = AF_INET6;
if (downstreams != NULL) if (downstreams != NULL)
use_if_id = ISC_TRUE; use_if_id = ISC_TRUE;
if (++i == argc) if (++i == argc)
usage(); usage();
sl = parse_downstream(argv[i]); sl = parse_downstream(argv[i]);
sl->next = downstreams; sl->next = downstreams;
downstreams = sl; downstreams = sl;
} else if (!strcmp(argv[i], "-u")) { } else if (!strcmp(argv[i], "-u")) {
if (local_family_set && (local_family == AF_INET)) { if (local_family_set && (local_family == AF_INET)) {
usage(); usage();
@ -328,9 +337,9 @@ main(int argc, char **argv) {
local_family = AF_INET6; local_family = AF_INET6;
if (++i == argc) if (++i == argc)
usage(); usage();
sl = parse_upstream(argv[i]); sl = parse_upstream(argv[i]);
sl->next = upstreams; sl->next = upstreams;
upstreams = sl; upstreams = sl;
#endif #endif
} else if (!strcmp(argv[i], "--version")) { } else if (!strcmp(argv[i], "--version")) {
log_info("isc-dhcrelay-%s", PACKAGE_VERSION); log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
@ -376,17 +385,17 @@ main(int argc, char **argv) {
} }
} }
if (local_family == AF_INET) { if (local_family == AF_INET) {
path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID"); path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
if (path_dhcrelay_pid == NULL) if (path_dhcrelay_pid == NULL)
path_dhcrelay_pid = _PATH_DHCRELAY_PID; path_dhcrelay_pid = _PATH_DHCRELAY_PID;
} }
#ifdef DHCPv6 #ifdef DHCPv6
else { else {
path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID"); path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
if (path_dhcrelay_pid == NULL) if (path_dhcrelay_pid == NULL)
path_dhcrelay_pid = _PATH_DHCRELAY6_PID; path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
} }
#endif #endif
if (!quiet) { if (!quiet) {
@ -400,63 +409,63 @@ main(int argc, char **argv) {
} }
/* Set default port */ /* Set default port */
if (local_family == AF_INET) { if (local_family == AF_INET) {
service_local = "dhcps"; service_local = "dhcps";
service_remote = "dhcps"; service_remote = "dhcps";
port_local = htons(67); port_local = htons(67);
port_remote = htons(67); port_remote = htons(67);
} }
#ifdef DHCPv6 #ifdef DHCPv6
else { else {
service_local = "dhcpv6-server"; service_local = "dhcpv6-server";
service_remote = "dhcpv6-client"; service_remote = "dhcpv6-client";
port_local = htons(547); port_local = htons(547);
port_remote = htons(546); port_remote = htons(546);
} }
#endif #endif
if (!local_port) { if (!local_port) {
ent = getservbyname(service_local, "udp"); ent = getservbyname(service_local, "udp");
if (ent) if (ent)
local_port = ent->s_port; local_port = ent->s_port;
else else
local_port = port_local; local_port = port_local;
ent = getservbyname(service_remote, "udp"); ent = getservbyname(service_remote, "udp");
if (ent) if (ent)
remote_port = ent->s_port; remote_port = ent->s_port;
else else
remote_port = port_remote; remote_port = port_remote;
endservent(); endservent();
} }
if (local_family == AF_INET) { if (local_family == AF_INET) {
/* We need at least one server */ /* We need at least one server */
if (servers == NULL) { if (servers == NULL) {
log_fatal("No servers specified."); log_fatal("No servers specified.");
} }
/* Set up the server sockaddrs. */ /* Set up the server sockaddrs. */
for (sp = servers; sp; sp = sp->next) { for (sp = servers; sp; sp = sp->next) {
sp->to.sin_port = local_port; sp->to.sin_port = local_port;
sp->to.sin_family = AF_INET; sp->to.sin_family = AF_INET;
#ifdef HAVE_SA_LEN #ifdef HAVE_SA_LEN
sp->to.sin_len = sizeof sp->to; sp->to.sin_len = sizeof sp->to;
#endif #endif
} }
} }
#ifdef DHCPv6 #ifdef DHCPv6
else { else {
unsigned code; unsigned code;
/* We need at least one upstream and one downstream interface */ /* We need at least one upstream and one downstream interface */
if (upstreams == NULL || downstreams == NULL) { if (upstreams == NULL || downstreams == NULL) {
log_info("Must specify at least one lower " log_info("Must specify at least one lower "
"and one upper interface.\n"); "and one upper interface.\n");
usage(); usage();
} }
/* Set up the initial dhcp option universe. */ /* Set up the initial dhcp option universe. */
initialize_common_option_spaces(); initialize_common_option_spaces();
@ -474,7 +483,7 @@ main(int argc, char **argv) {
&code, 0, MDL)) &code, 0, MDL))
log_fatal("Unable to find the INTERFACE_ID " log_fatal("Unable to find the INTERFACE_ID "
"option definition."); "option definition.");
} }
#endif #endif
/* Get the current time... */ /* Get the current time... */
@ -485,7 +494,7 @@ main(int argc, char **argv) {
#ifdef DHCPv6 #ifdef DHCPv6
if (local_family == AF_INET6) if (local_family == AF_INET6)
setup_streams(); setup_streams();
#endif #endif
/* Become a daemon... */ /* Become a daemon... */
@ -522,21 +531,21 @@ main(int argc, char **argv) {
close(2); close(2);
pid = setsid(); pid = setsid();
chdir("/"); chdir("/");
} }
/* Set up the packet handler... */ /* Set up the packet handler... */
if (local_family == AF_INET) if (local_family == AF_INET)
bootp_packet_handler = do_relay4; bootp_packet_handler = do_relay4;
#ifdef DHCPv6 #ifdef DHCPv6
else else
dhcpv6_packet_handler = do_packet6; dhcpv6_packet_handler = do_packet6;
#endif #endif
/* Start dispatching packets and timeouts... */ /* Start dispatching packets and timeouts... */
dispatch(); dispatch();
/* Not reached */ /* Not reached */
return (0); return (0);
} }
@ -676,9 +685,9 @@ do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
static int static int
strip_relay_agent_options(struct interface_info *in, strip_relay_agent_options(struct interface_info *in,
struct interface_info **out, struct interface_info **out,
struct dhcp_packet *packet, struct dhcp_packet *packet,
unsigned length) { unsigned length) {
int is_dhcp = 0; int is_dhcp = 0;
u_int8_t *op, *nextop, *sp, *max; u_int8_t *op, *nextop, *sp, *max;
int good_agent_option = 0; int good_agent_option = 0;
@ -785,7 +794,7 @@ strip_relay_agent_options(struct interface_info *in,
length = sp -((u_int8_t *)packet); length = sp -((u_int8_t *)packet);
/* Make sure the packet isn't short(this is unlikely, /* Make sure the packet isn't short(this is unlikely,
but WTH) */ but WTH) */
if (length < BOOTP_MIN_LEN) { if (length < BOOTP_MIN_LEN) {
memset(sp, DHO_PAD, BOOTP_MIN_LEN - length); memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
length = BOOTP_MIN_LEN; length = BOOTP_MIN_LEN;
@ -808,8 +817,8 @@ strip_relay_agent_options(struct interface_info *in,
static int static int
find_interface_by_agent_option(struct dhcp_packet *packet, find_interface_by_agent_option(struct dhcp_packet *packet,
struct interface_info **out, struct interface_info **out,
u_int8_t *buf, int len) { u_int8_t *buf, int len) {
int i = 0; int i = 0;
u_int8_t *circuit_id = 0; u_int8_t *circuit_id = 0;
unsigned circuit_id_len = 0; unsigned circuit_id_len = 0;
@ -872,8 +881,8 @@ find_interface_by_agent_option(struct dhcp_packet *packet,
*/ */
static int static int
add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet, add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
unsigned length, struct in_addr giaddr) { unsigned length, struct in_addr giaddr) {
int is_dhcp = 0; int is_dhcp = 0, mms;
unsigned optlen; unsigned optlen;
u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL; u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
@ -887,7 +896,7 @@ add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4)) if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
return (length); return (length);
max = ((u_int8_t *)packet) + length; max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
/* Commence processing after the cookie. */ /* Commence processing after the cookie. */
sp = op = &packet->options[4]; sp = op = &packet->options[4];
@ -919,7 +928,17 @@ add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
case DHO_DHCP_MESSAGE_TYPE: case DHO_DHCP_MESSAGE_TYPE:
is_dhcp = 1; is_dhcp = 1;
goto skip; goto skip;
break;
/*
* If there's a maximum message size option, we
* should pay attention to it
*/
case DHO_DHCP_MAX_MESSAGE_SIZE:
mms = ntohs(*(op + 2));
if (mms < dhcp_max_agent_option_packet_length &&
mms >= DHCP_MTU_MIN)
max = ((u_int8_t *)packet) + mms;
goto skip;
/* Quit immediately if we hit an End option. */ /* Quit immediately if we hit an End option. */
case DHO_END: case DHO_END:
@ -1013,30 +1032,45 @@ add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
log_fatal("Total agent option length(%u) out of range " log_fatal("Total agent option length(%u) out of range "
"[3 - 255] on %s\n", optlen, ip->name); "[3 - 255] on %s\n", optlen, ip->name);
/* Is there room for the option, its code+len, and DHO_END? */ /*
if ((sp > max) ||(max - sp < optlen + 3)) * Is there room for the option, its code+len, and DHO_END?
return (0); * If not, forward without adding the option.
*/
if (max - sp >= optlen + 3) {
log_debug("Adding %d-byte relay agent option", optlen + 3);
/* Okay, cons up *our* Relay Agent Information option. */ /* Okay, cons up *our* Relay Agent Information option. */
*sp++ = DHO_DHCP_AGENT_OPTIONS; *sp++ = DHO_DHCP_AGENT_OPTIONS;
*sp++ = optlen; *sp++ = optlen;
/* Copy in the circuit id... */ /* Copy in the circuit id... */
*sp++ = RAI_CIRCUIT_ID; *sp++ = RAI_CIRCUIT_ID;
*sp++ = ip->circuit_id_len; *sp++ = ip->circuit_id_len;
memcpy(sp, ip->circuit_id, ip->circuit_id_len); memcpy(sp, ip->circuit_id, ip->circuit_id_len);
sp += ip->circuit_id_len; sp += ip->circuit_id_len;
/* Copy in remote ID... */ /* Copy in remote ID... */
if (ip->remote_id) { if (ip->remote_id) {
*sp++ = RAI_REMOTE_ID; *sp++ = RAI_REMOTE_ID;
*sp++ = ip->remote_id_len; *sp++ = ip->remote_id_len;
memcpy(sp, ip->remote_id, ip->remote_id_len); memcpy(sp, ip->remote_id, ip->remote_id_len);
sp += ip->remote_id_len; sp += ip->remote_id_len;
}
} else {
++agent_option_errors;
log_error("No room in packet (used %d of %d) "
"for %d-byte relay agent option: omitted",
(int) (sp - ((u_int8_t *) packet)),
(int) (max - ((u_int8_t *) packet)),
optlen + 3);
} }
/* Deposit an END option. */ /*
*sp++ = DHO_END; * Deposit an END option unless the packet is full (shouldn't
* be possible).
*/
if (sp < max)
*sp++ = DHO_END;
/* Recalculate total packet length. */ /* Recalculate total packet length. */
length = sp -((u_int8_t *)packet); length = sp -((u_int8_t *)packet);
@ -1209,8 +1243,8 @@ setup_streams(void) {
for (dp = downstreams; dp; dp = dp->next) { for (dp = downstreams; dp; dp = dp->next) {
/* Check interface */ /* Check interface */
if (dp->ifp->v6address_count == 0) if (dp->ifp->v6address_count == 0)
log_fatal("Interface '%s' has no IPv6 addresses.", log_fatal("Interface '%s' has no IPv6 addresses.",
dp->ifp->name); dp->ifp->name);
/* Check/set link. */ /* Check/set link. */
if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr)) if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
@ -1223,8 +1257,8 @@ setup_streams(void) {
if (!link_is_set) if (!link_is_set)
break; break;
if (!memcmp(&dp->ifp->v6addresses[i], if (!memcmp(&dp->ifp->v6addresses[i],
&dp->link.sin6_addr, &dp->link.sin6_addr,
sizeof(dp->link.sin6_addr))) sizeof(dp->link.sin6_addr)))
break; break;
} }
if (i == dp->ifp->v6address_count) if (i == dp->ifp->v6address_count)
@ -1473,7 +1507,7 @@ process_down6(struct packet *packet) {
for (dp = downstreams; dp; dp = dp->next) { for (dp = downstreams; dp; dp = dp->next) {
/* Get the first matching one. */ /* Get the first matching one. */
if (!memcmp(&dp->link.sin6_addr, if (!memcmp(&dp->link.sin6_addr,
&packet->dhcpv6_link_address, &packet->dhcpv6_link_address,
sizeof(struct in6_addr))) sizeof(struct in6_addr)))
break; break;
} }
@ -1566,24 +1600,24 @@ dhcpv6(struct packet *packet) {
} }
log_info("Can't process packet from interface '%s'.", log_info("Can't process packet from interface '%s'.",
packet->interface->name); packet->interface->name);
} }
#endif #endif
/* Stub routines needed for linking with DHCP libraries. */ /* Stub routines needed for linking with DHCP libraries. */
void void
bootp(struct packet *packet) { bootp(struct packet *packet) {
return; return;
} }
void void
dhcp(struct packet *packet) { dhcp(struct packet *packet) {
return; return;
} }
void void
classify(struct packet *p, struct class *c) { classify(struct packet *p, struct class *c) {
return; return;
} }
int int