Opened 4 years ago

Closed 4 years ago

Last modified 4 years ago

#4308 closed defect (fixed)

Kea does not handle multiple v4 addresses on the interface + direct traffic

Reported by: tomek Owned by: tomek
Priority: medium Milestone: Kea1.1
Component: dhcp4 Version: git
Keywords: Cc:
CVSS Scoring: Parent Tickets:
Sensitive: no Defect Severity: N/A
Sub-Project: DHCP Feature Depending on Ticket:
Estimated Difficulty: 0 Add Hours to Ticket: 0.5
Total Hours: 3 Internal?: no

Description

Kea does not handle well the case where there are multiple IPv4 addresses on the interface.
This was discovered when investigating a bug report sent by a user:

I want to make a DHCP server For FTTH optical modem manegement, clients too many, maybe 100000+,so has many subnet。 The server is a DHCP server and gateway server, so I config many IP address on interface, in my test project, rc.conf as follow:
gateway_enable = "YES"
ifconfig_em1="inet 192.168.0.1 netmask 255.255.255.0"
ifconfig_em1_alias0="inet 192.168.1.1 netmask 255.255.255.0" 
ifconfig_em1_alias1="inet 192.168.2.1 netmask 255.255.255.0"
 
kea.conf as follow:
"Dhcp4":
{
  "option-data": [{
                    "name": "domain-name-servers",
                    "code": 6,
                    "space": "dhcp4",
                    "csv-format": true,
                    "data": "118.118.118.9"
                }],
  "interfaces-config": {
    "interfaces": ["em1"]
  },
...
 "subnet4": [
  {
       "option-data": [{"name":"routers", "code":3, "space":"dhcp4", "csv-format":true, "data":"192.168.0.1"}], 
       "subnet": "192.168.0.0/24",
       "pools": [ { "pool": "192.168.0.2 - 192.168.0.254" } ]
  },
  {
       "option-data": [{"name":"routers", "code":3, "space":"dhcp4", "csv-format":true, "data":"192.168.1.1"}], 
       "subnet": "192.168.1.0/24",
       "pools": [ { "pool": "192.168.1.2 - 192.168.1.254" } ]
  },
  {
       "option-data": [{"name":"routers", "code":3, "space":"dhcp4", "csv-format":true, "data":"192.168.2.1"}],   
       "subnet": "192.168.2.0/24",
       "pools": [ { "pool": "192.168.2.2 - 192.168.2.254" } ]
  }]
}
 
192.168.0.1 is first IP on interface, kea allocate this subnet for clients only,  if I change 192.168.1.1 as first IP and 192.168.0.1 as alias IP, then stop kea and restart kea, kea allocate 192.168.1.0/24 subnet for clients only, dosen't allocate 192.168.0.0/24 and 192.168.2.0/24. If I change a IP as first on interface not include kea pools, eg 10.0.0.1, kea then allocate 192.168.0.0/24 subnet for clients only. This is kea bug or my kea.conf is wrong? How to solve this problem?

While shared subnets is not yet supported, after some investigation we came to a realisation that supporting traffic on the interface with several v4 addresses can be improved.

Continuing this example, it doesn't seem possible to tell kea to serve a subnet 192.168.1.0/24. Here's the exact explanation. For direct traffic, we need interface parameter to be specified. However, even if interface: "em1" is added to the 192.168.1.0/24 subnet, it wouldn't work.

The code in CfgSubnets4::selectSubnet(const SubnetSelector?& selector) starts by doing various searches that are unrelated to the situation. Then it reaches this code:

if (!selector.iface_name_.empty()) {
        IfacePtr iface = IfaceMgr::instance().getIface(selector.iface_name_);
        ...
        iface->getAddress4(address);
    }


    // We have identified an address in the client's packet that can be
    // used for subnet selection. Match this packet with the subnets.
    return (selectSubnet(address, selector.client_classes_));

The problem here is that the iface->getAddress4() will always return the first address of the interface and then this address will be used to select a subnet. As a result, for directly connected clients Kea is able to select subnet that matches its first IPv4 address on the interface the packet was received from. Clearly, this could be improved.

Marcin suggested two possible things to consider here:

  1. we could do the subnet selection based on the IPv4 address of the socket used to receive the data.
  2. we could use server-id

Both of those require some thinking. Using sockets adds one extra level of indirection to the logic, which may make the decision more confusing. On the other hand, Marcin pointed out that there is typically only one socket per interface, thus it wouldn't have any performance degradation.

Alternatively, instead of calling iface->getAddress4() and calling selectSubnet(address, classes), we could implement selectSubnet(interface, classes) that would pick the subnet that has the interface specified, regardless of the (mis)matched IP addresses.

Subtickets

Change History (11)

comment:1 Changed 4 years ago by tomek

Postscriptum: In any case, we are not going to add support for shared networks, i.e. after addressing this issue, it still will not be possible to define several subnets on link and expect Kea to somehow share leases from all of them.

comment:2 Changed 4 years ago by hschempf

  • Milestone changed from Kea-proposed to Outstanding Tasks

Per team meeting 3/10, move to outstanding

comment:3 Changed 4 years ago by tomek

After debugging of unrelated bug, I found out that this issue has significantly bigger impact than initially thought. I have a server with 192.0.2.0/24 subnet specified in my config. This subnet has "interface": "eth1" specified. However, the actual address on the interface is 10.2.3.5/24. This causes the server to not be able to do subnet selection at all. Although this was a configuration error, it's a good representation for shared subnets.

The bottom line is we need a subnetSelection routine that selects subnet based on interface names, not doing assumptions and tricks with addresses.

comment:4 Changed 4 years ago by tomek

  • Milestone changed from Outstanding Tasks to Kea-proposed
  • Status changed from new to reviewing

Although this ticket is currently in Outstanding, I have encountered this issue while investigating an unrelated PXE issue and decided to fix the subnet selection based
on the interface name. It does not address the generic multiple addresses/shared subnet,
but at least makes possible to select a subnet based on interface name.

The code is ready for review on trac4308 branch. Proposed ChangeLog?:

1XXX.	[func]		tomek
	Subnet selection in DHCPv4 will now work, even if the addresses
	configured on interface are not in range of the subnet definition
	from the configuration file.
	(Trac #4308, git tbd)

Also, moving to Kea-proposed to bring people's attention to this ticket.

Last edited 4 years ago by tomek (previous) (diff)

comment:5 Changed 4 years ago by hschempf

  • Milestone changed from Kea-proposed to Kea1.1

Per team meeting, accept 1.1 as bug fixing. Estimated time remaining = .5d

comment:6 Changed 4 years ago by sar

  • Owner set to sar

comment:7 follow-up: Changed 4 years ago by sar

  • Total Hours changed from 0 to 2

I have fixed several typo level items please do a pull before continuing.

src/lib/dhcpsrv/cfg_subnets4.cc

In the selectSubnet() functions lines 103, 185 and 209 I find the comment
// Eliminate those subnets that do not meet client class criteria.
a bit misleading. I would have gone with something like
// If a subnet meets the client class criteria return it.

In the LOG_DEBUG statements the two .arg statements are on the same line which isn't what I've seen elsewhere in the code.

src/lib/dhcpsrv/cfgsubnets4_unittest.cc

In the code in cfgsubnets4 it appears that if there isn't a subnet with the given interface name that we will continue trying to get a subnet via the address. I didn't see a test that seemed to handle that condition. Should there be one?

comment:8 Changed 4 years ago by sar

  • Owner changed from sar to tomek

comment:9 in reply to: ↑ 7 Changed 4 years ago by tomek

  • Owner changed from tomek to sar

Replying to sar:

I have fixed several typo level items please do a pull before continuing.

Thanks.

src/lib/dhcpsrv/cfg_subnets4.cc

In the selectSubnet() functions lines 103, 185 and 209 I find the comment
// Eliminate those subnets that do not meet client class criteria.
a bit misleading. I would have gone with something like
// If a subnet meets the client class criteria return it.

Updated as suggested.

In the LOG_DEBUG statements the two .arg statements are on the same line which isn't what I've seen elsewhere in the code.

There were two instances of .arg() in the same line. Updated both.

src/lib/dhcpsrv/cfgsubnets4_unittest.cc

In the code in cfgsubnets4 it appears that if there isn't a subnet with the given interface name that we will continue trying to get a subnet via the address. I didn't see a test that seemed to handle that condition. Should there be one?

I believe there is one already. See first case of !CfgSubnets4Test.!selectSubnetInterface. The packet comes in through eth0. There is a 10.0.0.0/24 subnet, which is not explicitly associated with eth0. The code will fail to select the subnet based on interface name, but then will check that eth0 has 10.0.0.1 address that belongs to that subnet and will select it. This is checked in lines 317-319. Is this the scenario you wanted to test?

comment:10 Changed 4 years ago by sar

  • Owner changed from sar to tomek
  • Total Hours changed from 2 to 2.5

the changes look fine

the test you pointed out is what I wanted

ready to merge

comment:11 Changed 4 years ago by tomek

  • Add Hours to Ticket changed from 0 to 0.5
  • Resolution set to fixed
  • Status changed from reviewing to closed
  • Total Hours changed from 2.5 to 3

Thanks for the review. Code merged. Closing ticket.

Last edited 4 years ago by tomek (previous) (diff)
Note: See TracTickets for help on using tickets.