mirror of
https://gitlab.isc.org/isc-projects/dhcp
synced 2025-08-30 05:47:45 +00:00
Merged rt35711c (DHCPv4-over-DHCPv6 support) (new files)
This commit is contained in:
parent
785c1a519e
commit
52fac07044
116
common/dhcp4o6.c
Normal file
116
common/dhcp4o6.c
Normal file
@ -0,0 +1,116 @@
|
||||
/* dhcp4o6.c
|
||||
|
||||
DHCPv4 over DHCPv6 shared code... */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 by Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Internet Systems Consortium, Inc.
|
||||
* 950 Charter Street
|
||||
* Redwood City, CA 94063
|
||||
* <info@isc.org>
|
||||
* https://www.isc.org/
|
||||
*
|
||||
*/
|
||||
|
||||
#include "dhcpd.h"
|
||||
|
||||
#ifdef DHCP4o6
|
||||
|
||||
int dhcp4o6_fd = -1;
|
||||
omapi_object_t *dhcp4o6_object = NULL;
|
||||
omapi_object_type_t *dhcp4o6_type = NULL;
|
||||
|
||||
static int dhcp4o6_readsocket(omapi_object_t *);
|
||||
|
||||
/*
|
||||
* DHCPv4 over DHCPv6 Inter Process Communication setup
|
||||
*
|
||||
* A UDP socket is created between ::1 port and ::1 port + 1
|
||||
* (port is given in network order, the DHCPv6 side is bound to port,
|
||||
* the DHCPv4 side to port + 1. The socket descriptor is stored into
|
||||
* dhcp4o6_fd and an OMAPI handler is registered. Any failure is fatal.)
|
||||
*/
|
||||
void dhcp4o6_setup(u_int16_t port) {
|
||||
struct sockaddr_in6 local6, remote6;
|
||||
int flag;
|
||||
isc_result_t status;
|
||||
|
||||
/* Register DHCPv4 over DHCPv6 forwarding. */
|
||||
memset(&local6, 0, sizeof(local6));
|
||||
local6.sin6_family = AF_INET6;
|
||||
if (local_family == AF_INET6)
|
||||
local6.sin6_port = port;
|
||||
else
|
||||
local6.sin6_port = htons(ntohs(port) + 1);
|
||||
local6.sin6_addr.s6_addr[15] = 1;
|
||||
#ifdef HAVE_SA_LEN
|
||||
local6.sin6_len = sizeof(local6);
|
||||
#endif
|
||||
memset(&remote6, 0, sizeof(remote6));
|
||||
remote6.sin6_family = AF_INET6;
|
||||
if (local_family == AF_INET6)
|
||||
remote6.sin6_port = htons(ntohs(port) + 1);
|
||||
else
|
||||
remote6.sin6_port = port;
|
||||
remote6.sin6_addr.s6_addr[15] = 1;
|
||||
#ifdef HAVE_SA_LEN
|
||||
remote6.sin6_len = sizeof(remote6);
|
||||
#endif
|
||||
|
||||
dhcp4o6_fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (dhcp4o6_fd < 0)
|
||||
log_fatal("Can't create dhcp4o6 socket: %m");
|
||||
flag = 1;
|
||||
if (setsockopt(dhcp4o6_fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *)&flag, sizeof(flag)) < 0)
|
||||
log_fatal("Can't set SO_REUSEADDR option "
|
||||
"on dhcp4o6 socket: %m");
|
||||
if (bind(dhcp4o6_fd,
|
||||
(struct sockaddr *)&local6,
|
||||
sizeof(local6)) < 0)
|
||||
log_fatal("Can't bind dhcp4o6 socket: %m");
|
||||
if (connect(dhcp4o6_fd,
|
||||
(struct sockaddr *)&remote6,
|
||||
sizeof(remote6)) < 0)
|
||||
log_fatal("Can't connect dhcp4o6 socket: %m");
|
||||
|
||||
/* Omapi stuff. */
|
||||
/* TODO: add tracing support. */
|
||||
status = omapi_object_type_register(&dhcp4o6_type,
|
||||
"dhcp4o6",
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
sizeof(*dhcp4o6_object),
|
||||
0, RC_MISC);
|
||||
if (status != ISC_R_SUCCESS)
|
||||
log_fatal("Can't register dhcp4o6 type: %s",
|
||||
isc_result_totext(status));
|
||||
status = omapi_object_allocate(&dhcp4o6_object, dhcp4o6_type, 0, MDL);
|
||||
if (status != ISC_R_SUCCESS)
|
||||
log_fatal("Can't allocate dhcp4o6 object: %s",
|
||||
isc_result_totext(status));
|
||||
status = omapi_register_io_object(dhcp4o6_object,
|
||||
dhcp4o6_readsocket, 0,
|
||||
dhcpv4o6_handler, 0, 0);
|
||||
if (status != ISC_R_SUCCESS)
|
||||
log_fatal("Can't register dhcp4o6 handle: %s",
|
||||
isc_result_totext(status));
|
||||
}
|
||||
|
||||
static int dhcp4o6_readsocket(omapi_object_t *h) {
|
||||
IGNORE_UNUSED(h);
|
||||
return dhcp4o6_fd;
|
||||
}
|
||||
#endif /* DHCP4o6 */
|
113
doc/DHCPv4-over-DHCPv6
Normal file
113
doc/DHCPv4-over-DHCPv6
Normal file
@ -0,0 +1,113 @@
|
||||
Short notice about DHCPv4 over DHCPv6 aka RFC 7341
|
||||
--------------------------------------------------
|
||||
Note well: this code is still somewhat experimental and any user
|
||||
should take care when trying to use it.
|
||||
|
||||
First both the DHCPv4 over DHCPv6 client and server come with two
|
||||
processes (named "side" below):
|
||||
- a DHCPv6 side which performs usual DHCPv6 operations and
|
||||
forwards DHCPv4-query / DHCPv4-response (eventually encapsulated
|
||||
by / for DHCPv6 relay traversal) from / to the DHCPv4 side
|
||||
|
||||
- a DHCPv4 side which processes encapsulated DHCPv4 messages
|
||||
|
||||
Both sides support different command line arguments and configuration /
|
||||
lease / process ID files even some could be common, for instance
|
||||
most of the topology description.
|
||||
|
||||
Second open of the hairy issues about configuring a DHCP server is
|
||||
the localization, i.e., how to associate a client with a subnetwork
|
||||
on a link (aka shared network).
|
||||
|
||||
The topology is described in the server configuration file with
|
||||
shared-network and subnet/subnet6 declarations. A subnetwork is
|
||||
included in a shared-network, a shared network is created for
|
||||
each orphan subnetwork. For each requested interface, a shared network
|
||||
is built with all subnetworks matching its address.
|
||||
|
||||
The procedure for DHCPv4 is in order:
|
||||
- follow the Relay Agent Link Selection option if exists
|
||||
|
||||
- follow the Subnet Selection option if exists
|
||||
|
||||
- use the relay address if relayed
|
||||
|
||||
- use the receiving interface
|
||||
|
||||
At the exception of the last case the address must match a subnet address.
|
||||
|
||||
The procedure for DHCPv6 is in order:
|
||||
- when relayed, use the first relay with an usable (i.e., not unspecified
|
||||
or link-local) address
|
||||
|
||||
- use the receiving interface
|
||||
|
||||
Note there can be multiple relays in DHCPv6, including layer 2 relays
|
||||
which provide no usuable link addresses.
|
||||
|
||||
The localization issue is more complex (fun!) with DHCPv4 over DHCPv6
|
||||
as explained in RFC 7341 quoted here:
|
||||
Since the DHCPv4 message is encapsulated in the DHCPv6 message, it
|
||||
lacks the information that is typically used by the DHCPv4 server,
|
||||
implementing [RFC2131], to make address- allocation decisions,
|
||||
e.g., giaddr for relayed messages and IPv4 address of the interface
|
||||
that the server is using to communicate with a directly connected
|
||||
client.
|
||||
|
||||
In DHCPv4 over DHCPv6, there are a mixture of IPv6 and IPv4 addresses.
|
||||
The DHCPv4 over DHCPv6 server externally uses only IPv6 addresses,
|
||||
even at the DHCPv4 side, so shared networks associated to directly
|
||||
attached interfaces are identified by subnet6 declarations.
|
||||
For this reason, the DHCPv4 side should request no interface
|
||||
by the command line or configuration file: all usable interfaces
|
||||
will be requested (i.e., standard behavior when no interface is
|
||||
specified in the command line or configuration file) and it is
|
||||
not an error to have an interface with an address and no matching
|
||||
subnet6 declaration, nor an error to have no usable interfaces
|
||||
(i.e., fully relayed or routed topologies are accepted).
|
||||
|
||||
Note also there is no involved DHCPv4 relays (DHCPv4 messages are
|
||||
directly encapsulated into DHCPv6 DHCPv4-query/DHCPv4-response
|
||||
messages by clients and servers as there is no cross DHCP version
|
||||
relays specified by RFC 7341) so to get a Relay Agent option or
|
||||
a relay address are very unlikely cases.
|
||||
|
||||
So the procedure is:
|
||||
- follow the Relay Agent Link Selection option if exists
|
||||
|
||||
- follow the DHCPv4 Subnet Selection option if exists
|
||||
|
||||
- use the DHCPv4 relay address if DHCPv4 relayed
|
||||
|
||||
- when DHCPv6 relayed, use the first relay with an usable (i.e., not
|
||||
unspecified or link-local) address
|
||||
|
||||
- use the receiving interface
|
||||
|
||||
So for more fun one can get a configuration like:
|
||||
|
||||
shared-network "link1" {
|
||||
subnet6 2001:db8:1:1::/64 { }
|
||||
|
||||
subnet 192.168.1.0 netmask 255.255.255.0 {
|
||||
range 192.168.1.100 192.168.1.199;
|
||||
}
|
||||
}
|
||||
|
||||
So a DHCPv4 over DHCPv6 client using the 2001:db8:1:1::10 IPv6 address
|
||||
will get a 192.168.1.1xy assigned.
|
||||
|
||||
For more fun there is a remaining question: on which interface
|
||||
a DHCPv4 over DHCPv6 client should apply the assigned IPv4 address?
|
||||
RFC 7341 does not really help:
|
||||
Before applying for an IPv4 address via a DHCPv4-query message, the
|
||||
client must identify a suitable network interface for the address.
|
||||
Once the request is acknowledged by the server, the client can
|
||||
configure the address and other relevant parameters on this
|
||||
interface. The mechanism for determining a suitable interface is out
|
||||
of the scope of the document.
|
||||
|
||||
The ISC DHCP answer is the IPv4 address is (in fact is required to be)
|
||||
specified in the command line of the DHCPv4 side of the DHCPv4 over DHCPv6
|
||||
client. BTW in the usual case where the upstream interface is IPv6 only,
|
||||
the IPv4 interface will be a different one.
|
Loading…
x
Reference in New Issue
Block a user