rtbrick / bngblaster

The BNG Blaster is an open-source network tester for access and routing protocols.
https://rtbrick.github.io/bngblaster/
BSD 3-Clause "New" or "Revised" License
206 stars 34 forks source link

null DHCP siaddr being used for renews and releases #261

Closed mivsvit closed 1 month ago

mivsvit commented 4 months ago

Describe the bug

DHCPv4 logs from bngblaster show session established successfully but renews failing. After the dhcp retry count is exceeded, a full DORA, where the discover and request are sent to the all-1s broadcast address succeeds.

May 29 14:01:10.465772 Resolve network interfaces
May 29 14:01:10.465897 All network interfaces resolved
May 29 14:01:10.465963 DHCP (ID: 1) Start DHCP
May 29 14:01:10.466216 DHCP (ID: 1) DHCP-Discover send
May 29 14:01:10.471384 DHCP (ID: 1) DHCP-Offer received
May 29 14:01:10.471486 DHCP (ID: 1) DHCP-Request send
May 29 14:01:10.477014 DHCP (ID: 1) DHCP-ACK received
May 29 14:01:10.477528 ALL SESSIONS ESTABLISHED
May 29 14:01:11.065770 ALL SESSION TRAFFIC FLOWS VERIFIED
May 29 14:03:40.477313 DHCP (ID: 1) DHCP-Request send
May 29 14:03:45.477680 DHCP (ID: 1) DHCP-Request send
May 29 14:03:50.478129 DHCP (ID: 1) DHCP-Request send
May 29 14:03:55.478550 DHCP (ID: 1) DHCP-Request send
May 29 14:04:00.479066 DHCP (ID: 1) DHCP-Request send
May 29 14:04:05.479478 DHCP (ID: 1) DHCP-Request send
May 29 14:04:10.479806 DHCP (ID: 1) DHCP-Request send
May 29 14:04:15.480163 DHCP (ID: 1) DHCP-Request send
May 29 14:04:20.480481 DHCP (ID: 1) DHCP-Request send
May 29 14:04:25.480983 DHCP (ID: 1) DHCP-Request send
May 29 14:04:30.481197 DHCP (ID: 1) Stop DHCP
May 29 14:04:30.481322 DHCP (ID: 1) Start DHCP
May 29 14:04:30.481385 DHCP (ID: 1) DHCP-Discover send
May 29 14:04:30.499269 DHCP (ID: 1) DHCP-Offer received
May 29 14:04:30.499398 DHCP (ID: 1) DHCP-Request send
May 29 14:04:30.505665 DHCP (ID: 1) DHCP-ACK received
May 29 14:07:00.506045 DHCP (ID: 1) DHCP-Request send
May 29 14:07:05.506462 DHCP (ID: 1) DHCP-Request send
May 29 14:07:10.506874 DHCP (ID: 1) DHCP-Request send
May 29 14:07:15.507099 DHCP (ID: 1) DHCP-Request send
May 29 14:07:20.507421 DHCP (ID: 1) DHCP-Request send
May 29 14:07:25.507798 DHCP (ID: 1) DHCP-Request send
May 29 14:07:30.508222 DHCP (ID: 1) DHCP-Request send
May 29 14:07:35.508512 DHCP (ID: 1) DHCP-Request send
May 29 14:07:40.508878 DHCP (ID: 1) DHCP-Request send
May 29 14:07:45.509246 DHCP (ID: 1) DHCP-Request send
May 29 14:07:50.509462 DHCP (ID: 1) Stop DHCP
May 29 14:07:50.509570 DHCP (ID: 1) Start DHCP
May 29 14:07:50.509813 DHCP (ID: 1) DHCP-Discover send
May 29 14:07:50.515829 DHCP (ID: 1) DHCP-Offer received
May 29 14:07:50.516102 DHCP (ID: 1) DHCP-Request send
May 29 14:07:50.521918 DHCP (ID: 1) DHCP-ACK received
May 29 14:10:20.522290 DHCP (ID: 1) DHCP-Request send
May 29 14:10:25.043940 Teardown request (1)
May 29 14:10:25.522776 DHCP (ID: 1) DHCP-Request send
May 29 14:10:26.505391 DHCP (ID: 1) DHCP-Release send
May 29 14:10:27.505862 DHCP (ID: 1) DHCP-Release send
May 29 14:10:28.506383 DHCP (ID: 1) DHCP-Release send
May 29 14:10:28.506523 ALL SESSIONS TERMINATED

A packet capture from bngblaster shows a null destination address for the DHCP renew and releases. Expected behaviour is that the siaddr from DHCP option 54 is used as a unicast destination address of the renew and releases.

    7   0.998132      0.0.0.0 → 255.255.255.255 DHCP 344 DHCP Discover - Transaction ID 0x5344215c
   12   1.003300  172.31.19.2 → 255.255.255.255 DHCP 346 DHCP Offer    - Transaction ID 0x5344215c
   13   1.003300      0.0.0.0 → 255.255.255.255 DHCP 352 DHCP Request  - Transaction ID 0x5344215c
   14   1.008932  172.31.19.2 → 255.255.255.255 DHCP 346 DHCP ACK      - Transaction ID 0x5344215c
  123 151.009232 100.66.48.150 → 0.0.0.0      DHCP 336 DHCP Request  - Transaction ID 0x93af8839
  127 156.009597 100.66.48.150 → 0.0.0.0      DHCP 336 DHCP Request  - Transaction ID 0x93af8839
  131 161.010045 100.66.48.150 → 0.0.0.0      DHCP 336 DHCP Request  - Transaction ID 0x93af8839
  136 166.010464 100.66.48.150 → 0.0.0.0      DHCP 336 DHCP Request  - Transaction ID 0x93af8839
  140 171.010981 100.66.48.150 → 0.0.0.0      DHCP 336 DHCP Request  - Transaction ID 0x93af8839
  144 176.011394 100.66.48.150 → 0.0.0.0      DHCP 336 DHCP Request  - Transaction ID 0x93af8839
  149 181.011722 100.66.48.150 → 0.0.0.0      DHCP 336 DHCP Request  - Transaction ID 0x93af8839
  153 186.012079 100.66.48.150 → 0.0.0.0      DHCP 336 DHCP Request  - Transaction ID 0x93af8839
  157 191.012397 100.66.48.150 → 0.0.0.0      DHCP 336 DHCP Request  - Transaction ID 0x93af8839
  162 196.012900 100.66.48.150 → 0.0.0.0      DHCP 336 DHCP Request  - Transaction ID 0x93af8839
  166 201.013301      0.0.0.0 → 255.255.255.255 DHCP 344 DHCP Discover - Transaction ID 0x6cfa5c63
  167 201.031183  172.31.19.2 → 255.255.255.255 DHCP 346 DHCP Offer    - Transaction ID 0x6cfa5c63
  168 201.031183      0.0.0.0 → 255.255.255.255 DHCP 352 DHCP Request  - Transaction ID 0x6cfa5c63
  169 201.037583  172.31.19.2 → 255.255.255.255 DHCP 346 DHCP ACK      - Transaction ID 0x6cfa5c63
  [snip]
   442 557.037307 100.66.48.150 → 0.0.0.0      DHCP 304 DHCP Release  - Transaction ID 0xecceb2d
  444 558.037776 100.66.48.150 → 0.0.0.0      DHCP 304 DHCP Release  - Transaction ID 0xecceb2d
  445 559.038298 100.66.48.150 → 0.0.0.0      DHCP 304 DHCP Release  - Transaction ID 0xecceb2d

Packet capture of the first DHCP ACK (packet 14), including the siaddr:

Dynamic Host Configuration Protocol (ACK)
    Message type: Boot Reply (2)
    Hardware type: Ethernet (0x01)
    Hardware address length: 6
    Hops: 0
    Transaction ID: 0x5344215c
    Seconds elapsed: 0
    Bootp flags: 0x8000, Broadcast flag (Broadcast)
        1... .... .... .... = Broadcast flag: Broadcast
        .000 0000 0000 0000 = Reserved flags: 0x0000
    Client IP address: 0.0.0.0
    Your (client) IP address: 100.66.48.150
    Next server IP address: 0.0.0.0
    Relay agent IP address: 10.9.23.3
    Client MAC address: 02:00:01:00:00:01 (02:00:01:00:00:01)
    Client hardware address padding: 00000000000000000000
    Server host name not given
    Boot file name not given
    Magic cookie: DHCP
    Option: (53) DHCP Message Type (ACK)
        Length: 1
        DHCP: ACK (5)
    Option: (1) Subnet Mask (255.254.0.0)
        Length: 4
        Subnet Mask: 255.254.0.0
    Option: (3) Router
        Length: 4
        Router: 100.66.0.1
    Option: (51) IP Address Lease Time
        Length: 4
        IP Address Lease Time: (300s) 5 minutes
    Option: (54) DHCP Server Identifier (172.31.19.2)
        Length: 4
        DHCP Server Identifier: 172.31.19.2
    Option: (58) Renewal Time Value
        Length: 4
        Renewal Time Value: (150s) 2 minutes, 30 seconds
    Option: (59) Rebinding Time Value
        Length: 4
        Rebinding Time Value: (262s) 4 minutes, 22 seconds
    Option: (6) Domain Name Server
        Length: 8
        Domain Name Server: 8.8.8.8
        Domain Name Server: 1.1.1.1
    Option: (255) End
        Option End: 255
    Padding: 00000000000000000000

To Reproduce

Version (bngblaster -v):

$ bngblaster -v
Version: 0.8.51
Compiler: GNU (9.4.0)
IO Modes: packet_mmap_raw (default), packet_mmap, raw

JSON configuration:

{
   "interfaces": {
        "qdisc-bypass": true,
        "mac-modifier": 1,
        "io-mode": "packet_mmap_raw",
        "_capture-include-streams": true,
         "network": {
         "interface": "ens160.2016",
         "address": "172.31.19.5",
         "gateway": "172.31.19.4",
         "address-ipv6": "2001:db8:1:2016::2",
         "gateway-ipv6": "2001:db8:1:2016::1"
        },
        "access": [
        {
            "interface": "ens192",
            "type": "ipoe",
            "outer-vlan-min": 2012,
            "outer-vlan-max": 2015,
            "inner-vlan-min": 2,
            "inner-vlan-max": 4094,
            "ipv4": true,
            "ipv6": false,
            "vlan-mode": "1:1"
        }
     ]
    },
    "sessions": {
        "count": 1,
        "max-outstanding": 16000,
        "start-rate": 1000,
        "stop-rate": 1000
    },
    "access-line": {
        "agent-remote-id": "BLAST2012{session-global}",
        "agent-circuit-id": "0.0.0.0/0.0.0.0 eth 0:{session-global}"
    },
    "dhcp": {
        "broadcast": true,
        "enable": true
    },
    "dhcpv6": {
        "enable": true,
        "ia-na": false,
        "ldra": true,
        "rapid-commit": true
    },
   "session-traffic": {
        "ipv4-pps": 1,
        "ipv6pd-pps": 1
    }
}

Expected behavior

The siaddr (in the above example, 172.31.19.2) is the expected destination address for the DHCP request and release packets rather than 0.0.0.0.

rfc2131 section-4.4.5 says "At time T1 the client moves to RENEWING state and sends (via unicast) a DHCPREQUEST message to the server to extend its lease." and rfc2131 section-4.1 says "DHCP clients MUST use the IP address provided in the 'server identifier' option for any unicast requests to the DHCP server."

This appears to be the intention from the code: https://github.com/rtbrick/bngblaster/blob/a913ac73c1d42f9d93149969aacfd5d1415202b1/code/bngblaster/src/bbl_dhcp.c#L281 https://github.com/rtbrick/bngblaster/blob/a913ac73c1d42f9d93149969aacfd5d1415202b1/code/bngblaster/src/bbl_tx.c#L1185

GIC-de commented 3 months ago

Sorry for the late response.

In the ACK the siaddr (Next server IP address) is zero, but giaddr (Relay agent IP address) is set, which is not expected on packets received by clients. I could implement to renew to broadcast if server address is zero, but need to check if this is allowed by RFC.

Dynamic Host Configuration Protocol (ACK)
    Message type: Boot Reply (2)
...
    Your (client) IP address: 100.66.48.150
    Next server IP address: 0.0.0.0
    Relay agent IP address: 10.9.23.3

Which DHCP server is used here?

mivsvit commented 1 month ago

Apologies for not spotting your reply earlier.

It's ISC's Kea DHCP server. The BNG has a local DHCP proxy service sitting between kea and the client and will include the siaddr / Next-Server if it is present in the response from the external server.

If I configure Kea to add a siaddr then the bngblaster behaviour is as you describe.

Maybe there is some confusion between siaddr and DHCP Option 54 Server Identifier?

My understanding is that option 54 Server-Identifier rather than siaddr should be used to determine the destination address for the renew as [rfc2131 section-4.1] says "DHCP clients MUST use the IP address provided in the 'server identifier' option for any unicast requests to the DHCP server."

And for siaddr, the RFC says "A DHCP server may return its own address in the 'siaddr' field, if the server is prepared to supply the next bootstrap service (e.g., delivery of an operating system executable image)."

There is no next stage of the bootstrap- the server is not providing an operating system for a broadband CPE or emulated bngblaster client so I would not have thought it is appropriate to set next-server.

I've compared the bngblaster behaviour with ISC's dhclient. With no siaddr present it sends the renews to the unicast address it received the Offer/Ack from which is also the address in the option 54 Server-ID. This is the behaviour I expected.

Edit: I see the behaviour I would expect with bngblaster if I make this code change.

diff --git a/code/bngblaster/src/bbl_tx.c b/code/bngblaster/src/bbl_tx.c
index 0bb814c..af75533 100644
--- a/code/bngblaster/src/bbl_tx.c
+++ b/code/bngblaster/src/bbl_tx.c
@@ -1197,7 +1197,7 @@ bbl_tx_encode_packet_dhcp(bbl_session_s *session)
             session->stats.dhcp_tx_request++;
             LOG(DHCP, "DHCP (ID: %u) DHCP-Request send\n", session->session_id);
             eth.dst = session->dhcp_server_mac;
-            ipv4.dst = session->dhcp_server;
+            ipv4.dst = session->dhcp_server_identifier;
             header.ciaddr = session->ip_address;
             break;
         case BBL_DHCP_RELEASE:
@@ -1205,7 +1205,7 @@ bbl_tx_encode_packet_dhcp(bbl_session_s *session)
             session->stats.dhcp_tx_release++;
             LOG(DHCP, "DHCP (ID: %u) DHCP-Release send\n", session->session_id);
             eth.dst = session->dhcp_server_mac;
-            ipv4.dst = session->dhcp_server;
+            ipv4.dst = session->dhcp_server_identifier;
             header.ciaddr = session->ip_address;
             dhcp.option_server_identifier = true;
             dhcp.server_identifier = session->dhcp_server_identifier;
@@ -1692,4 +1692,4 @@ bbl_tx(bbl_interface_s *interface, uint8_t *buf, uint16_t *len)
         network_interface = network_interface->next;
     }
     return result;
GIC-de commented 1 month ago

Thanks for the great input and the provided fix (a6e7c27) which will be included in the next release, scheduled for this week.