first let me introduce the scenario: I am running two home networks (two locations) that each have a direct connection to the internet and run an OpenWRT router with dnsmasq to do DHCP and DNS services. In addition OpenWRT connects the two sites in bridge mode such that both sites have full access two all services on both sides (OK, at present only one network has a server, but this may change) while maintaining direct connectivity to the internet. Some devices (two laptops and an android phone) roam between the two locations.
The typical problem with bridge scenario is that there should be just one DHCP as – at least with dnsmasq - there is no way to restrict service to the local network. An alternative is to run two DHCP servers that ignore certain devices that are not considered to be local, however then roaming devices will get a poor connection to the internet via the bridge only, and none at all when the bridge fails.
If we number the solutions 1) one DHCP server, 2) classifying the clients, then the other standard solutions I am aware of are: 3) don´t use a bridge but routing, but that limits connectivity to IP based services, and broadcast/multicast based services can be an issue. E.g. WOL does not pass transparently. 4) filter DHCP via ebtables – but this requires to recompile the OpenWRT kernel, can cause performance issues for all bridged networks, and I also have no experience with that kind of configuration. 5) use the undocumented packet filter in OpenVPN, but it looks like this is also difficult to script…
Thus I came up with the idea to modify OpenVPN to suppress DHCP requests received from the local network. OpenVPN receives the DHCP request via device tap0, and the processing is in
Code: Select all
process_incoming_tun (struct context *c)
{
// some code omitted…
/* Show packet content */
dmsg (D_TUN_RW, "TUN READ [%d]", BLEN (&c->c2.buf));
// check for DHCP request packets. Ingore the possibility of IP options.
// drop just host->server for the usecase of suppressing non local DHCP hosts
// check UDP and port first, then IP and MAC header, for speed
if ((c->c2.buf.len >= 38)
&& (c->c2.buf.data[c->c2.buf.offset+37]== 67) // UDP destination
&& (c->c2.buf.data[c->c2.buf.offset+36]== 00)
&& (c->c2.buf.data[c->c2.buf.offset+35]== 68) // UDP source
&& (c->c2.buf.data[c->c2.buf.offset+34]== 00)
&& (c->c2.buf.data[c->c2.buf.offset+23]==0x11) // UDP protocol identifier
&& (c->c2.buf.data[c->c2.buf.offset+14]==0x45) // IP version and header length, ignoring the possibility of options
&& (c->c2.buf.data[c->c2.buf.offset+13]==0x00) // MAC encapsulation of IP
&& (c->c2.buf.data[c->c2.buf.offset+12]==0x08))
{
msg (D_TUN_RW, "discarded DHCP request [%d] : %s",
BLEN (&c->c2.buf),
PROTO_DUMP (&c->c2.buf, &gc));
c->c2.buf.len = 0; // discard frame
}
if (c->c2.buf.len > 0)
{
// more code omitted…
}
I am willing to contribute a modification that adds a configuration option (e.g. filterdhcprequests), but before investing more time, I would like to understand the process and collect feedback on both the approach and the specific implementation. Of course I would also appreciate someone experienced in processing optons and the process to take this up and implement it...
Best regards, Joachim