isc-dhcp: dhcrelay: Add RFC3527 link selection sub-option

RFC3527 link selection sub-option is used to select the "uplink" interface of
dhcrelay in cases where the DHCP server does not know how to reach the dhcrelay
based on the DHCP range. This can happen in weird network configurations like
Mesh networks.

Signed-off-by: Bruno Randolf <br1@einfach.org>

git-svn-id: svn://svn.openwrt.org/openwrt/packages@38447 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
jow 2013-10-18 11:40:00 +00:00
parent a8ce2d5f8c
commit 6ca07c5c90
3 changed files with 110 additions and 0 deletions

View File

@ -26,6 +26,13 @@ start() {
fi
done
# link selection sub-option (RFC3527)
local link_selection
config_get link_selection ipv4 link_selection
if network_get_device ifname "$link_selection"; then
append args "-l $ifname"
fi
# relay mode
local relay_mode
config_get relay_mode ipv4 relay_mode

View File

@ -15,6 +15,9 @@ config dhcrelay ipv4
# 'discard': Don't forward
option 'relay_mode' ''
# enable RFC3527 link selection sub-option and use the IP address of
# the specified network interface as "uplink" IP address (e.g. wan)
option 'link_selection' ''
config dhcrelay ipv6
# option dhcpserver '2001:db8:1::1'

View File

@ -0,0 +1,100 @@
--- a/relay/dhcrelay.c
+++ b/relay/dhcrelay.c
@@ -65,6 +65,7 @@ int server_packets_relayed = 0; /* Packe
int client_packet_errors = 0; /* Errors sending packets to clients. */
int add_agent_options = 0; /* If nonzero, add relay agent options. */
+int add_rfc3527_suboption = 0; /* If nonzero, add RFC3527 link selection sub-option. */
int agent_option_errors = 0; /* Number of packets forwarded without
agent options because there was no room. */
@@ -104,6 +105,8 @@ struct server_list {
struct sockaddr_in to;
} *servers;
+struct interface_info *uplink;
+
#ifdef DHCPv6
struct stream_list {
struct stream_list *next;
@@ -144,6 +147,7 @@ static const char url[] =
" [-pf <pid-file>] [--no-pid]\n"\
" [-m append|replace|forward|discard]\n" \
" [-i interface0 [ ... -i interfaceN]\n" \
+" [-l interface]\n" \
" server0 [ ... serverN]\n\n" \
" dhcrelay -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
" [-pf <pid-file>] [--no-pid]\n"\
@@ -157,6 +161,7 @@ static const char url[] =
" [-pf <pid-file>] [--no-pid]\n"\
" [-m append|replace|forward|discard]\n" \
" [-i interface0 [ ... -i interfaceN]\n" \
+" [-l interface]\n" \
" server0 [ ... serverN]\n\n"
#endif
@@ -314,6 +319,20 @@ main(int argc, char **argv) {
agent_relay_mode = discard;
} else
usage();
+ } else if (!strcmp (argv [i], "-l")) {
+ add_agent_options = 1;
+ add_rfc3527_suboption = 1;
+ if (++i == argc)
+ usage();
+
+ status = interface_allocate(&uplink, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal("%s: interface_allocate: %s",
+ argv[i],
+ isc_result_totext(status));
+ strcpy(uplink->name, argv[i]);
+ interface_snorf(uplink, INTERFACE_REQUESTED);
+ //interface_dereference(&uplink, MDL);
} else if (!strcmp(argv[i], "-D")) {
#ifdef DHCPv6
if (local_family_set && (local_family == AF_INET6)) {
@@ -685,12 +704,17 @@ do_relay4(struct interface_info *ip, str
ip->addresses[0])))
return;
+ /* RFC3527: Replace giaddr address by uplink address. The original
+ * giaddr will be used in the link selection sub-option */
+ if (add_rfc3527_suboption)
+ packet->giaddr = uplink->addresses[0];
+
/* If giaddr is not already set, Set it so the server can
figure out what net it's from and so that we can later
forward the response to the correct net. If it's already
set, the response will be sent directly to the relay agent
that set giaddr, so we won't see it. */
- if (!packet->giaddr.s_addr)
+ else if (!packet->giaddr.s_addr)
packet->giaddr = ip->addresses[0];
if (packet->hops < max_hop_count)
packet->hops = packet->hops + 1;
@@ -1062,6 +1086,9 @@ add_relay_agent_options(struct interface
optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */
}
+ if (add_rfc3527_suboption)
+ optlen += 6;
+
/* We do not support relay option fragmenting(multiple options to
* support an option data exceeding 255 bytes).
*/
@@ -1093,6 +1120,14 @@ add_relay_agent_options(struct interface
memcpy(sp, ip->remote_id, ip->remote_id_len);
sp += ip->remote_id_len;
}
+
+ if (add_rfc3527_suboption) {
+ *sp++ = RAI_LINK_SELECT;
+ *sp++ = 4u;
+ memcpy(sp, &giaddr.s_addr, 4);
+ sp += 4;
+ log_debug ("RFC3527 link selection sub-option added: %s", inet_ntoa(giaddr));
+ }
} else {
++agent_option_errors;
log_error("No room in packet (used %d of %d) "