Binding DHCP services to specific IP addresses or interfaces, does not always succeed. It depends on the application. A rather typical answer found on the internet regarding ISC DHCP is found here:

Citation from the as correct marked on this popular website answer:

The ISC DHCP server will only work when it binds to the all zeros address. Keep in mind that the DHCP server must communicate with clients that have no IP address, so binding to the IP associated with a specific interface doesn't make a lot of sense.

The all zeros address sentence in this answer is even technically correct, I will show comparable output displaying when actually dnsmasq will work via lo interface only. Binding a service or daemon to one specific IP addressing or interface makes technically sense.

This specific ISC DHCP server configuration setting is possible reading the dhcp-users mailing list entry:

You can also use the local-address statement, but beware the special requirements. [...] Note also that since this bind()s all DHCP sockets to the specified address, that only one address may be supported in a daemon at a given time.

Binding linux services and daemons to IP addresses makes sense. Not only for the DHCP service, I would say for almost all linux daemons it makes lots of sense being available using particular IP addressing only. Using dnsmasq and binding its DHCP service to the linux interface lo works, and servicing DHCP clients also works using this setting. The setup is explained below, mentions all that dnsmasq configurations that does not work first, and listing configurations or parts of it leading to nothing. Failed configuration listing. Finally at the end, one particular dnsmasq configuration setting makes it work. Found this configuration by accident, while tinkering with dnsmasq trying it to force to listen to a particular IP address only. The configuration setting does not make sense at first sight. But I try to explain why it would work from the point of view of IP stack and the application, shown the given DHCP debug output of the participating DHCP nodes and applications. Without knowing anything in particular about dnsmasq code or coding design.

This way the applications on the running node or computer, in the network topology this is DHCP-67, do not need to be in a specific IP subnet. Mostly that subnet configured on eth0 interface. And the application running is not sticked to one geographic location. The eth0 prefix is only the for transport and routing adjacency. No need to have stretched L2 broadcast domain across large, long distances. Running a dynamic routing protocol, of your choice is involved on the appliance plus the one application. But this is the only effort that is needed. Compared to the effort to have L2 connectivity across geographic locations, operational effort, specific hardware needed, planning, designing, upgrading, migrating, building one, huge failure domain across large distances, ... lots more of resources are needed and experience running that L2 network.

Network topology

Network topology with IP adressing:

                                                   lo
   DCHP client                                 203.0.113.67
    +-------+                                   +-------+
    |       |                                   |       |
    |  C1   |                                   |DHCP-67|
    |       |                                   |       |
    +---+---+                                   +---+---+
   eth0 |                                    +--->  | eth0
        |                                    |      |
        |                                  OSPF     |
        |                                    |      |
  Gi0/2 | 10.100.200.1/24                    +--->  | Gi0/1
    +---+---+                                   +---+---+
    |       |                                   |       |
    |  R0   +-----------------------------------+  R1   |
    |       | Gi0/0     <-- OSPF -->       Gi0/0|       |
    +-------+                                   +-------+
       lo                                          lo
    192.0.2.0                                   192.0.2.1

The GNS3 appliance DHCP-67 runs following:

  • linux
  • FRRouting
  • dnsmasq

The DHCP server - DHCP-67 is first, a OSPF router advertising its lo IPv4 prefix. Additionally the dnsmasq DHCP IPv4 server is running and bound to the configured IP prefix - 203.0.113.67/32. The reachability of the IP prefix and the DHCP protocol are the main focus in this GNS3 netlab.

The goal is achieved if the service - operating the DHCP protocol, is bound to one IP address and runs as a service using this one IP prefix only.

The appliance used in example is available in Building 64bit alpine linux GNS3 FRRouting appliance. You can even setup this netlab using only the linked article appliance, running FRRouting Router appliances instead. Everything will work the same.

Configuration

FRR

This appliance runs following FRRrouting configuration:

config
!
frr version 9.1
frr defaults traditional
hostname DHCP-67
log syslog informational
service integrated-vtysh-config
!
interface lo
 ip address 203.0.113.67/32
 ip ospf area 0
 ip ospf passive
exit
!
interface eth0
 ip ospf area 0
 ip ospf network point-to-point
exit
!
router ospf
 ospf router-id 203.0.113.67
 log-adjacency-changes detail
exit
!
end

Running OSPF, advertising the configured lo IP address. Simple OSPF configuration. It could run any other routing protocol as well:

  • RIP
  • BGP
  • IS-IS
  • EIGRP
  • BABEL
  • ...

Choose your favourite routing protocol here for the individual setup. I have read in big setups, iBGP is chosen for running such solution. Also read that RIP is also used, for node connectivity and then redistributed to another routing protocol. The redistribution does not take place on the nodeor the appliance. The node runs always and only one dynamic routing protocol. Running RIP makes sense, because of RIP's easy design. RIP is easy, it has no concept of areas like OSPF or IS-IS, it is easy to configure and operate, for everyone. And good enough for the job.

IP setup

Appliance current setup:

Used linux kernel version:

root@DHCP-67 ~ # uname -a
Linux DHCP-67 6.6.14-0-virt #1-Alpine SMP PREEMPT_DYNAMIC Thu, 25 Jan 2024 06:38:57 +0000 x86_64 Linux

One NIC eth0,:

root@DHCP-67 ~ # ip link show up
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 0c:ab:0f:ef:00:00 brd ff:ff:ff:ff:ff:ff

eth0 IP address is setup by DHCP, dynamically. Here in this example a single P2P /30 DHCP subnet is configured, pragmatic approach. This can be also a /24 IP subnet, for many routing nodes, the used IP network does not matter, it can be randomly chosen.

The lo address is static. The only IP address that is configured statically on the appliance.

  • lo - 203.0.113.67/32 static
  • eth0 - 10.0.0.22/30 dynamic configuration

ip add full output:

root@DHCP-67 ~ # ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet 203.0.113.67/32 brd 203.0.113.67 scope global lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host proto kernel_lo 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 0c:ab:0f:ef:00:00 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.22/30 brd 10.0.0.23 scope global dynamic noprefixroute eth0
       valid_lft 71952sec preferred_lft 61152sec
    inet6 fe80::eab:fff:feef:0/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever

Version of dnsmasq:

root@DHCP-67 ~ # apk list -I dnsmasq
dnsmasq-2.89-r6 x86_64 {dnsmasq} (GPL-2.0-or-later) [installed]

ss socket output when dnsmasq is running explained setup. Displays dnsmasq listening to all IPv4 interfaces when it is running, bound to 0.0.0.0:

root@DHCP-67 ~ # ss -tulpn  | head -n 4
Netid State  Recv-Q Send-Q               Local Address:Port Peer Address:PortProcess
udp   UNCONN 0      0                          0.0.0.0:67        0.0.0.0:*    users:(("dnsmasq",pid=3162,fd=4)) 
udp   UNCONN 0      0                        10.0.0.22:68        0.0.0.0:*    users:(("dhcpcd",pid=1982,fd=10)) 
udp   UNCONN 0      0      [fe80::eab:fff:feef:0]%eth0:546          [::]:*    users:(("dhcpcd",pid=1963,fd=10))

Verification

Prior to further configuration of dnsmasq DHCP server, the running OSPF neighborship between DHCP-67 and the R1 router needs to be verified at this point.

vtysh

Using the FRR vtysh CLI use following commands to show the current status of the routing neighbors:

show ip ospf neighbor

Following can be found in the command line output shown:

  • Neighbor ID - 192.0.2.1 - R1
  • State - FULL - P2P OSPF link - routing has converged.
  • Interface - eth0

Command line output from the DHCP-67 appliance router:

DHCP-67# sh ip ospf neighbor 
Neighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL
192.0.2.1         1 Full/-          1m11s             30.715s 10.0.0.21       eth0:10.0.0.22                       0     0     0

Full output of the converged IP routing table. Displaying following important data:

The important IP prefix is directly connected to lo - C>* 203.0.113.67/32 is directly connected, lo, 00:07:51

It looks like FRRouting creates a local OSPF route for a directly connected IP prefix. O 203.0.113.67/32 [110/0] is directly connected, lo, weight 1, 00:07:51. This is something each routing operating system handles a differently.

 DHCP-67# sh ip route
 Codes: K - kernel route, C - connected, S - static, R - RIP,
        O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
        T - Table, v - VNC, V - VNC-Direct, A - Babel, F - PBR,
        f - OpenFabric,
        > - selected route, * - FIB route, q - queued, r - rejected, b - backup
        t - trapped, o - offload failure

 K>* 0.0.0.0/0 [0/1002] via 10.0.0.21, eth0, src 10.0.0.22, 00:05:22
 O>* 10.0.0.0/30 [110/101] via 10.0.0.21, eth0, weight 1, 00:05:15
 O   10.0.0.20/30 [110/100] is directly connected, eth0, weight 1, 00:05:21
 K * 10.0.0.20/30 [0/1002] is directly connected, eth0, 00:05:22
 C>* 10.0.0.20/30 is directly connected, eth0, 00:05:22
 O>* 10.100.200.0/24 [110/102] via 10.0.0.21, eth0, weight 1, 00:05:15
 O>* 192.0.2.0/32 [110/102] via 10.0.0.21, eth0, weight 1, 00:05:15
 O>* 192.0.2.1/32 [110/101] via 10.0.0.21, eth0, weight 1, 00:05:15
 O   203.0.113.67/32 [110/0] is directly connected, lo, weight 1, 00:07:51
 C>* 203.0.113.67/32 is directly connected, lo, 00:07:51

Everything fine here. The loopbacks of the R0 and R1 and the client network 10.100.200.0/24, where the DHCP clients are connected to, are in the routing table.

iproute2

This is the same routing table as above, showing the converged IP routing using the iproute2 linux utility:

zebra@DHCP-67 ~ % ip route
default via 10.0.0.21 dev eth0 proto dhcp src 10.0.0.22 metric 1002
10.0.0.0/30 nhid 21 via 10.0.0.21 dev eth0 proto ospf metric 20
10.0.0.20/30 dev eth0 proto dhcp scope link src 10.0.0.22 metric 1002
10.100.200.0/24 nhid 21 via 10.0.0.21 dev eth0 proto ospf metric 20
192.0.2.0 nhid 21 via 10.0.0.21 dev eth0 proto ospf metric 20
192.0.2.1 nhid 21 via 10.0.0.21 dev eth0 proto ospf metric 20

dnsmasq

This is a list of configurations that, did not change anything to the protocol and application behaviour. Trying to force dnsmasq stick to lo only.

did not work

Following dnsmasq settings have been tested and failed.

1) Explicit setting where to await DHCP packets, do this:

interface=lo
listen-address=203.0.113.67

2) Most specific setting, and bind to those configured interfaces:

listen-address=127.0.0.1
listen-address=203.0.113.67
bind-interfaces

3) Reverse approach. Exclude the not interesting interfaces for DHCP, and use the left one's, there is only lo left:

except-interface=eth0
bind-interfaces

4) Specifiy interface NOT run DHCP service on to

no-dhcp-interface=eth0

Whitelisting, specifying interesting interfaces, and the blacklisting approach were not successful, here.

IOS - DHCP relay debug

This is how the router involved in DHCP relay-ing messages displays the broken DHCP communication. If using the above dnsmasq configurations. This is how it fails.

The prefix 203.0.113.67 is in the routing table. This is the loopback address of the DHCP server. It is a /32 host route, only one IP address. Known via OSPF distance 110:

R0#sh ip route 203.0.113.67
Routing entry for 203.0.113.67/32
  Known via "ospf 1", distance 110, metric 200, type intra area
  Last update from 10.0.1.1 on GigabitEthernet0/0, 04:33:41 ago
  Routing Descriptor Blocks:
  * 10.0.1.1, from 203.0.113.67, 04:33:41 ago, via GigabitEthernet0/0
      Route metric is 200, traffic share count is 1

Verify, replies to ICMP:

R0#ping 203.0.113.67 
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 203.0.113.67, timeout is 2 seconds:
!!!!!

This is the local router R0 interface configuration. The ip helper-address is set. Clients requesting IP DHCP are connected to this interface. The DHCP requests that come from this IP subnet 10.100.200.0/24 are forwared unicast to 203.0.113.67, the DHCP server IP address.

R0#sh run int ǵi0/2
Building configuration...
!
interface GigabitEthernet0/2
 ip address 10.100.200.1 255.255.255.0
 ip helper-address 203.0.113.67
 ip ospf 1 area 0
end

IOS R0 router debugging messages. Enable it using following command

debug ip dhcp server packet

Router has no DHCP server config. It displays the DHCP requests from client:

*Mar 10 13:40:59.674: DHCPD: client's VPN is .
*Mar 10 13:40:59.674: DHCPD: No option 125
*Mar 10 13:40:59.674: DHCPD: Option 125 not present in the msg.
*Mar 10 13:40:59.674: DHCPD: DHCPDISCOVER received from client 0c64.c808.0000 on interface GigabitEthernet0/2.
*Mar 10 13:40:59.674: DHCPD: Option 125 not present in the msg.
*Mar 10 13:40:59.674: DHCPD: setting giaddr to 10.100.200.1.
*Mar 10 13:40:59.675: DHCPD: BOOTREQUEST from 0c64.c808.0000 forwarded to 203.0.113.67.

The DHCP request times out or has not been send, no reply. Not even return to sender. Nothing. No DHCP communication from the DHCP server side. The same output over an over, without replies from the DHCP 203.0.117.67.

Results, DHCP client finally assigning an APIPA IP address to its failing DHCP requests, and has no IP connectivity.

man dnsmasq

The man dnsmasq writes following in the:

 -I, --except-interface=<interface name>
              Do  not listen on the specified interface. Note that the order of --listen-address --interface and --except-interface options does not matter and that --except-interface options always override the others. The comments about interface labels for --listen-address apply here.

Using -I lo takes precedence over any other listed, not working above, configuration setting. No settings at all are needed here using this running option. It ignores everything configured.

Solution

Run the dnsmasq daemon using the --except-interface=lo configuration setting only. This works.

dnsmasq.conf

That is the full running testing setup dnsmasq configuration used in the netlab example. IPv4 only, only DHCP, most basic configuration, 4 lines only:

# Run everywhere, except on the linux lo interface, do not run there:
except-interface=lo

# Please no DNS deamon implied when starting dnsmasq
port=0

# DHCP configuration
dhcp-range=10.100.200.10,10.100.200.150,255.255.255.0,6h
dhcp-option=option:router,10.100.200.1

Result running the configuration syntax sanitiser command dnsmasq --test:

root@DHCP-67 /etc/dnsmasq.d # dnsmasq --test
dnsmasq: syntax check OK.

Start dnsmasq. It will use the 203.0.113.67/32 configured IP address for its DHCP daemon, or at least receive messages using it. And all the communication between DHCP client and DHCP server, will start to work resulting in successful assign of IP address to the requesting DHCP client.

Verification

Related debug messages showing successful DHCP communication between the DHCP client and the 203.0.113.76 - DHCP server routed loopback IP address.

IOS - DHCP relay debug

The following debug command is used to see the DHCP verbose output.

debug ip dhcp server packet

R0 is relaying the DHCP messages from the client to the DHCP server. The debug displays the successful DHCP communication between client and server, resulting in client getting an IP address assigned:

*Mar 10 14:52:06.498: DHCPD: client's VPN is .
*Mar 10 14:52:06.498: DHCPD: No option 125
*Mar 10 14:52:06.498: DHCPD: Option 125 not present in the msg.
*Mar 10 14:52:06.498: DHCPD: DHCPDISCOVER received from client 0c64.c808.0000 on interface GigabitEthernet0/2.
*Mar 10 14:52:06.498: DHCPD: Option 125 not present in the msg.
*Mar 10 14:52:06.498: DHCPD: setting giaddr to 10.100.200.1.
*Mar 10 14:52:06.498: DHCPD: BOOTREQUEST from 0c64.c808.0000 forwarded to 203.0.113.67.
*Mar 10 14:52:06.500: DHCPD: client's VPN is .
*Mar 10 14:52:06.500: DHCPD: No option 125
*Mar 10 14:52:06.500: DHCPD: forwarding BOOTREPLY to client 0c64.c808.0000.
*Mar 10 14:52:06.500: DHCPD: Option 125 not present in the msg.
*Mar 10 14:52:06.500: DHCPD: no option 125
*Mar 10 14:52:06.500: DHCPD: src nbma addr as zero
*Mar 10 14:52:06.500: DHCPD: ARP entry exists (10.100.200.79, 0c64.c808.0000).
*Mar 10 14:52:06.500: DHCPD: unicasting BOOTREPLY to client 0c64.c808.0000 (10.100.200.79).
*Mar 10 14:52:07.230: DHCPD: client's VPN is .
*Mar 10 14:52:07.230: DHCPD: No option 125
*Mar 10 14:52:07.230: DHCPD: DHCPREQUEST received from client 0c64.c808.0000.
*Mar 10 14:52:07.230: DHCPD: Finding a relay for client 0c64.c808.0000 on interface GigabitEthernet0/2.
*Mar 10 14:52:07.230: DHCPD: Option 125 not present in the msg.
*Mar 10 14:52:07.230: DHCPD: setting giaddr to 10.100.200.1.
*Mar 10 14:52:07.230: DHCPD: BOOTREQUEST from 0c64.c808.0000 forwarded to 203.0.113.67.
*Mar 10 14:52:07.271: DHCPD: client's VPN is .
*Mar 10 14:52:07.271: DHCPD: No option 125
*Mar 10 14:52:07.271: DHCPD: forwarding BOOTREPLY to client 0c64.c808.0000.
*Mar 10 14:52:07.271: DHCPD: Option 125 not present in the msg.
*Mar 10 14:52:07.271: DHCPD: no option 125
*Mar 10 14:52:07.271: DHCPD: src nbma addr as zero
*Mar 10 14:52:07.271: DHCPD: ARP entry exists (10.100.200.79, 0c64.c808.0000).
*Mar 10 14:52:07.271: DHCPD: unicasting BOOTREPLY to client 0c64.c808.0000 (10.100.200.79).

Sorry, for the few seconds difference between IOS (upper) and the dnsmasq (lower) debug output in the time displayed in the logs.

dnsmasq log-debug

dnsmasq writes much about service-identifier with eth0 IP addressing 10.0.0.22. Even the next server and tags eth0

Mar 10 14:51:48 DHCP-67 dnsmasq-dhcp[3501]: DHCP, IP range 10.100.200.10 -- 10.100.200.150, lease time 6h
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 available DHCP range: 10.100.200.10 -- 10.100.200.150
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 vendor class: dhcpcd-10.0.2:Linux-6.1.42-0-virt:x86_64:GenuineIntel
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 client provides name: alpine
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 DHCPDISCOVER(eth0) 0c:64:c8:08:00:00 
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 tags: eth0
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 DHCPOFFER(eth0) 10.100.200.79 0c:64:c8:08:00:00 
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 requested options: 1:netmask, 3:router, 28:broadcast, 33:static-route, 
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 requested options: 51:lease-time, 58:T1, 59:T2
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 next server: 10.0.0.22
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  1 option: 53 message-type  2
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option: 54 server-identifier  10.0.0.22
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option: 51 lease-time  6h
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option: 58 T1  3h
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option: 59 T2  5h15m
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option:  1 netmask  255.255.255.0
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option: 28 broadcast  10.100.200.255
Mar 10 14:52:03 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option:  3 router  10.100.200.1
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 available DHCP range: 10.100.200.10 -- 10.100.200.150
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 vendor class: dhcpcd-10.0.2:Linux-6.1.42-0-virt:x86_64:GenuineIntel
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 client provides name: alpine
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 DHCPREQUEST(eth0) 10.100.200.79 0c:64:c8:08:00:00 
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 tags: eth0
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 DHCPACK(eth0) 10.100.200.79 0c:64:c8:08:00:00 alpine
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 requested options: 1:netmask, 3:router, 28:broadcast, 33:static-route, 
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 requested options: 51:lease-time, 58:T1, 59:T2
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 next server: 10.0.0.22
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  1 option: 53 message-type  5
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option: 54 server-identifier  10.0.0.22
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option: 51 lease-time  6h
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option: 58 T1  3h
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option: 59 T2  5h15m
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option:  1 netmask  255.255.255.0
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option: 28 broadcast  10.100.200.255
Mar 10 14:52:04 DHCP-67 dnsmasq-dhcp[3501]: 2248 sent size:  4 option:  3 router  10.100.200.1

No entry listing lo or 203.0.113.67/32 IPv4 address at all. As if dnsmasq is not aware about any other interface or IP.

DHCP client - dhcpcd

And the client node requesting the IPv4 address is totally happy receiving one. This is the whole process as seen from the DHCP client side:

[...]
dhcpcd-10.0.6 starting
DUID 00:01:00:01:2d:46:fa:64:52:54:00:12:34:56
eth0: waiting for carrier
eth0: carrier acquired
eth0: IAID 98:7d:00:00
eth0: adding address fe80::e62:98ff:fe7d:0
eth0: soliciting a DHCP lease
eth0: soliciting an IPv6 router
eth0: offered 10.100.200.103 from 10.0.0.22
eth0: probing address 10.100.200.103/24
eth0: leased 10.100.200.103 for 21600 seconds
eth0: adding route to 10.100.200.0/24
eth0: adding default route via 10.100.200.1

Cool stuff. Amazing. Using this approach, and the dnsmasq application you can build a service that is script-able and automate-able. A flexible solution for the DHCP protocol. If you do not use any proprietary DHCP DB sync protocols and put a smart IP DHCP scope design on top, you can run a micro-service, redundant, not involving other protocols, and not having IP conflicts.

Conclusion

I have completely no knowledge of the dnsmasq code-base. How this technically works, could be concluded only from the debugging results. This is what I think how it could work:

dnsmasq is using eventually implied defaults on the linux lo interface in its code. This binding of DHCP server to 203.0.113.67/32 which is configured on lo as secondary prefix, started working by excluding the interesting lo interface. I suspect, there is some association between the lo and its default IPv4 prefix 127.0.0.1/8. And excluding lo interface perhaps then excludes that implied hard-coded addressing 127.0.0.1/8 too:

Everything that is left after excluding:

  • eth0
    • 10.0.0.22/30
  • 203.0.113.67/32

Logical conclusion why this setup works for DHCP and UDP.

root@DHCP-67 ~ # ss -tulpn | head -n 4
Netid State  Recv-Q Send-Q               Local Address:Port Peer Address:PortProcess
udp   UNCONN 0      0                          0.0.0.0:67        0.0.0.0:*    users:(("dnsmasq",pid=3807,fd=4)) 
udp   UNCONN 0      0                        10.0.0.22:68        0.0.0.0:*    users:(("dhcpcd",pid=1982,fd=10)) 
udp   UNCONN 0      0      [fe80::eab:fff:feef:0]%eth0:546          [::]:*    users:(("dhcpcd",pid=1963,fd=10))

The socket output does not list anything particular or even a changed setting, neither it lists the /32 IP prefix listening or bound to anything using dnsmasq. it will stick to the all zero address - term at the beginning here.

This is my personal hypothesis. In reality it can be totally different. I have no means of looking it up in the code-base of dnsmasq.

Binding DHCP service to a different interface than the physical one and using providing the DHCP service works, when dnsmasq is the chosen DHCP server application. :)

See also

This is the GNS3 alpine appliance used in this GNS3 netlab. It has minimal resource requirements CPU/MEM/Storage.

References