aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore16
-rw-r--r--relay/dhcrelay.828
-rw-r--r--relay/dhcrelay.c147
3 files changed, 142 insertions, 49 deletions
diff --git a/.gitignore b/.gitignore
index 1850904..5ffa7ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,19 @@
*.la
*.so
*.lai
+*.tmp
+*.log
.libs
-.depends
-config.log
+.deps
config.status
+Makefile
+stamp-h1
+includes/config.h
+bind/bind-9.8.0-P4
+bind/include
+client/dhclient
+dhcpctl/cltest
+dhcpctl/omshell
+omapip/svtest
+relay/dhcrelay
+server/dhcpd
diff --git a/relay/dhcrelay.8 b/relay/dhcrelay.8
index a087d14..1a0e90f 100644
--- a/relay/dhcrelay.8
+++ b/relay/dhcrelay.8
@@ -86,7 +86,7 @@ dhcrelay - Dynamic Host Configuration Protocol Relay Agent
.PP
.B dhcrelay -6
[
-.B -dqI
+.B -dqIL
]
[
.B -p
@@ -103,6 +103,10 @@ dhcrelay - Dynamic Host Configuration Protocol Relay Agent
[
.B --no-pid
]
+[
+.B -e
+.I enterprisenumber
+]
.B -l
.I lower0
[
@@ -221,15 +225,31 @@ in use, to disambiguate between them. The \fB-I\fR option causes
dhcrelay to send the option even if there is only one downstream
interface.
.TP
--l [\fIaddress%\fR]\fIifname\fR[\fI#index\fR]
+-L
+Enable Lightweight DHCPv6 Relay Agent (layer 2) operation according to
+RFC6221. This forces the \fB-I\fR option on, and uses the unspecified
+address (::) as link address in all relayed packets, enabling operation
+on a layer 2 brigde with no global address at all. Note that this
+option makes both the \fB-I\fR option and the \fIaddress%\fR parameter
+of the \fB-l\fR option redundant. Any configured \fIaddress%\fR will
+be silently ignored.
+.TP
+-e \fIenterprisenumber\fR
+Specifies the IANA allocated enterprise number to be used in REMOTE-ID
+options. Required for adding REMOTE-ID
+.TP
+-l [\fIaddress%\fR]\fIifname\fR[\fI#index\fR][\fI!remoteid\fR][\fI&subscriberid\fR]
Specifies the ``lower'' network interface for DHCPv6 relay mode: the
interface on which queries will be received from clients or from other
relay agents. At least one \fB-l\fR option must be included in the command
line when running in DHCPv6 mode. The interface name \fIifname\fR is a
mandatory parameter. The link address can be specified by \fIaddress%\fR;
if it isn't, dhcrelay will use the first non-link-local address configured
-on the interface. The optional \fI#index\fR parameter specifies the
-interface index.
+on the interface unless configured for LDRA with the \fB-L\fR option.
+The optional \fI#index\fR parameter specifies the interface index. The
+optional \fI!remoteid\fR parameter specifies the ascii remote id value
+(requires the \fB-e\fR option as well). The optional \fI&subscriberid\fR
+parameter enables adding a subscriber-id option.
.TP
-u [\fIaddress%\fR]\fIifname\fR
Specifies the ``upper'' network interface for DHCPv6 relay mode: the
diff --git a/relay/dhcrelay.c b/relay/dhcrelay.c
index 7b3909e..812f838 100644
--- a/relay/dhcrelay.c
+++ b/relay/dhcrelay.c
@@ -83,6 +83,7 @@ int max_hop_count = 10; /* Maximum hop count */
#ifdef DHCPv6
/* Force use of DHCPv6 interface-id option. */
isc_boolean_t use_if_id = ISC_FALSE;
+isc_boolean_t rfc6221_ldra = ISC_FALSE; /* implement RFC6221 LDRA */
#endif
/* Maximum size of a packet with agent options added. */
@@ -105,6 +106,11 @@ struct server_list {
} *servers;
#ifdef DHCPv6
+int enterprise_number = 0; /* enterprise-number for use with remote-id */
+struct remote_id {
+ int enterprise;
+ char id[];
+};
struct stream_list {
struct stream_list *next;
struct interface_info *ifp;
@@ -145,11 +151,12 @@ static const char url[] =
" [-m append|replace|forward|discard]\n" \
" [-i interface0 [ ... -i interfaceN]\n" \
" server0 [ ... serverN]\n\n" \
-" dhcrelay -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
+" dhcrelay -6 [-d] [-q] [-I] [-L] [-c <hops>] [-p <port>]\n" \
" [-pf <pid-file>] [--no-pid]\n"\
+" -e <enterprisenumber>\n"\
" -l lower0 [ ... -l lowerN]\n" \
" -u upper0 [ ... -u upperN]\n" \
-" lower (client link): [address%%]interface[#index]\n" \
+" lower (client link): [address%%]interface[#index][!remoteid][&subscriberid]\n" \
" upper (server link): [address%%]interface"
#else
#define DHCRELAY_USAGE \
@@ -331,6 +338,23 @@ main(int argc, char **argv) {
local_family_set = 1;
local_family = AF_INET6;
use_if_id = ISC_TRUE;
+ } else if (!strcmp(argv[i], "-L")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ rfc6221_ldra = ISC_TRUE;
+ use_if_id = ISC_TRUE; /* required by RFC6221 */
+ } else if (!strcmp(argv[i], "-e")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ if (++i == argc)
+ usage();
+ enterprise_number = atoi(argv[i]);
} else if (!strcmp(argv[i], "-l")) {
if (local_family_set && (local_family == AF_INET)) {
usage();
@@ -1123,13 +1147,13 @@ add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
#ifdef DHCPv6
/*
- * Parse a downstream argument: [address%]interface[#index].
+ * Parse a downstream argument: [address%]interface[#index][!remoteid][&subscriberid].
*/
static struct stream_list *
parse_downstream(char *arg) {
struct stream_list *dp, *up;
struct interface_info *ifp = NULL;
- char *ifname, *addr, *iid;
+ char *ifname, *addr, *iid, *rid, *sid;
isc_result_t status;
if (!supports_multiple_interfaces(ifp) &&
@@ -1145,6 +1169,14 @@ parse_downstream(char *arg) {
*ifname++ = '\0';
addr = arg;
}
+ sid = strchr(ifname, '&');
+ if (sid != NULL) {
+ *sid++ = '\0';
+ }
+ rid = strchr(ifname, '!');
+ if (rid != NULL) {
+ *rid++ = '\0';
+ }
iid = strchr(ifname, '#');
if (iid != NULL) {
*iid++ = '\0';
@@ -1192,13 +1224,26 @@ parse_downstream(char *arg) {
log_fatal("No memory for downstream.");
dp->ifp = ifp;
if (iid != NULL) {
- dp->id = atoi(iid);
+ dp->id = htonl(atoi(iid));
} else {
dp->id = -1;
}
+ if ((rid != NULL) && ((rid - sizeof(int)) >= arg)) {
+ struct remote_id *remote_id;
+ remote_id = (struct remote_id *) (rid - sizeof(int));
+ remote_id->enterprise = htonl(enterprise_number);
+ dp->ifp->remote_id = (u_int8_t *) remote_id;
+ dp->ifp->remote_id_len = sizeof(int) + strlen(rid);
+ }
+ /* we're overloading the circuit_id option */
+ if (sid != NULL) {
+ ifp->circuit_id = (u_int8_t *) sid;
+ ifp->circuit_id_len = strlen(sid);
+ }
/* !addr case handled by setup. */
- if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
- log_fatal("Bad link address '%s'", addr);
+ if (!rfc6221_ldra && /* silently ignore any addr for LDRA */
+ addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
+ log_fatal("Bad link address '%s'", addr);
return dp;
}
@@ -1262,6 +1307,10 @@ parse_upstream(char *arg) {
up->ifp = ifp;
+ /* RFC6221 requires dst = All_DHCP_Relay_Agents_and_Servers. ignore any configured address */
+ if (rfc6221_ldra)
+ addr = All_DHCP_Relay_Agents_and_Servers;
+
if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
log_fatal("Bad address %s", addr);
@@ -1278,6 +1327,14 @@ setup_streams(void) {
isc_boolean_t link_is_set;
for (dp = downstreams; dp; dp = dp->next) {
+ /* Set interface-id. */
+ if (dp->id == -1)
+ dp->id = htonl(dp->ifp->index);
+
+ /* link address must be :: for LDRA according to RFC6221 */
+ if (rfc6221_ldra)
+ continue;
+
/* Check interface */
if (dp->ifp->v6address_count == 0)
log_fatal("Interface '%s' has no IPv6 addresses.",
@@ -1305,10 +1362,6 @@ setup_streams(void) {
memcpy(&dp->link.sin6_addr,
&dp->ifp->v6addresses[i],
sizeof(dp->link.sin6_addr));
-
- /* Set interface-id. */
- if (dp->id == -1)
- dp->id = dp->ifp->index;
}
for (up = upstreams; up; up = up->next) {
@@ -1329,6 +1382,8 @@ setup_streams(void) {
*/
static const int required_forw_opts[] = {
D6O_INTERFACE_ID,
+ D6O_REMOTE_ID,
+ D6O_SUBSCRIBER_ID,
D6O_RELAY_MSG,
0
};
@@ -1414,40 +1469,46 @@ process_up6(struct packet *packet, struct stream_list *dp) {
if (!option_state_allocate(&opts, MDL)) {
log_fatal("No memory for upwards options.");
}
-
- /* Add an interface-id (if used). */
- if (use_if_id) {
- int if_id;
- if (dp) {
- if_id = dp->id;
- } else if (!downstreams->next) {
- if_id = downstreams->id;
- } else {
- log_info("Don't know the interface.");
- option_state_dereference(&opts, MDL);
- return;
- }
+ /* we need a specific interface for relay options */
+ if (!dp && !downstreams->next) /* only one downstream? */
+ dp = downstreams;
- if (!save_option_buffer(&dhcpv6_universe, opts,
- NULL, (unsigned char *) &if_id,
- sizeof(int),
- D6O_INTERFACE_ID, 0)) {
- log_error("Can't save interface-id.");
- option_state_dereference(&opts, MDL);
- return;
- }
+ if (!dp && use_if_id) {
+opt_error:
+ log_info("Can't save option.");
+ option_state_dereference(&opts, MDL);
+ return;
}
+ /* Add an interface-id (if used). */
+ if (use_if_id && !save_option_buffer(&dhcpv6_universe, opts,
+ NULL, (unsigned char *) &dp->id,
+ sizeof(int),
+ D6O_INTERFACE_ID, 0))
+ goto opt_error;
+
+ /* add remote id */
+ if (dp->ifp->remote_id && !save_option_buffer(&dhcpv6_universe, opts,
+ NULL, dp->ifp->remote_id,
+ dp->ifp->remote_id_len,
+ D6O_REMOTE_ID, 0))
+ goto opt_error;
+
+ /* add subscriber id */
+ if (dp->ifp->circuit_id &&
+ !save_option_buffer(&dhcpv6_universe, opts,
+ NULL, dp->ifp->circuit_id,
+ dp->ifp->circuit_id_len,
+ D6O_SUBSCRIBER_ID, 0))
+ goto opt_error;
+
/* Add the relay-msg carrying the packet. */
if (!save_option_buffer(&dhcpv6_universe, opts,
NULL, (unsigned char *) packet->raw,
packet->packet_length,
- D6O_RELAY_MSG, 0)) {
- log_error("Can't save relay-msg.");
- option_state_dereference(&opts, MDL);
- return;
- }
+ D6O_RELAY_MSG, 0))
+ goto opt_error;
/* Finish the relay-forward message. */
cursor += store_options6(forw_data + cursor,
@@ -1571,10 +1632,10 @@ process_down6(struct packet *packet) {
case DHCPV6_RECONFIGURE:
case DHCPV6_RELAY_FORW:
case DHCPV6_LEASEQUERY_REPLY:
- log_info("Relaying %s to %s port %d down.",
+ log_info("Relaying %s to %s port %d down on %s.",
dhcpv6_type_names[msg->msg_type],
piaddr(peer),
- ntohs(to.sin6_port));
+ ntohs(to.sin6_port), dp->ifp->name);
break;
case DHCPV6_SOLICIT:
@@ -1586,17 +1647,17 @@ process_down6(struct packet *packet) {
case DHCPV6_DECLINE:
case DHCPV6_INFORMATION_REQUEST:
case DHCPV6_LEASEQUERY:
- log_info("Discarding %s to %s port %d down.",
+ log_info("Discarding %s to %s port %d down on %s.",
dhcpv6_type_names[msg->msg_type],
piaddr(peer),
- ntohs(to.sin6_port));
+ ntohs(to.sin6_port), dp->ifp->name);
goto cleanup;
default:
- log_info("Unknown %d type to %s port %d down.",
+ log_info("Unknown %d type to %s port %d down on %s.",
msg->msg_type,
piaddr(peer),
- ntohs(to.sin6_port));
+ ntohs(to.sin6_port), dp->ifp->name);
goto cleanup;
}