2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-22 01:49:35 +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
"-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)
- 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 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
don't have matching circuit-id's. */
int corrupt_agent_options = 0; /* Number of packets dropped because
@ -80,7 +83,7 @@ isc_boolean_t use_if_id = ISC_FALSE;
#endif
/* 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
already have a relay option: */
@ -113,15 +116,15 @@ static void setup_streams(void);
static void do_relay4(struct interface_info *, struct dhcp_packet *,
unsigned int, unsigned int, struct iaddr,
struct hardware *);
struct hardware *);
static int add_relay_agent_options(struct interface_info *,
struct dhcp_packet *, unsigned,
struct in_addr);
struct dhcp_packet *, unsigned,
struct in_addr);
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 *,
struct interface_info **,
struct dhcp_packet *, unsigned);
struct interface_info **,
struct dhcp_packet *, unsigned);
static char copyright[] = "Copyright 2004-2008 Internet Systems Consortium.";
static char arr[] = "All rights reserved.";
@ -157,29 +160,29 @@ main(int argc, char **argv) {
isc_result_t status;
struct servent *ent;
struct server_list *sp = NULL;
struct interface_info *tmp = NULL;
char *service_local, *service_remote;
u_int16_t port_local, port_remote;
struct interface_info *tmp = NULL;
char *service_local, *service_remote;
u_int16_t port_local, port_remote;
int no_daemon = 0, quiet = 0;
int fd;
int i;
#ifdef DHCPv6
struct stream_list *sl = NULL;
struct stream_list *sl = NULL;
int local_family_set = 0;
#endif
/* Make sure that file descriptors 0(stdin), 1,(stdout), and
2(stderr) are open. To do this, we assume that when we
open a file the lowest available file descriptor is used. */
fd = open("/dev/null", O_RDWR);
if (fd == 0)
fd = open("/dev/null", O_RDWR);
if (fd == 1)
fd = open("/dev/null", O_RDWR);
if (fd == 2)
log_perror = 0; /* No sense logging to /dev/null. */
else if (fd != -1)
close(fd);
/* Make sure that file descriptors 0(stdin), 1,(stdout), and
2(stderr) are open. To do this, we assume that when we
open a file the lowest available file descriptor is used. */
fd = open("/dev/null", O_RDWR);
if (fd == 0)
fd = open("/dev/null", O_RDWR);
if (fd == 1)
fd = open("/dev/null", O_RDWR);
if (fd == 2)
log_perror = 0; /* No sense logging to /dev/null. */
else if (fd != -1)
close(fd);
openlog("dhcrelay", LOG_NDELAY, LOG_DAEMON);
@ -269,7 +272,13 @@ main(int argc, char **argv) {
#endif
if (++i == argc)
usage();
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")) {
#ifdef DHCPv6
if (local_family_set && (local_family == AF_INET6)) {
@ -313,13 +322,13 @@ main(int argc, char **argv) {
}
local_family_set = 1;
local_family = AF_INET6;
if (downstreams != NULL)
use_if_id = ISC_TRUE;
if (downstreams != NULL)
use_if_id = ISC_TRUE;
if (++i == argc)
usage();
sl = parse_downstream(argv[i]);
sl->next = downstreams;
downstreams = sl;
sl = parse_downstream(argv[i]);
sl->next = downstreams;
downstreams = sl;
} else if (!strcmp(argv[i], "-u")) {
if (local_family_set && (local_family == AF_INET)) {
usage();
@ -328,9 +337,9 @@ main(int argc, char **argv) {
local_family = AF_INET6;
if (++i == argc)
usage();
sl = parse_upstream(argv[i]);
sl->next = upstreams;
upstreams = sl;
sl = parse_upstream(argv[i]);
sl->next = upstreams;
upstreams = sl;
#endif
} else if (!strcmp(argv[i], "--version")) {
log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
@ -376,17 +385,17 @@ main(int argc, char **argv) {
}
}
if (local_family == AF_INET) {
path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
if (path_dhcrelay_pid == NULL)
path_dhcrelay_pid = _PATH_DHCRELAY_PID;
}
if (local_family == AF_INET) {
path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
if (path_dhcrelay_pid == NULL)
path_dhcrelay_pid = _PATH_DHCRELAY_PID;
}
#ifdef DHCPv6
else {
path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
if (path_dhcrelay_pid == NULL)
path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
}
else {
path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
if (path_dhcrelay_pid == NULL)
path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
}
#endif
if (!quiet) {
@ -400,63 +409,63 @@ main(int argc, char **argv) {
}
/* Set default port */
if (local_family == AF_INET) {
service_local = "dhcps";
service_remote = "dhcps";
port_local = htons(67);
port_remote = htons(67);
}
if (local_family == AF_INET) {
service_local = "dhcps";
service_remote = "dhcps";
port_local = htons(67);
port_remote = htons(67);
}
#ifdef DHCPv6
else {
service_local = "dhcpv6-server";
service_remote = "dhcpv6-client";
port_local = htons(547);
port_remote = htons(546);
}
else {
service_local = "dhcpv6-server";
service_remote = "dhcpv6-client";
port_local = htons(547);
port_remote = htons(546);
}
#endif
if (!local_port) {
ent = getservbyname(service_local, "udp");
if (ent)
local_port = ent->s_port;
else
local_port = port_local;
ent = getservbyname(service_local, "udp");
if (ent)
local_port = ent->s_port;
else
local_port = port_local;
ent = getservbyname(service_remote, "udp");
if (ent)
remote_port = ent->s_port;
else
remote_port = port_remote;
ent = getservbyname(service_remote, "udp");
if (ent)
remote_port = ent->s_port;
else
remote_port = port_remote;
endservent();
}
endservent();
}
if (local_family == AF_INET) {
/* We need at least one server */
if (servers == NULL) {
log_fatal("No servers specified.");
}
if (local_family == AF_INET) {
/* We need at least one server */
if (servers == NULL) {
log_fatal("No servers specified.");
}
/* Set up the server sockaddrs. */
for (sp = servers; sp; sp = sp->next) {
sp->to.sin_port = local_port;
sp->to.sin_family = AF_INET;
/* Set up the server sockaddrs. */
for (sp = servers; sp; sp = sp->next) {
sp->to.sin_port = local_port;
sp->to.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
sp->to.sin_len = sizeof sp->to;
sp->to.sin_len = sizeof sp->to;
#endif
}
}
}
}
#ifdef DHCPv6
else {
else {
unsigned code;
/* We need at least one upstream and one downstream interface */
if (upstreams == NULL || downstreams == NULL) {
log_info("Must specify at least one lower "
"and one upper interface.\n");
usage();
}
/* We need at least one upstream and one downstream interface */
if (upstreams == NULL || downstreams == NULL) {
log_info("Must specify at least one lower "
"and one upper interface.\n");
usage();
}
/* Set up the initial dhcp option universe. */
initialize_common_option_spaces();
@ -474,7 +483,7 @@ main(int argc, char **argv) {
&code, 0, MDL))
log_fatal("Unable to find the INTERFACE_ID "
"option definition.");
}
}
#endif
/* Get the current time... */
@ -485,7 +494,7 @@ main(int argc, char **argv) {
#ifdef DHCPv6
if (local_family == AF_INET6)
setup_streams();
setup_streams();
#endif
/* Become a daemon... */
@ -522,21 +531,21 @@ main(int argc, char **argv) {
close(2);
pid = setsid();
chdir("/");
chdir("/");
}
/* Set up the packet handler... */
if (local_family == AF_INET)
bootp_packet_handler = do_relay4;
if (local_family == AF_INET)
bootp_packet_handler = do_relay4;
#ifdef DHCPv6
else
dhcpv6_packet_handler = do_packet6;
else
dhcpv6_packet_handler = do_packet6;
#endif
/* Start dispatching packets and timeouts... */
dispatch();
/* Not reached */
/* Not reached */
return (0);
}
@ -676,9 +685,9 @@ do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
static int
strip_relay_agent_options(struct interface_info *in,
struct interface_info **out,
struct dhcp_packet *packet,
unsigned length) {
struct interface_info **out,
struct dhcp_packet *packet,
unsigned length) {
int is_dhcp = 0;
u_int8_t *op, *nextop, *sp, *max;
int good_agent_option = 0;
@ -785,7 +794,7 @@ strip_relay_agent_options(struct interface_info *in,
length = sp -((u_int8_t *)packet);
/* Make sure the packet isn't short(this is unlikely,
but WTH) */
but WTH) */
if (length < BOOTP_MIN_LEN) {
memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
length = BOOTP_MIN_LEN;
@ -808,8 +817,8 @@ strip_relay_agent_options(struct interface_info *in,
static int
find_interface_by_agent_option(struct dhcp_packet *packet,
struct interface_info **out,
u_int8_t *buf, int len) {
struct interface_info **out,
u_int8_t *buf, int len) {
int i = 0;
u_int8_t *circuit_id = 0;
unsigned circuit_id_len = 0;
@ -872,8 +881,8 @@ find_interface_by_agent_option(struct dhcp_packet *packet,
*/
static int
add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
unsigned length, struct in_addr giaddr) {
int is_dhcp = 0;
unsigned length, struct in_addr giaddr) {
int is_dhcp = 0, mms;
unsigned optlen;
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))
return (length);
max = ((u_int8_t *)packet) + length;
max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
/* Commence processing after the cookie. */
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:
is_dhcp = 1;
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. */
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 "
"[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))
return (0);
/*
* Is there room for the option, its code+len, and DHO_END?
* 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. */
*sp++ = DHO_DHCP_AGENT_OPTIONS;
*sp++ = optlen;
/* Okay, cons up *our* Relay Agent Information option. */
*sp++ = DHO_DHCP_AGENT_OPTIONS;
*sp++ = optlen;
/* Copy in the circuit id... */
*sp++ = RAI_CIRCUIT_ID;
*sp++ = ip->circuit_id_len;
memcpy(sp, ip->circuit_id, ip->circuit_id_len);
sp += ip->circuit_id_len;
/* Copy in the circuit id... */
*sp++ = RAI_CIRCUIT_ID;
*sp++ = ip->circuit_id_len;
memcpy(sp, ip->circuit_id, ip->circuit_id_len);
sp += ip->circuit_id_len;
/* Copy in remote ID... */
if (ip->remote_id) {
*sp++ = RAI_REMOTE_ID;
*sp++ = ip->remote_id_len;
memcpy(sp, ip->remote_id, ip->remote_id_len);
sp += ip->remote_id_len;
/* Copy in remote ID... */
if (ip->remote_id) {
*sp++ = RAI_REMOTE_ID;
*sp++ = ip->remote_id_len;
memcpy(sp, ip->remote_id, 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. */
length = sp -((u_int8_t *)packet);
@ -1209,8 +1243,8 @@ setup_streams(void) {
for (dp = downstreams; dp; dp = dp->next) {
/* Check interface */
if (dp->ifp->v6address_count == 0)
log_fatal("Interface '%s' has no IPv6 addresses.",
dp->ifp->name);
log_fatal("Interface '%s' has no IPv6 addresses.",
dp->ifp->name);
/* Check/set link. */
if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
@ -1223,8 +1257,8 @@ setup_streams(void) {
if (!link_is_set)
break;
if (!memcmp(&dp->ifp->v6addresses[i],
&dp->link.sin6_addr,
sizeof(dp->link.sin6_addr)))
&dp->link.sin6_addr,
sizeof(dp->link.sin6_addr)))
break;
}
if (i == dp->ifp->v6address_count)
@ -1473,7 +1507,7 @@ process_down6(struct packet *packet) {
for (dp = downstreams; dp; dp = dp->next) {
/* Get the first matching one. */
if (!memcmp(&dp->link.sin6_addr,
&packet->dhcpv6_link_address,
&packet->dhcpv6_link_address,
sizeof(struct in6_addr)))
break;
}
@ -1566,24 +1600,24 @@ dhcpv6(struct packet *packet) {
}
log_info("Can't process packet from interface '%s'.",
packet->interface->name);
packet->interface->name);
}
#endif
/* Stub routines needed for linking with DHCP libraries. */
void
bootp(struct packet *packet) {
return;
return;
}
void
dhcp(struct packet *packet) {
return;
return;
}
void
classify(struct packet *p, struct class *c) {
return;
return;
}
int