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

Merged rt44535 (relay port)

This commit is contained in:
Francis Dupont 2017-12-23 01:18:23 +01:00
parent 0d6d300fec
commit 563f0b8aef
27 changed files with 685 additions and 57 deletions

View File

@ -312,6 +312,19 @@ dhcp-users@lists.isc.org.
when building with --enable-use-sockets and --enable-ipv4-pktinfo.
[ISC-Bugs #36118]
(to be finalized before code freeze next year)
- Added experimental support for relay port (draft-ietf-dhc-relay-port-10.txt)
feature for DHCPv4, DHCPv6 and DHCPv4-over-DHCPv6. As the code points
were not yet assigned by IANA temporary (next free) values are used.
Relay port had be enabled at compile time via --enable-relay-port and
is fully backward compatible, i.e. works with previous implementations
of servers and relays, of course in this case using legacy ports.
A new --rp <relay-port> command line option specifies to dhcrelay
an alternate source port for upstream (i.e. toward the server) messages.
(update this)
Thanks to Naiming Shen and Enke Chen for submitting patches.
[ISC-Bugs #44535]
Changes since 4.3.6 (Bugs):
- Corrected an issue where the server would return a client's previously

View File

@ -192,12 +192,51 @@ struct bpf_insn dhcp_bpf_filter [] = {
BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */
/* If we passed all the tests, ask for the whole packet. */
BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
BPF_STMT (BPF_RET + BPF_K, (u_int)-1),
/* Otherwise, drop it. */
BPF_STMT(BPF_RET+BPF_K, 0),
BPF_STMT (BPF_RET + BPF_K, 0),
};
#if defined(RELAY_PORT)
/*
* For relay port extension
*/
struct bpf_insn dhcp_bpf_relay_filter [] = {
/* Make sure this is an IP packet... */
BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12),
BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10),
/* Make sure it's a UDP packet... */
BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23),
BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8),
/* Make sure this isn't a fragment... */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0),
/* Get the IP header length... */
BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14),
/* Make sure it's to the right port... */
BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 2, 0), /* patch */
/* relay can have an alternative port... */
BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */
/* If we passed all the tests, ask for the whole packet. */
BPF_STMT (BPF_RET + BPF_K, (u_int)-1),
/* Otherwise, drop it. */
BPF_STMT (BPF_RET + BPF_K, 0),
};
int dhcp_bpf_relay_filter_len =
sizeof dhcp_bpf_relay_filter / sizeof (struct bpf_insn);
#endif
#if defined (DEC_FDDI)
struct bpf_insn *bpf_fddi_filter = NULL;
#endif
@ -309,7 +348,19 @@ void if_register_receive (info)
/* Patch the server port into the BPF program...
XXX changes to filter program may require changes
to the insn number(s) used below! XXX */
dhcp_bpf_filter [8].k = ntohs (local_port);
#if defined(RELAY_PORT)
if (relay_port) {
/*
* If user defined relay UDP port, we need to filter
* also on the user UDP port.
*/
p.bf_len = dhcp_bpf_relay_filter_len;
p.bf_insns = dhcp_bpf_relay_filter;
dhcp_bpf_relay_filter [10].k = ntohs (relay_port);
}
#endif
p.bf_insns [8].k = ntohs (local_port);
if (ioctl (info -> rfdesc, BIOCSETF, &p) < 0)
log_fatal ("Can't install packet filter program: %m");

View File

@ -44,6 +44,7 @@ int interfaces_invalidated;
int quiet_interface_discovery;
u_int16_t local_port;
u_int16_t remote_port;
u_int16_t relay_port = 0;
int dhcpv4_over_dhcpv6 = 0;
int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *);
int (*dhcp_interface_discovery_hook) (struct interface_info *);
@ -581,6 +582,10 @@ discover_interfaces(int state) {
int ir;
isc_result_t status;
int wifcount = 0;
#ifdef RELAY_PORT
int updone = 0;
int downdone = 0;
#endif
static int setup_fallback = 0;
@ -946,9 +951,39 @@ discover_interfaces(int state) {
switch (local_family) {
#ifdef DHCPv6
case AF_INET6:
#ifdef RELAY_PORT
#define UPSTREAM(ifp) \
((ifp->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)
#define DOWNSTREAM(ifp) \
((ifp->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM)
if (relay_port) {
/*
* The normal IPv6 relay only needs one
* socket as long as we find an interface.
* When user relay port is defined, and we
* have two different UDP ports. One to
* receive from DHCP client with port 547,
* and the other is user defined for sending
* to the server or upstream relay agent.
* Thus we need to register sockets for one
* upstream and one downstream interfaces.
*/
if (updone && UPSTREAM(tmp))
continue;
if (downdone && DOWNSTREAM(tmp))
continue;
}
#endif
status = omapi_register_io_object((omapi_object_t *)tmp,
if_readsocket,
0, got_one_v6, 0, 0);
#ifdef RELAY_PORT
if (UPSTREAM(tmp))
updone++;
else
downdone++;
#endif
break;
#endif /* DHCPv6 */
case AF_INET:
@ -970,8 +1005,12 @@ discover_interfaces(int state) {
* dynamically adding and removing interfaces, but
* we're well beyond that point in terms of mess.
*/
if (((state == DISCOVER_SERVER) || (state == DISCOVER_RELAY)) &&
(local_family == AF_INET6))
if (((state == DISCOVER_SERVER) || (state == DISCOVER_RELAY))
&& (local_family == AF_INET6)
#if defined(RELAY_PORT)
&& ((relay_port == 0) || (updone && downdone))
#endif
)
break;
#endif
} /* for (tmp = interfaces; ... */

View File

@ -409,6 +409,10 @@ void if_deregister_send (info)
XXX Changes to the filter program may require changes to the constant
offsets used in if_register_send to patch the NIT program! XXX */
#if defined(RELAY_PORT)
#error "Relay port is not yet supported for DLPI"
#endif
void if_register_receive (info)
struct interface_info *info;
{

View File

@ -177,6 +177,11 @@ void if_deregister_send (info)
extern struct sock_filter dhcp_bpf_filter [];
extern int dhcp_bpf_filter_len;
#if defined(RELAY_PORT)
extern struct sock_filter dhcp_bpf_relay_filter [];
extern int dhcp_bpf_relay_filter_len;
#endif
#if defined (HAVE_TR_SUPPORT)
extern struct sock_filter dhcp_bpf_tr_filter [];
extern int dhcp_bpf_tr_filter_len;
@ -256,7 +261,19 @@ static void lpf_gen_filter_setup (info)
/* Patch the server port into the LPF program...
XXX changes to filter program may require changes
to the insn number(s) used below! XXX */
dhcp_bpf_filter [8].k = ntohs ((short)local_port);
#if defined(RELAY_PORT)
if (relay_port) {
/*
* If user defined relay UDP port, we need to filter
* also on the user UDP port.
*/
p.len = dhcp_bpf_relay_filter_len;
p.filter = dhcp_bpf_relay_filter;
dhcp_bpf_relay_filter [10].k = ntohs (relay_port);
}
#endif
dhcp_bpf_filter [8].k = ntohs (local_port);
if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
sizeof p) < 0) {

View File

@ -172,6 +172,10 @@ void if_deregister_send (info)
XXX Changes to the filter program may require changes to the constant
offsets used in if_register_send to patch the NIT program! XXX */
#if defined(RELAY_PORT)
#error "Relay port is not yet supported for NIT"
#endif
void if_register_receive (info)
struct interface_info *info;
{

View File

@ -167,6 +167,12 @@ void assemble_udp_ip_header (interface, buf, bufix,
/* Fill out the UDP header */
udp.uh_sport = local_port; /* XXX */
udp.uh_dport = port; /* XXX */
#if defined(RELAY_PORT)
/* Change to relay port defined if sending to server */
if (relay_port && (port == htons(67))) {
udp.uh_sport = relay_port;
}
#endif
udp.uh_ulen = htons(sizeof(udp) + len);
memset (&udp.uh_sum, 0, sizeof udp.uh_sum);
@ -296,7 +302,12 @@ decode_udp_ip_header(struct interface_info *interface,
return -1;
/* Is it to the port we're serving? */
#if defined(RELAY_PORT)
if ((udp.uh_dport != local_port) &&
((relay_port == 0) || (udp.uh_dport != relay_port)))
#else
if (udp.uh_dport != local_port)
#endif
return -1;
#endif /* USERLAND_FILTER */

View File

@ -55,14 +55,14 @@ void if_register_send (info)
/* Set up the address we're going to connect to. */
name.sin_family = AF_INET;
name.sin_port = local_port;
name.sin_port = relay_port ? relay_port : local_port;
name.sin_addr.s_addr = htonl (INADDR_BROADCAST);
memset (name.sin_zero, 0, sizeof (name.sin_zero));
/* List addresses on which we're listening. */
if (!quiet_interface_discovery)
log_info ("Sending on %s, port %d",
piaddr (info -> address), htons (local_port));
piaddr (info -> address), htons (name.sin_port));
if ((sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
log_fatal ("Can't create dhcp socket: %m");

View File

@ -65,6 +65,10 @@
static int no_global_v6_socket = 0;
static unsigned int global_v6_socket_references = 0;
static int global_v6_socket = -1;
#if defined(RELAY_PORT)
static unsigned int relay_port_v6_socket_references = 0;
static int relay_port_v6_socket = -1;
#endif
static void if_register_multicast(struct interface_info *info);
#endif
@ -157,6 +161,11 @@ if_register_socket(struct interface_info *info, int family,
addr6 = (struct sockaddr_in6 *)&name;
addr6->sin6_family = AF_INET6;
addr6->sin6_port = local_port;
#if defined(RELAY_PORT)
if (relay_port &&
((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM))
addr6->sin6_port = relay_port;
#endif
/* A server feature */
if (bind_local_address6) {
memcpy(&addr6->sin6_addr,
@ -187,7 +196,7 @@ if_register_socket(struct interface_info *info, int family,
default:
addr = (struct sockaddr_in *)&name;
addr->sin_family = AF_INET;
addr->sin_port = local_port;
addr->sin_port = relay_port ? relay_port : local_port;
memcpy(&addr->sin_addr,
&local_address,
sizeof(addr->sin_addr));
@ -496,6 +505,10 @@ if_register6(struct interface_info *info, int do_multicast) {
log_fatal("Impossible condition at %s:%d", MDL);
}
#if defined(RELAY_PORT)
if (!relay_port ||
((info->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM)) {
#endif
if (global_v6_socket_references == 0) {
global_v6_socket = if_register_socket(info, AF_INET6,
&req_multi, NULL);
@ -527,6 +540,30 @@ if_register6(struct interface_info *info, int do_multicast) {
info->wfdesc = global_v6_socket;
global_v6_socket_references++;
#if defined(RELAY_PORT)
} else {
/*
* If relay port is defined, we need to register one
* IPv6 UPD socket to handle upstream server or relay agent
* with a non-547 UDP local port.
*/
if ((relay_port_v6_socket_references == 0) &&
((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)) {
relay_port_v6_socket = if_register_socket(info, AF_INET6,
&req_multi, NULL);
if (relay_port_v6_socket < 0) {
log_fatal("Impossible condition at %s:%d", MDL);
} else {
log_info("Bound to relay port *:%d",
(int) ntohs(relay_port));
}
}
info->rfdesc = relay_port_v6_socket;
info->wfdesc = relay_port_v6_socket;
relay_port_v6_socket_references++;
}
#endif
if (req_multi)
if_register_multicast(info);
@ -617,6 +654,16 @@ if_deregister6(struct interface_info *info) {
global_v6_socket_references--;
info->rfdesc = -1;
info->wfdesc = -1;
#if defined(RELAY_PORT)
} else if (relay_port &&
(info->rfdesc == relay_port_v6_socket) &&
(info->wfdesc == relay_port_v6_socket) &&
(relay_port_v6_socket_references > 0)) {
/* Dereference the relay port v6 socket. */
relay_port_v6_socket_references--;
info->rfdesc = -1;
info->wfdesc = -1;
#endif
} else {
log_fatal("Impossible condition at %s:%d", MDL);
}
@ -633,12 +680,23 @@ if_deregister6(struct interface_info *info) {
}
}
if (!no_global_v6_socket &&
(global_v6_socket_references == 0)) {
close(global_v6_socket);
global_v6_socket = -1;
if (!no_global_v6_socket) {
if (global_v6_socket_references == 0) {
close(global_v6_socket);
global_v6_socket = -1;
log_info("Unbound from *:%d", ntohs(local_port));
log_info("Unbound from *:%d",
(int) ntohs(local_port));
}
#if defined(RELAY_PORT)
if (relay_port && (relay_port_v6_socket_references == 0)) {
close(relay_port_v6_socket);
relay_port_v6_socket = -1;
log_info("Unbound from relay port *:%d",
(int) ntohs(relay_port));
}
#endif
}
}
#endif /* DHCPv6 */

View File

@ -557,12 +557,6 @@ static struct option dhcpv6_options[] = {
{ "solmax-rt", "L", &dhcpv6_universe, 82, 1 },
{ "inf-max-rt", "L", &dhcpv6_universe, 83, 1 },
#endif
#if defined(RFC7710_OPTIONS)
{ "v6-captive-portal", "t", &dhcpv6_universe, 103, 1 },
#endif
#if defined(RFC6153_OPTIONS)
{ "ipv6-address-andsf", "6A", &dhcpv6_universe, 143, 1 },
#endif
/* RFC7341 OPTIONS */
#if defined(RFC7341_OPTIONS)
@ -570,6 +564,16 @@ static struct option dhcpv6_options[] = {
{ "dhcp4-o-dhcp6-server", "6A", &dhcpv6_universe, 88, 1 },
#endif
#if defined(RFC7710_OPTIONS)
{ "v6-captive-portal", "t", &dhcpv6_universe, 103, 1 },
#endif
{ "relay-source-port", "S", &dhcpv6_universe, 135, 1 },
#if defined(RFC6153_OPTIONS)
{ "ipv6-address-andsf", "6A", &dhcpv6_universe, 143, 1 },
#endif
{ NULL, NULL, NULL, 0, 0 }
};

View File

@ -156,6 +156,9 @@ void if_deregister_send (info)
XXX Changes to the filter program may require changes to the constant
offsets used in if_register_send to patch the UPF program! XXX */
#if defined(RELAY_PORT)
#error "Relay port is not yet supported for UPF"
#endif
void if_register_receive (info)
struct interface_info *info;

25
configure vendored
View File

@ -768,6 +768,7 @@ enable_tracing
enable_delayed_ack
enable_dhcpv6
enable_dhcpv4o6
enable_relay_port
enable_paranoia
enable_early_chroot
enable_ipv4_pktinfo
@ -1446,6 +1447,7 @@ Optional Features:
--enable-dhcpv6 enable support for DHCPv6 (default is yes)
--enable-dhcpv4o6 enable support for DHCPv4-over-DHCPv6 (default is
no)
--enable-relay-port enable support for relay port (default is no)
--enable-paranoia enable support for chroot/setuid (default is no)
--enable-early-chroot enable chrooting prior to configuration (default is
no)
@ -5487,6 +5489,19 @@ else
enable_dhcpv4o6="no"
fi
# Relay port (draft-ietf-dhc-relay-port-10.txt) optional compile-time feature.
# Check whether --enable-relay-port was given.
if test "${enable_relay_port+set}" = set; then :
enableval=$enable_relay_port;
fi
# Relay port is off by default (for now)
if test "$enable_relay_port" = "yes"; then
$as_echo "#define RELAY_PORT 1" >>confdefs.h
fi
# PARANOIA is off by default (until we can test it with all features)
# Check whether --enable-paranoia was given.
if test "${enable_paranoia+set}" = set; then :
@ -6207,6 +6222,7 @@ fi
done
# needed for linux/filter.h on old systems
relay_port_supported="no"
ac_fn_c_check_header_compile "$LINENO" "linux/filter.h" "ac_cv_header_linux_filter_h" "
#ifdef HAVE_LINUX_TYPES_H
#include <linux/types.h>
@ -6223,6 +6239,7 @@ then
$as_echo "#define HAVE_LPF 1" >>confdefs.h
relay_port_supported="yes"
else
ac_fn_c_check_header_mongrel "$LINENO" "sys/dlpi.h" "ac_cv_header_sys_dlpi_h" "$ac_includes_default"
if test "x$ac_cv_header_sys_dlpi_h" = xyes; then :
@ -6247,10 +6264,17 @@ fi
$as_echo "#define HAVE_BPF 1" >>confdefs.h
relay_port_supported="yes"
fi
fi
fi
if test "$enable_relay_port" = "yes"; then
if test "$relay_port_supported" != "yes"; then
as_fn_error $? "--enable-relay-port requires BPF or LPF" "$LINENO" 5
fi
fi
# SIOCGLIFCONF uses some transport structures. Trick is not all platforms
# use the same structures. We like to use 'struct lifconf' and 'struct
# lifreq', but we'll use these other structures if they're present. HPUX
@ -9014,6 +9038,7 @@ Features:
dhcpv6: $enable_dhcpv6
delayed-ack: $enable_delayed_ack
dhcpv4o6: $enable_dhcpv4o6
relay-port: $enable_relay_port
Developer:
ATF unittests : $atf_path

View File

@ -177,6 +177,15 @@ else
enable_dhcpv4o6="no"
fi
# Relay port (draft-ietf-dhc-relay-port-10.txt) optional compile-time feature.
AC_ARG_ENABLE(relay-port,
AS_HELP_STRING([--enable-relay-port],[enable support for relay port (default is no)]))
# Relay port is off by default (for now)
if test "$enable_relay_port" = "yes"; then
AC_DEFINE([RELAY_PORT], [1],
[Define to 1 to include relay port support.])
fi
# PARANOIA is off by default (until we can test it with all features)
AC_ARG_ENABLE(paranoia,
AS_HELP_STRING([--enable-paranoia],[enable support for chroot/setuid (default is no)]))
@ -521,6 +530,7 @@ AC_CHECK_HEADERS(ifaddrs.h)
# figure out what IPv4 interface code to use
AC_CHECK_HEADERS(linux/types.h) # needed for linux/filter.h on old systems
relay_port_supported="no"
AC_CHECK_HEADER(linux/filter.h, DO_LPF=1, ,
[
#ifdef HAVE_LINUX_TYPES_H
@ -531,6 +541,7 @@ if test -n "$DO_LPF"
then
AC_DEFINE([HAVE_LPF], [1],
[Define to 1 to use the Linux Packet Filter interface code.])
relay_port_supported="yes"
else
AC_CHECK_HEADER(sys/dlpi.h, DO_DLPI=1)
if test -n "$DO_DLPI"
@ -544,10 +555,17 @@ else
AC_DEFINE([HAVE_BPF], [1],
[Define to 1 to use the
Berkeley Packet Filter interface code.])
relay_port_supported="yes"
fi
fi
fi
if test "$enable_relay_port" = "yes"; then
if test "$relay_port_supported" != "yes"; then
AC_MSG_ERROR([--enable-relay-port requires BPF or LPF])
fi
fi
# SIOCGLIFCONF uses some transport structures. Trick is not all platforms
# use the same structures. We like to use 'struct lifconf' and 'struct
# lifreq', but we'll use these other structures if they're present. HPUX
@ -1035,6 +1053,7 @@ Features:
dhcpv6: $enable_dhcpv6
delayed-ack: $enable_delayed_ack
dhcpv4o6: $enable_dhcpv4o6
relay-port: $enable_relay_port
Developer:
ATF unittests : $atf_path

View File

@ -178,6 +178,15 @@ else
enable_dhcpv4o6="no"
fi
# Relay port (draft-ietf-dhc-relay-port-10.txt) optional compile-time feature.
AC_ARG_ENABLE(relay-port,
AS_HELP_STRING([--enable-relay-port],[enable support for relay port (default is no)]))
# Relay port is off by default (for now)
if test "$enable_relay_port" = "yes"; then
AC_DEFINE([RELAY_PORT], [1],
[Define to 1 to include relay port support.])
fi
# PARANOIA is off by default (until we can test it with all features)
AC_ARG_ENABLE(paranoia,
AS_HELP_STRING([--enable-paranoia],[enable support for chroot/setuid (default is no)]))
@ -522,6 +531,7 @@ AC_CHECK_HEADERS(ifaddrs.h)
# figure out what IPv4 interface code to use
AC_CHECK_HEADERS(linux/types.h) # needed for linux/filter.h on old systems
relay_port_supported="no"
AC_CHECK_HEADER(linux/filter.h, DO_LPF=1, ,
[
#ifdef HAVE_LINUX_TYPES_H
@ -532,6 +542,7 @@ if test -n "$DO_LPF"
then
AC_DEFINE([HAVE_LPF], [1],
[Define to 1 to use the Linux Packet Filter interface code.])
relay_port_supported="yes"
else
AC_CHECK_HEADER(sys/dlpi.h, DO_DLPI=1)
if test -n "$DO_DLPI"
@ -545,10 +556,17 @@ else
AC_DEFINE([HAVE_BPF], [1],
[Define to 1 to use the
Berkeley Packet Filter interface code.])
relay_port_supported="yes"
fi
fi
fi
if test "$enable_relay_port" = "yes"; then
if test "$relay_port_supported" != "yes"; then
AC_MSG_ERROR([--enable-relay-port requires BPF or LPF])
fi
fi
# SIOCGLIFCONF uses some transport structures. Trick is not all platforms
# use the same structures. We like to use 'struct lifconf' and 'struct
# lifreq', but we'll use these other structures if they're present. HPUX
@ -1040,6 +1058,7 @@ Features:
dhcpv6: $enable_dhcpv6
delayed-ack: $enable_delayed_ack
dhcpv4o6: $enable_dhcpv4o6
relay-port: $enable_relay_port
Developer:
ATF unittests : $atf_path

View File

@ -183,6 +183,15 @@ else
enable_dhcpv4o6="no"
fi
# Relay port (draft-ietf-dhc-relay-port-10.txt) optional compile-time feature.
AC_ARG_ENABLE(relay-port,
AS_HELP_STRING([--enable-relay-port],[enable support for relay port (default is no)]))
# Relay port is off by default (for now)
if test "$enable_relay_port" = "yes"; then
AC_DEFINE([RELAY_PORT], [1],
[Define to 1 to include relay port support.])
fi
# PARANOIA is off by default (until we can test it with all features)
AC_ARG_ENABLE(paranoia,
AS_HELP_STRING([--enable-paranoia],[enable support for chroot/setuid (default is no)]))
@ -527,6 +536,7 @@ AC_CHECK_HEADERS(ifaddrs.h)
# figure out what IPv4 interface code to use
AC_CHECK_HEADERS(linux/types.h) # needed for linux/filter.h on old systems
relay_port_supported="no"
AC_CHECK_HEADER(linux/filter.h, DO_LPF=1, ,
[
#ifdef HAVE_LINUX_TYPES_H
@ -537,6 +547,7 @@ if test -n "$DO_LPF"
then
AC_DEFINE([HAVE_LPF], [1],
[Define to 1 to use the Linux Packet Filter interface code.])
relay_port_supported="yes"
else
AC_CHECK_HEADER(sys/dlpi.h, DO_DLPI=1)
if test -n "$DO_DLPI"
@ -550,10 +561,17 @@ else
AC_DEFINE([HAVE_BPF], [1],
[Define to 1 to use the
Berkeley Packet Filter interface code.])
relay_port_supported="yes"
fi
fi
fi
if test "$enable_relay_port" = "yes"; then
if test "$relay_port_supported" != "yes"; then
AC_MSG_ERROR([--enable-relay-port requires BPF or LPF])
fi
fi
# SIOCGLIFCONF uses some transport structures. Trick is not all platforms
# use the same structures. We like to use 'struct lifconf' and 'struct
# lifreq', but we'll use these other structures if they're present. HPUX
@ -1076,6 +1094,7 @@ Features:
dhcpv6: $enable_dhcpv6
delayed-ack: $enable_delayed_ack
dhcpv4o6: $enable_dhcpv4o6
relay-port: $enable_relay_port
Developer:
ATF unittests : $atf_path

View File

@ -177,6 +177,15 @@ else
enable_dhcpv4o6="no"
fi
# Relay port (draft-ietf-dhc-relay-port-10.txt) optional compile-time feature.
AC_ARG_ENABLE(relay-port,
AS_HELP_STRING([--enable-relay-port],[enable support for relay port (default is no)]))
# Relay port is off by default (for now)
if test "$enable_relay_port" = "yes"; then
AC_DEFINE([RELAY_PORT], [1],
[Define to 1 to include relay port support.])
fi
# PARANOIA is off by default (until we can test it with all features)
AC_ARG_ENABLE(paranoia,
AS_HELP_STRING([--enable-paranoia],[enable support for chroot/setuid (default is no)]))
@ -521,6 +530,7 @@ AC_CHECK_HEADERS(ifaddrs.h)
# figure out what IPv4 interface code to use
AC_CHECK_HEADERS(linux/types.h) # needed for linux/filter.h on old systems
relay_port_supported="no"
AC_CHECK_HEADER(linux/filter.h, DO_LPF=1, ,
[
#ifdef HAVE_LINUX_TYPES_H
@ -531,6 +541,7 @@ if test -n "$DO_LPF"
then
AC_DEFINE([HAVE_LPF], [1],
[Define to 1 to use the Linux Packet Filter interface code.])
relay_port_supported="yes"
else
AC_CHECK_HEADER(sys/dlpi.h, DO_DLPI=1)
if test -n "$DO_DLPI"
@ -544,10 +555,17 @@ else
AC_DEFINE([HAVE_BPF], [1],
[Define to 1 to use the
Berkeley Packet Filter interface code.])
relay_port_supported="yes"
fi
fi
fi
if test "$enable_relay_port" = "yes"; then
if test "$relay_port_supported" != "yes"; then
AC_MSG_ERROR([--enable-relay-port requires BPF or LPF])
fi
fi
# SIOCGLIFCONF uses some transport structures. Trick is not all platforms
# use the same structures. We like to use 'struct lifconf' and 'struct
# lifreq', but we'll use these other structures if they're present. HPUX
@ -1035,6 +1053,7 @@ Features:
dhcpv6: $enable_dhcpv6
delayed-ack: $enable_delayed_ack
dhcpv4o6: $enable_dhcpv4o6
relay-port: $enable_relay_port
Developer:
ATF unittests : $atf_path

View File

@ -219,3 +219,10 @@ to start the server.
Finally note in the configuration file the use of the shared-network to
connect the DHCPv4 and DHCPv6 subnets.
USE WITH DHCPv6 RELAY(s)
If the DHCPv6 infrastructure uses one (or more) relay because the client
and the server are not on the same link the best choice is to put the
first (closest to client) relay address in the dhcp4-o-dhcp6-server
option so the same path between the DHCPv6 client part and server part
will be used for DHCPv6 and DHCPv4-over-DHCPv6 traffic.

View File

@ -157,6 +157,9 @@
/* Define to any value to include Ari's PARANOIA patch. */
#undef PARANOIA
/* Define to 1 to include relay port support. */
#undef RELAY_PORT
/* The size of `struct iaddr *', as computed by sizeof. */
#undef SIZEOF_STRUCT_IADDR_P

View File

@ -183,6 +183,8 @@ struct dhcp_packet {
#define RAI_REMOTE_ID 2
#define RAI_AGENT_ID 3
#define RAI_LINK_SELECT 5
/* not yet assigned but next free value */
#define RAI_RELAY_PORT 19
/* FQDN suboptions: */
#define FQDN_NO_CLIENT_UPDATE 1

View File

@ -115,6 +115,8 @@
#define D6O_V6_PCP_SERVER 86 /* RFC7291 */
#define D6O_DHCPV4_MSG 87 /* RFC7341 */
#define D6O_DHCP4_O_DHCP6_SERVER 88 /* RFC7341 */
/* not yet assigned but next free value */
#define D6O_RELAY_SOURCE_PORT 135 /* I-D */
/*
* Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007, 5460.

View File

@ -469,9 +469,12 @@ struct packet {
*/
isc_boolean_t unicast;
/* Propogates server value SV_ECHO_CLIENT_ID so it is available
/* Propagates server value SV_ECHO_CLIENT_ID so it is available
* in cons_options() */
int sv_echo_client_id;
/* Relay port check */
isc_boolean_t relay_source_port;
};
/*
@ -2440,6 +2443,8 @@ void eval_network_statements(struct option_state **options,
struct packet *packet,
struct group *network_group);
u_int16_t dhcp_check_relayport(struct packet *packet);
/* dhcpleasequery.c */
void dhcpleasequery (struct packet *, int);
void dhcpv6_leasequery (struct data_string *, struct packet *);
@ -2833,6 +2838,7 @@ extern int bind_local_address6;
extern u_int16_t local_port;
extern u_int16_t remote_port;
extern u_int16_t relay_port;
extern int dhcpv4_over_dhcpv6;
extern int (*dhcp_interface_setup_hook) (struct interface_info *,
struct iaddr *);

View File

@ -43,6 +43,9 @@ dhcrelay - Dynamic Host Configuration Protocol Relay Agent
[
.B -p
.I port
|
.B -rp
.I relay-port
]
[
.B -c
@ -112,6 +115,9 @@ dhcrelay - Dynamic Host Configuration Protocol Relay Agent
[
.B -p
.I port
|
.B -rp
.I relay-port
]
[
.B -c
@ -174,7 +180,7 @@ the command line, to which DHCP/BOOTP queries should be relayed.
.PP
\fIOptions available for both DHCPv4 and DHCPv6:\fR
.TP
-c COUNT
-c \fIcount\fR
Maximum hop count. When forwarding packets, dhcrelay discards packets
which have reached a hop count of COUNT. Default is 10. Maximum is 255.
.TP
@ -182,9 +188,17 @@ which have reached a hop count of COUNT. Default is 10. Maximum is 255.
Force dhcrelay to run as a foreground process. Useful when running
dhcrelay under a debugger, or running out of inittab on System V systems.
.TP
-p PORT
-p \fIport\fR
Listen and transmit on port PORT. This is mostly useful for debugging
purposes. Default is port 67 for DHCPv4/BOOTP, or port 547 for DHCPv6.
Incompatible with \fB-rp\fR.
.TP
-rp \fIrelay-port\fR
Alternative source port for upstream (i.e toward the server) messages
with DHCPv4 RAI relay-port sub-option or DHCPv6 relay-source-port
option. Relay port support is only available if the code was compiled
with (./configure --enable-relay-port) and requires LPF or BPF link
layer access.
.TP
-q
Quiet mode. Prevents dhcrelay6 from printing its network configuration
@ -209,7 +223,7 @@ be the printable name of the interface on which the client request was
received. The client supports inclusion of a Remote ID suboption as well,
but this is not used by default.
.TP
-A LENGTH
-A \fIlength\fR
Specify the maximum packet size to send to a DHCPv4/BOOTP server. This
might be done to allow sufficient space for addition of relay agent
options while still fitting into the Ethernet MTU size.

View File

@ -152,8 +152,30 @@ static const char url[] =
char *progname;
#ifdef DHCPv6
#ifdef RELAY_PORT
#define DHCRELAY_USAGE \
"Usage: %s [-4] [-d] [-q] [-a] [-D]\n"\
"Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \
" [-A <length>] [-c <hops>]\n" \
" [-p <port> | -rp <relay-port>]\n" \
" [-pf <pid-file>] [--no-pid]\n"\
" [-m append|replace|forward|discard]\n" \
" [-i interface0 [ ... -i interfaceN]\n" \
" [-iu interface0 [ ... -iu interfaceN]\n" \
" [-id interface0 [ ... -id interfaceN]\n" \
" [-U interface]\n" \
" server0 [ ... serverN]\n\n" \
" %s -6 [-d] [-q] [-I] [-c <hops>]\n" \
" [-p <port> | -rp <relay-port>]\n" \
" [-pf <pid-file>] [--no-pid]\n" \
" [-s <subscriber-id>]\n" \
" -l lower0 [ ... -l lowerN]\n" \
" -u upper0 [ ... -u upperN]\n" \
" lower (client link): [address%%]interface[#index]\n" \
" upper (server link): [address%%]interface\n\n" \
" %s {--version|--help|-h}"
#else
#define DHCRELAY_USAGE \
"Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \
" [-A <length>] [-c <hops>] [-p <port>]\n" \
" [-pf <pid-file>] [--no-pid]\n"\
" [-m append|replace|forward|discard]\n" \
@ -170,6 +192,20 @@ char *progname;
" lower (client link): [address%%]interface[#index]\n" \
" upper (server link): [address%%]interface\n\n" \
" %s {--version|--help|-h}"
#endif
#else /* !DHCPv6 */
#ifdef RELAY_PORT
#define DHCRELAY_USAGE \
"Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>]\n" \
" [-p <port> | -rp <relay-port>]\n" \
" [-pf <pid-file>] [--no-pid]\n" \
" [-m append|replace|forward|discard]\n" \
" [-i interface0 [ ... -i interfaceN]\n" \
" [-iu interface0 [ ... -iu interfaceN]\n" \
" [-id interface0 [ ... -id interfaceN]\n" \
" [-U interface]\n" \
" server0 [ ... serverN]\n\n" \
" %s {--version|--help|-h}"
#else
#define DHCRELAY_USAGE \
"Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
@ -182,6 +218,7 @@ char *progname;
" server0 [ ... serverN]\n\n" \
" %s {--version|--help|-h}"
#endif
#endif
/*!
*
@ -199,6 +236,12 @@ char *progname;
* \return Nothing
*/
static const char use_noarg[] = "No argument for command: %s";
#ifdef RELAY_PORT
static const char use_port_defined[] = "Port already set, %s inappropriate";
#if !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE)
static const char bpf_sock_support[] = "Only LPF and BPF are supported: %s";
#endif
#endif
#ifdef DHCPv6
static const char use_badproto[] = "Protocol already set, %s inappropriate";
static const char use_v4command[] = "Command not used for DHCPv6: %s";
@ -236,6 +279,9 @@ main(int argc, char **argv) {
int quiet = 0;
int fd;
int i;
#ifdef RELAY_PORT
int port_defined = 0;
#endif
#ifdef DHCPv6
struct stream_list *sl = NULL;
int local_family_set = 0;
@ -349,9 +395,26 @@ main(int argc, char **argv) {
} else if (!strcmp(argv[i], "-p")) {
if (++i == argc)
usage(use_noarg, argv[i-1]);
#ifdef RELAY_PORT
if (port_defined)
usage(use_port_defined, argv[i-1]);
port_defined = 1;
#endif
local_port = validate_port(argv[i]);
log_debug("binding to user-specified port %d",
ntohs(local_port));
#ifdef RELAY_PORT
} else if (!strcmp(argv[i], "-rp")) {
if (++i == argc)
usage(use_noarg, argv[i-1]);
if (port_defined)
usage(use_port_defined, argv[i-1]);
port_defined = 1;
relay_port = validate_port(argv[i]);
log_debug("binding to user-specified relay port %d",
ntohs(relay_port));
add_agent_options = 1;
#endif
} else if (!strcmp(argv[i], "-c")) {
int hcount;
if (++i == argc)
@ -572,6 +635,12 @@ main(int argc, char **argv) {
}
}
#if defined(RELAY_PORT) && \
!defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE)
if (relay_port && (local_family == AF_INET))
usage(bpf_sock_support, "-rp");
#endif
/*
* If the user didn't specify a pid file directly
* find one from environment variables or defaults
@ -1259,6 +1328,12 @@ add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
optlen += 6;
}
#ifdef RELAY_PORT
if (relay_port) {
optlen += 2;
}
#endif
/* We do not support relay option fragmenting(multiple options to
* support an option data exceeding 255 bytes).
*/
@ -1303,6 +1378,14 @@ add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
log_debug ("Adding link selection suboption"
" with addr: %s", inet_ntoa(giaddr));
}
#ifdef RELAY_PORT
/* draft-ietf-dhc-relay-port-10.txt section 5.1 */
if (relay_port) {
*sp++ = RAI_RELAY_PORT;
*sp++ = 0u;
}
#endif
} else {
++agent_option_errors;
log_error("No room in packet (used %d of %d) "
@ -1548,6 +1631,9 @@ setup_streams(void) {
static const int required_forw_opts[] = {
D6O_INTERFACE_ID,
D6O_SUBSCRIBER_ID,
#if defined(RELAY_PORT)
D6O_RELAY_SOURCE_PORT,
#endif
D6O_RELAY_MSG,
0
};
@ -1562,6 +1648,7 @@ process_up6(struct packet *packet, struct stream_list *dp) {
struct dhcpv6_relay_packet *relay;
struct option_state *opts;
struct stream_list *up;
u_int16_t relay_client_port = 0;
/* Check if the message should be relayed to the server. */
switch (packet->dhcpv6_msg_type) {
@ -1622,6 +1709,10 @@ process_up6(struct packet *packet, struct stream_list *dp) {
}
memset(&relay->link_address, 0, 16);
}
if (packet->client_port != htons(547)) {
relay_client_port = packet->client_port;
}
} else {
relay->hop_count = 0;
if (!dp)
@ -1674,6 +1765,30 @@ process_up6(struct packet *packet, struct stream_list *dp) {
}
#if defined(RELAY_PORT)
/*
* If we use a non-547 UDP source port or if we have received
* from a downstream relay agent uses a non-547 port, we need
* to include the RELAY-SOURCE-PORT option. The "Downstream
* UDP Port" field value in the option allow us to send
* relay-reply message back to the downstream relay agent
* with the correct UDP source port.
*/
if (relay_port || relay_client_port) {
if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
(unsigned char *) &relay_client_port,
sizeof(u_int16_t),
D6O_RELAY_SOURCE_PORT, 0)) {
log_error("Can't save relay-source-port.");
option_state_dereference(&opts, MDL);
return;
}
}
#else
/* Avoid unused but set warning, */
(void)(relay_client_port);
#endif
/* Add the relay-msg carrying the packet. */
if (!save_option_buffer(&dhcpv6_universe, opts,
NULL, (unsigned char *) packet->raw,
@ -1708,6 +1823,9 @@ process_down6(struct packet *packet) {
struct data_string relay_msg;
const struct dhcpv6_packet *msg;
struct data_string if_id;
#if defined(RELAY_PORT)
struct data_string down_port;
#endif
struct sockaddr_in6 to;
struct iaddr peer;
@ -1729,6 +1847,9 @@ process_down6(struct packet *packet) {
/* Inits. */
memset(&relay_msg, 0, sizeof(relay_msg));
memset(&if_id, 0, sizeof(if_id));
#if defined(RELAY_PORT)
memset(&down_port, 0, sizeof(down_port));
#endif
memset(&to, 0, sizeof(to));
to.sin6_family = AF_INET6;
#ifdef HAVE_SA_LEN
@ -1799,6 +1920,37 @@ process_down6(struct packet *packet) {
/* Relay-Reply of for another relay, not a client. */
case DHCPV6_RELAY_REPL:
to.sin6_port = local_port;
#if defined(RELAY_PORT)
oc = lookup_option(&dhcpv6_universe, packet->options,
D6O_RELAY_SOURCE_PORT);
if (oc != NULL) {
u_int16_t down_relay_port;
memset(&down_port, 0, sizeof(down_port));
if (!evaluate_option_cache(&down_port, packet, NULL,
NULL, packet->options, NULL,
&global_scope, oc, MDL) ||
(down_port.len != sizeof(u_int16_t))) {
log_info("Can't evaluate down "
"relay-source-port.");
goto cleanup;
}
memcpy(&down_relay_port, down_port.data,
sizeof(u_int16_t));
/*
* If the down_relay_port value is non-zero,
* that means our downstream relay agent uses
* a non-547 UDP source port sending
* relay-forw message to us. We need to use
* the same UDP port sending reply back.
*/
if (down_relay_port) {
to.sin6_port = down_relay_port;
}
}
#endif
/* Fall into: */
case DHCPV6_ADVERTISE:

View File

@ -1063,6 +1063,20 @@ void dhcpdecline (packet, ms_nulltp)
lease_dereference (&lease, MDL);
}
#if defined(RELAY_PORT)
u_int16_t dhcp_check_relayport(packet)
struct packet *packet;
{
if (lookup_option(&agent_universe,
packet->options,
RAI_RELAY_PORT) != NULL) {
return (packet->client_port);
}
return (0);
}
#endif
void dhcpinform (packet, ms_nulltp)
struct packet *packet;
int ms_nulltp;
@ -1084,6 +1098,7 @@ void dhcpinform (packet, ms_nulltp)
struct interface_info *interface;
int result, h_m_client_ip = 0;
struct host_decl *host = NULL, *hp = NULL, *h;
u_int16_t relay_port = 0;
#if defined (DEBUG_INFORM_HOST)
int h_w_fixed_addr = 0;
#endif
@ -1146,6 +1161,10 @@ void dhcpinform (packet, ms_nulltp)
return;
}
#if defined(RELAY_PORT)
relay_port = dhcp_check_relayport(packet);
#endif
/* Find the subnet that the client is on.
* CC: Do the link selection / subnet selection
*/
@ -1696,7 +1715,7 @@ void dhcpinform (packet, ms_nulltp)
*/
if (!raw.ciaddr.s_addr && gip.len) {
memcpy(&to.sin_addr, gip.iabuf, 4);
to.sin_port = local_port;
to.sin_port = relay_port ? relay_port : local_port;
raw.flags |= htons(BOOTP_BROADCAST);
} else {
gip.len = 0;
@ -1753,6 +1772,7 @@ void nak_lease (packet, cip, network_group)
unsigned char nak = DHCPNAK;
struct packet outgoing;
unsigned i;
u_int16_t relay_port = 0;
struct option_state *options = (struct option_state *)0;
struct option_cache *oc = (struct option_cache *)0;
struct option_state *eval_options = NULL;
@ -1781,6 +1801,10 @@ void nak_lease (packet, cip, network_group)
save_option (&dhcp_universe, options, oc);
option_cache_dereference (&oc, MDL);
#if defined(RELAY_PORT)
relay_port = dhcp_check_relayport(packet);
#endif
/* Set DHCP_MESSAGE to whatever the message is */
if (!option_cache_allocate (&oc, MDL)) {
log_error ("No memory for DHCPNAK message type.");
@ -1929,7 +1953,7 @@ void nak_lease (packet, cip, network_group)
if (raw.giaddr.s_addr) {
to.sin_addr = raw.giaddr;
if (raw.giaddr.s_addr != htonl (INADDR_LOOPBACK))
to.sin_port = local_port;
to.sin_port = relay_port ? relay_port : local_port;
else
to.sin_port = remote_port; /* for testing. */
@ -3752,6 +3776,7 @@ void dhcp_reply (lease)
int result;
struct lease_state *state = lease -> state;
int nulltp, bootpp, unicastp = 1;
u_int16_t relay_port = 0;
struct data_string d1;
const char *s;
@ -3921,11 +3946,15 @@ void dhcp_reply (lease)
#endif
memset (to.sin_zero, 0, sizeof to.sin_zero);
#if defined(RELAY_PORT)
relay_port = dhcp_check_relayport(state->packet);
#endif
/* If this was gatewayed, send it back to the gateway... */
if (raw.giaddr.s_addr) {
to.sin_addr = raw.giaddr;
if (raw.giaddr.s_addr != htonl (INADDR_LOOPBACK))
to.sin_port = local_port;
to.sin_port = relay_port ? relay_port : local_port;
else
to.sin_port = remote_port; /* For debugging. */

View File

@ -152,6 +152,7 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
u_int32_t time_rebinding;
u_int32_t time_expiry;
u_int32_t client_last_transaction_time;
u_int16_t relay_port = 0;
struct sockaddr_in to;
struct in_addr siaddr;
struct data_string prl;
@ -660,12 +661,16 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
#endif
memset(to.sin_zero, 0, sizeof(to.sin_zero));
#if defined(RELAY_PORT)
relay_port = dhcp_check_relayport(packet);
#endif
/*
* Leasequery packets are be sent to the gateway address.
*/
to.sin_addr = packet->raw->giaddr;
if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) {
to.sin_port = local_port;
to.sin_port = relay_port ? relay_port : local_port;
} else {
to.sin_port = remote_port; /* XXXSK: For debugging. */
}

View File

@ -27,6 +27,14 @@ static void send_dhcpv4_response(struct data_string *raw);
static void recv_dhcpv4_query(struct data_string *raw);
static void dhcp4o6_dhcpv4_query(struct data_string *reply_ret,
struct packet *packet);
struct udp_data4o6 {
u_int16_t src_port;
u_int8_t rsp_opt_exist;
u_int8_t reserved;
};
static int offset_data4o6 = 36; /* 16+16+4 */
#endif
/*
@ -211,7 +219,7 @@ isc_result_t dhcpv4o6_handler(omapi_object_t *h) {
cc = recv(dhcp4o6_fd, buf, sizeof(buf), 0);
if (cc < DHCP_FIXED_NON_UDP + 32)
if (cc < DHCP_FIXED_NON_UDP + offset_data4o6)
return ISC_R_UNEXPECTED;
memset(&raw, 0, sizeof(raw));
if (!buffer_allocate(&raw.buffer, cc, MDL)) {
@ -237,7 +245,7 @@ isc_result_t dhcpv4o6_handler(omapi_object_t *h) {
* \brief Send the DHCPv4-response back to the DHCPv6 side
* (DHCPv6 server function)
*
* Format: interface:16 + address:16 + DHCPv6 DHCPv4-response message
* Format: interface:16 + address:16 + udp:4 + DHCPv6 DHCPv4-response message
*
* \param raw the IPC message content
*/
@ -246,6 +254,7 @@ static void send_dhcpv4_response(struct data_string *raw) {
char name[16 + 1];
struct sockaddr_in6 to_addr;
char pbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
struct udp_data4o6 udp_data;
int send_ret;
memset(name, 0, sizeof(name));
@ -263,26 +272,32 @@ static void send_dhcpv4_response(struct data_string *raw) {
memset(&to_addr, 0, sizeof(to_addr));
to_addr.sin6_family = AF_INET6;
memcpy(&to_addr.sin6_addr, raw->data + 16, 16);
if ((raw->data[32] == DHCPV6_RELAY_FORW) ||
(raw->data[32] == DHCPV6_RELAY_REPL)) {
to_addr.sin6_port = local_port;
memset(&udp_data, 0, sizeof(udp_data));
memcpy(&udp_data, raw->data + 32, 4);
if ((raw->data[36] == DHCPV6_RELAY_FORW) ||
(raw->data[36] == DHCPV6_RELAY_REPL)) {
if (udp_data.rsp_opt_exist) {
to_addr.sin6_port = udp_data.src_port;
} else {
to_addr.sin6_port = local_port;
}
} else {
to_addr.sin6_port = remote_port;
}
log_info("send_dhcpv4_response(): sending %s on %s to %s port %d",
dhcpv6_type_names[raw->data[32]],
dhcpv6_type_names[raw->data[36]],
name,
inet_ntop(AF_INET6, raw->data + 16, pbuf, sizeof(pbuf)),
ntohs(to_addr.sin6_port));
send_ret = send_packet6(ip, raw->data + 32, raw->len - 32, &to_addr);
send_ret = send_packet6(ip, raw->data + 36, raw->len - 36, &to_addr);
if (send_ret < 0) {
log_error("send_dhcpv4_response: send_packet6(): %m");
} else if (send_ret != raw->len - 32) {
} else if (send_ret != raw->len - 36) {
log_error("send_dhcpv4_response: send_packet6() "
"sent %d of %d bytes",
send_ret, raw->len - 32);
send_ret, raw->len - 36);
}
}
#endif /* DHCP4o6 */
@ -857,6 +872,9 @@ static const int required_opts_solicit[] = {
};
static const int required_opts_agent[] = {
D6O_INTERFACE_ID,
#if defined(RELAY_PORT)
D6O_RELAY_SOURCE_PORT,
#endif
D6O_RELAY_MSG,
0
};
@ -6854,6 +6872,35 @@ dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
data_string_forget(&a_opt, MDL);
}
#if defined(RELAY_PORT)
/*
* Append the relay_source_port option if present.
*/
oc = lookup_option(&dhcpv6_universe, packet->options,
D6O_RELAY_SOURCE_PORT);
if (oc != NULL) {
if (!evaluate_option_cache(&a_opt, packet,
NULL, NULL,
packet->options, NULL,
&global_scope, oc, MDL)) {
log_error("dhcpv6_relay_forw: error evaluating "
"Relay Source Port.");
goto exit;
}
if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL,
(unsigned char *)a_opt.data,
a_opt.len,
D6O_RELAY_SOURCE_PORT, 0)) {
log_error("dhcpv6_relay_forw: error saving "
"Relay Source Port.");
goto exit;
}
data_string_forget(&a_opt, MDL);
packet->relay_source_port = ISC_TRUE;
}
#endif
/*
* Append our encapsulated stuff for caller.
*/
@ -7147,6 +7194,35 @@ dhcp4o6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
data_string_forget(&a_opt, MDL);
}
#if defined(RELAY_PORT)
/*
* Append the relay_source_port option if present.
*/
oc = lookup_option(&dhcpv6_universe, packet->options,
D6O_RELAY_SOURCE_PORT);
if (oc != NULL) {
if (!evaluate_option_cache(&a_opt, packet,
NULL, NULL,
packet->options, NULL,
&global_scope, oc, MDL)) {
log_error("dhcpv4o6_relay_forw: error evaluating "
"Relay Source Port.");
goto exit;
}
if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL,
(unsigned char *)a_opt.data,
a_opt.len,
D6O_RELAY_SOURCE_PORT, 0)) {
log_error("dhcpv4o6_relay_forw: error saving "
"Relay Source Port.");
goto exit;
}
data_string_forget(&a_opt, MDL);
packet->relay_source_port = ISC_TRUE;
}
#endif
/*
* Append our encapsulated stuff for caller.
*/
@ -7436,12 +7512,13 @@ exit:
* \brief Forward a DHCPv4-query message to the DHCPv4 side
* (DHCPv6 server function)
*
* Format: interface:16 + address:16 + DHCPv6 DHCPv4-query message
* Format: interface:16 + address:16 + udp:4 + DHCPv6 DHCPv4-query message
*
* \brief packet the DHCPv6 DHCPv4-query message
*/
static void forw_dhcpv4_query(struct packet *packet) {
struct data_string ds;
struct udp_data4o6 udp_data;
unsigned len;
int cc;
@ -7458,7 +7535,7 @@ static void forw_dhcpv4_query(struct packet *packet) {
}
/* Get a buffer. */
len = packet->packet_length + 32;
len = packet->packet_length + 36;
memset(&ds, 0, sizeof(ds));
if (!buffer_allocate(&ds.buffer, len, MDL)) {
log_error("forw_dhcpv4_query: "
@ -7472,7 +7549,10 @@ static void forw_dhcpv4_query(struct packet *packet) {
strncpy((char *)ds.buffer->data, packet->interface->name, 16);
memcpy(ds.buffer->data + 16,
packet->client_addr.iabuf, 16);
memcpy(ds.buffer->data + 32,
memset(&udp_data, 0, sizeof(udp_data));
udp_data.src_port = packet->client_port;
memcpy(ds.buffer->data + 32, &udp_data, 4);
memcpy(ds.buffer->data + 36,
(unsigned char *)packet->raw,
packet->packet_length);
@ -7690,6 +7770,15 @@ dhcpv6(struct packet *packet) {
to_addr.sin6_port = packet->client_port;
#endif
#if defined(RELAY_PORT)
/*
* Check relay source port.
*/
if (packet->relay_source_port) {
to_addr.sin6_port = packet->client_port;
}
#endif
memcpy(&to_addr.sin6_addr, packet->client_addr.iabuf,
sizeof(to_addr.sin6_addr));
@ -7716,7 +7805,7 @@ dhcpv6(struct packet *packet) {
* Receive a message with a DHCPv4-query inside from the DHCPv6 server.
* (code copied from \ref do_packet6() \ref and dhcpv6())
*
* Format: interface:16 + address:16 + DHCPv6 DHCPv4-query message
* Format: interface:16 + address:16 + udp:4 + DHCPv6 DHCPv4-query message
*
* \param raw the DHCPv6 DHCPv4-query message raw content
*/
@ -7730,6 +7819,7 @@ static void recv_dhcpv4_query(struct data_string *raw) {
const struct dhcpv4_over_dhcpv6_packet *msg;
struct data_string reply;
struct data_string ds;
struct udp_data4o6 udp_data;
unsigned len;
int cc;
@ -7748,14 +7838,17 @@ static void recv_dhcpv4_query(struct data_string *raw) {
iaddr.len = 16;
memcpy(iaddr.iabuf, raw->data + 16, 16);
memset(&udp_data, 0, sizeof(udp_data));
memcpy(&udp_data, raw->data + 32, 4);
/*
* From do_packet6().
*/
if (!packet6_len_okay((char *)raw->data + 32, raw->len - 32)) {
if (!packet6_len_okay((char *)raw->data + 36, raw->len - 36)) {
log_error("recv_dhcpv4_query: "
"short packet from %s, len %d, dropped",
piaddr(iaddr), raw->len - 32);
piaddr(iaddr), raw->len - 36);
return;
}
@ -7774,18 +7867,18 @@ static void recv_dhcpv4_query(struct data_string *raw) {
return;
}
packet->raw = (struct dhcp_packet *)(raw->data + 32);
packet->packet_length = raw->len - 32;
packet->client_port = remote_port;
packet->raw = (struct dhcp_packet *)(raw->data + 36);
packet->packet_length = raw->len - 36;
packet->client_port = udp_data.src_port;
packet->client_addr = iaddr;
interface_reference(&packet->interface, ip, MDL);
msg_type = raw->data[32];
msg_type = raw->data[36];
if ((msg_type == DHCPV6_RELAY_FORW) ||
(msg_type == DHCPV6_RELAY_REPL)) {
int relaylen =
(int)(offsetof(struct dhcpv6_relay_packet, options));
relay = (const struct dhcpv6_relay_packet *)(raw->data + 32);
relay = (const struct dhcpv6_relay_packet *)(raw->data + 36);
packet->dhcpv6_msg_type = relay->msg_type;
/* relay-specific data */
@ -7797,7 +7890,7 @@ static void recv_dhcpv4_query(struct data_string *raw) {
if (!parse_option_buffer(packet->options,
relay->options,
raw->len - 32 - relaylen,
raw->len - 36 - relaylen,
&dhcpv6_universe)) {
/* no logging here, as parse_option_buffer() logs all
cases where it fails */
@ -7808,7 +7901,7 @@ static void recv_dhcpv4_query(struct data_string *raw) {
(msg_type == DHCPV6_DHCPV4_RESPONSE)) {
int msglen =
(int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options));
msg = (struct dhcpv4_over_dhcpv6_packet *)(raw->data + 32);
msg = (struct dhcpv4_over_dhcpv6_packet *)(raw->data + 36);
packet->dhcpv6_msg_type = msg->msg_type;
/* message-specific data */
@ -7817,7 +7910,7 @@ static void recv_dhcpv4_query(struct data_string *raw) {
if (!parse_option_buffer(packet->options,
msg->options,
raw->len - 32 - msglen,
raw->len - 36 - msglen,
&dhcpv6_universe)) {
/* no logging here, as parse_option_buffer() logs all
cases where it fails */
@ -7878,18 +7971,19 @@ static void recv_dhcpv4_query(struct data_string *raw) {
*/
build_dhcpv6_reply(&reply, packet);
packet_dereference(&packet, MDL);
if (reply.data == NULL)
if (reply.data == NULL) {
packet_dereference(&packet, MDL);
return;
}
/*
* Forward the response.
*/
len = reply.len + 32;
len = reply.len + 36;
memset(&ds, 0, sizeof(ds));
if (!buffer_allocate(&ds.buffer, len, MDL)) {
log_error("recv_dhcpv4_query: no memory.");
packet_dereference(&packet, MDL);
return;
}
ds.data = ds.buffer->data;
@ -7897,7 +7991,15 @@ static void recv_dhcpv4_query(struct data_string *raw) {
memcpy(ds.buffer->data, name, 16);
memcpy(ds.buffer->data + 16, iaddr.iabuf, 16);
memcpy(ds.buffer->data + 32, reply.data, reply.len);
udp_data.rsp_opt_exist = packet->relay_source_port ? 1 : 0;
memcpy(ds.buffer->data + 32, &udp_data, 4);
memcpy(ds.buffer->data + 36, reply.data, reply.len);
/*
* Now we can release the packet.
*/
packet_dereference(&packet, MDL);
cc = send(dhcp4o6_fd, ds.data, ds.len, 0);
if (cc < 0)
log_error("recv_dhcpv4_query: send(): %m");

View File

@ -169,6 +169,7 @@ static struct option agent_options[] = {
{ "agent-id", "I", &agent_universe, 3, 1 },
{ "DOCSIS-device-class", "L", &agent_universe, 4, 1 },
{ "link-selection", "I", &agent_universe, 5, 1 },
{ "relay-port", "Z", &agent_universe, 19, 1 },
{ NULL, NULL, NULL, 0, 0 }
};