Re: dhcpleased: default route with classless static routes option

2021-07-18 Thread Bjorn Ketelaars
On Sun 18/07/2021 10:38, Florian Obser wrote:
> On 2021-07-18 01:02 +02, Bjorn Ketelaars  wrote:
> > On Sat 17/07/2021 17:12, Florian Obser wrote:
> >> 
> >> 
> >> On 17 July 2021 13:16:59 CEST, Bjorn Ketelaars  wrote:
> >> >An inconsistency exists between dhclient(8) and dhcpleased(8) when
> >> >receiving the Classless Static Routes option: dhcpleased creates a
> >> >default route, while dhclient does not.
> >> >
> >> >If I'm not mistaken, the behaviour of dhclient is correct. From
> >> >rfc3442:
> >> >"If the DHCP server returns both a Classless Static Routes option and a
> >> >Router option, the DHCP client MUST ignore the Router option."
> >> >
> >> 
> >> Correct. But you are fixing it in the wrong place. It doesn't say to 
> >> ignore a default route, which can be transmitted via classless static 
> >> routes option (it's 0/0).
> >
> > That makes sense. New diff...
> 
> Not quite. This assumes that the router option comes first, a DHCP
> server could send us something like this:
> 
> DHO_ROUTERS
> DHO_CLASSLESS_STATIC_ROUTES
> DHO_ROUTERS
> DHO_CLASSLESS_STATIC_ROUTES
> 
> Which would be quite broken, but hey...
> 
> How about this? Do you actually have a test case for this?

I have a simple test case: DHCP server from ISP (more specific IPTV
platform from KPN), which sends a single router option followed by a
classless static router option. With your diff the router option is
ignored.

This fixes my issue, thank you!



Re: dhcpleased: default route with classless static routes option

2021-07-18 Thread Florian Obser
On 2021-07-18 01:02 +02, Bjorn Ketelaars  wrote:
> On Sat 17/07/2021 17:12, Florian Obser wrote:
>> 
>> 
>> On 17 July 2021 13:16:59 CEST, Bjorn Ketelaars  wrote:
>> >An inconsistency exists between dhclient(8) and dhcpleased(8) when
>> >receiving the Classless Static Routes option: dhcpleased creates a
>> >default route, while dhclient does not.
>> >
>> >If I'm not mistaken, the behaviour of dhclient is correct. From
>> >rfc3442:
>> >"If the DHCP server returns both a Classless Static Routes option and a
>> >Router option, the DHCP client MUST ignore the Router option."
>> >
>> 
>> Correct. But you are fixing it in the wrong place. It doesn't say to ignore 
>> a default route, which can be transmitted via classless static routes option 
>> (it's 0/0).
>
> That makes sense. New diff...

Not quite. This assumes that the router option comes first, a DHCP
server could send us something like this:

DHO_ROUTERS
DHO_CLASSLESS_STATIC_ROUTES
DHO_ROUTERS
DHO_CLASSLESS_STATIC_ROUTES

Which would be quite broken, but hey...

How about this? Do you actually have a test case for this?

dhcpd(8) correctly only hands me a routers option or classless static
routes option, never both.

diff --git engine.c engine.c
index ad8202c9a33..700d7c5ae80 100644
--- engine.c
+++ engine.c
@@ -613,7 +613,7 @@ parse_dhcp(struct dhcpleased_iface *iface, struct imsg_dhcp 
*dhcp)
uint32_t rebinding_time = 0;
uint8_t *p, dho = DHO_PAD, dho_len;
uint8_t  dhcp_message_type = 0;
-   int  routes_len = 0;
+   int  routes_len = 0, routers = 0, csr = 0;
char from[sizeof("xx:xx:xx:xx:xx:xx")];
char to[sizeof("xx:xx:xx:xx:xx:xx")];
char hbuf_src[INET_ADDRSTRLEN];
@@ -836,19 +836,28 @@ parse_dhcp(struct dhcpleased_iface *iface, struct 
imsg_dhcp *dhcp)
if (dho_len % sizeof(routes[routes_len].gw) != 0)
goto wrong_length;
 
-   while (routes_len < MAX_DHCP_ROUTES && dho_len > 0) {
-   memcpy(&routes[routes_len].gw, p,
-   sizeof(routes[routes_len].gw));
-   if (log_getverbose() > 1) {
-   log_debug("DHO_ROUTER: %s",
-   inet_ntop(AF_INET,
-   &routes[routes_len].gw, hbuf,
-   sizeof(hbuf)));
+   /*
+* Ignore routers option if classless static routes
+* are present (RFC3442).
+*/
+   if (!csr) {
+   routers = 1;
+   while (routes_len < MAX_DHCP_ROUTES &&
+   dho_len > 0) {
+   memcpy(&routes[routes_len].gw, p,
+   sizeof(routes[routes_len].gw));
+   if (log_getverbose() > 1) {
+   log_debug("DHO_ROUTER: %s",
+   inet_ntop(AF_INET,
+   &routes[routes_len].gw,
+   hbuf, sizeof(hbuf)));
+   }
+   p += sizeof(routes[routes_len].gw);
+   rem -= sizeof(routes[routes_len].gw);
+   dho_len -=
+   sizeof(routes[routes_len].gw);
+   routes_len++;
}
-   p += sizeof(routes[routes_len].gw);
-   rem -= sizeof(routes[routes_len].gw);
-   dho_len -= sizeof(routes[routes_len].gw);
-   routes_len++;
}
if (dho_len != 0) {
/* ignore > MAX_DHCP_ROUTES routes */
@@ -939,6 +948,15 @@ parse_dhcp(struct dhcpleased_iface *iface, struct 
imsg_dhcp *dhcp)
case DHO_CLASSLESS_STATIC_ROUTES: {
int prefixlen, compressed_prefixlen;
 
+   csr = 1;
+   if (routers) {
+   /*
+* Ignore routers option if classless static
+* routes are present (RFC3442).
+*/
+   routers = 0;
+   routes_len = 0;
+   }
while (routes_len < MAX_DHCP_ROUTES && dho_len > 0

Re: dhcpleased: default route with classless static routes option

2021-07-17 Thread Bjorn Ketelaars
On Sat 17/07/2021 17:12, Florian Obser wrote:
> 
> 
> On 17 July 2021 13:16:59 CEST, Bjorn Ketelaars  wrote:
> >An inconsistency exists between dhclient(8) and dhcpleased(8) when
> >receiving the Classless Static Routes option: dhcpleased creates a
> >default route, while dhclient does not.
> >
> >If I'm not mistaken, the behaviour of dhclient is correct. From
> >rfc3442:
> >"If the DHCP server returns both a Classless Static Routes option and a
> >Router option, the DHCP client MUST ignore the Router option."
> >
> 
> Correct. But you are fixing it in the wrong place. It doesn't say to ignore a 
> default route, which can be transmitted via classless static routes option 
> (it's 0/0).

That makes sense. New diff...


diff --git sbin/dhcpleased/engine.c sbin/dhcpleased/engine.c
index ad8202c9a33..b3a86e68398 100644
--- sbin/dhcpleased/engine.c
+++ sbin/dhcpleased/engine.c
@@ -613,7 +613,7 @@ parse_dhcp(struct dhcpleased_iface *iface, struct imsg_dhcp 
*dhcp)
uint32_t rebinding_time = 0;
uint8_t *p, dho = DHO_PAD, dho_len;
uint8_t  dhcp_message_type = 0;
-   int  routes_len = 0;
+   int  routes_len = 0, csr_len = 0, n;
char from[sizeof("xx:xx:xx:xx:xx:xx")];
char to[sizeof("xx:xx:xx:xx:xx:xx")];
char hbuf_src[INET_ADDRSTRLEN];
@@ -979,6 +979,7 @@ parse_dhcp(struct dhcpleased_iface *iface, struct imsg_dhcp 
*dhcp)
dho_len -= sizeof(routes[routes_len].gw);
 
routes_len++;
+   csr_len++;
}
 
if (dho_len != 0) {
@@ -996,6 +997,20 @@ parse_dhcp(struct dhcpleased_iface *iface, struct 
imsg_dhcp *dhcp)
}
 
}
+
+   /*
+   * RFC3442 states that if the DHCP server returns both a Classless Static
+   * Routes option and a Router option, the DHCP client MUST ignore the
+   * Router option.
+   */
+   if (csr_len > 0) {
+   log_warnx("%s: ignoring router option", __func__);
+   for (n = 0; n < routes_len - csr_len; n++) {
+   routes[n] = routes[n + 1];
+   routes_len--;
+   }
+   }
+
while (rem != 0) {
if (*p != DHO_PAD)
break;



Re: dhcpleased: default route with classless static routes option

2021-07-17 Thread Florian Obser



On 17 July 2021 13:16:59 CEST, Bjorn Ketelaars  wrote:
>An inconsistency exists between dhclient(8) and dhcpleased(8) when
>receiving the Classless Static Routes option: dhcpleased creates a
>default route, while dhclient does not.
>
>If I'm not mistaken, the behaviour of dhclient is correct. From
>rfc3442:
>"If the DHCP server returns both a Classless Static Routes option and a
>Router option, the DHCP client MUST ignore the Router option."
>

Correct. But you are fixing it in the wrong place. It doesn't say to ignore a 
default route, which can be transmitted via classless static routes option 
(it's 0/0).

You need to fix this in parse_dhcp in engine.c

>Comments?
>
>
>diff --git sbin/dhcpleased/dhcpleased.c sbin/dhcpleased/dhcpleased.c
>index 2157406e71a..93df9f55e7f 100644
>--- sbin/dhcpleased/dhcpleased.c
>+++ sbin/dhcpleased/dhcpleased.c
>@@ -819,7 +819,7 @@ configure_routes(uint8_t rtm_type, struct
>imsg_configure_interface *imsg)
> {
>   struct sockaddr_in   dst, mask, gw, ifa;
>   in_addr_taddrnet, gwnet;
>-  int  i;
>+  int  csr = 0, i;
> 
>   memset(&ifa, 0, sizeof(ifa));
>   ifa.sin_family = AF_INET;
>@@ -840,6 +840,13 @@ configure_routes(uint8_t rtm_type, struct
>imsg_configure_interface *imsg)
> 
>   addrnet = imsg->addr.s_addr & imsg->mask.s_addr;
> 
>+  for (i = 0; i < imsg->routes_len; i++) {
>+  if (imsg->routes[i].dst.s_addr != INADDR_ANY) {
>+  csr = 1;
>+  break;
>+  }
>+  }
>+
>   for (i = 0; i < imsg->routes_len; i++) {
>   dst.sin_addr.s_addr = imsg->routes[i].dst.s_addr;
>   mask.sin_addr.s_addr = imsg->routes[i].mask.s_addr;
>@@ -872,6 +879,15 @@ configure_routes(uint8_t rtm_type, struct
>imsg_configure_interface *imsg)
>   /* directly connected default */
>   configure_route(rtm_type, imsg->if_index,
>   imsg->rdomain, &dst, &mask, &gw, NULL, 0);
>+  } else if (csr) {
>+  /*
>+  * RFC3442 states that if the DHCP server returns
>+  * both a Classless Static Routes option and a
>+  * Router option, the DHCP client MUST ignore the
>+  * Router option.
>+  */
>+  log_warnx("%s: ignoring router option",
>+  __func__);
>   } else {
>   /* default route via gateway */
>   configure_route(rtm_type, imsg->if_index,

-- 
Sent from a mobile device. Please excuse poor formatting.



Re: dhcpleased: default route with classless static routes option

2021-07-17 Thread Stuart Henderson
On 2021/07/17 13:16, Bjorn Ketelaars wrote:
> An inconsistency exists between dhclient(8) and dhcpleased(8) when
> receiving the Classless Static Routes option: dhcpleased creates a
> default route, while dhclient does not.
> 
> If I'm not mistaken, the behaviour of dhclient is correct. From rfc3442:
> "If the DHCP server returns both a Classless Static Routes option and a
> Router option, the DHCP client MUST ignore the Router option."

That is correct.

> Comments?
> 
> 
> diff --git sbin/dhcpleased/dhcpleased.c sbin/dhcpleased/dhcpleased.c
> index 2157406e71a..93df9f55e7f 100644
> --- sbin/dhcpleased/dhcpleased.c
> +++ sbin/dhcpleased/dhcpleased.c
> @@ -819,7 +819,7 @@ configure_routes(uint8_t rtm_type, struct 
> imsg_configure_interface *imsg)
>  {
>   struct sockaddr_in   dst, mask, gw, ifa;
>   in_addr_taddrnet, gwnet;
> - int  i;
> + int  csr = 0, i;
>  
>   memset(&ifa, 0, sizeof(ifa));
>   ifa.sin_family = AF_INET;
> @@ -840,6 +840,13 @@ configure_routes(uint8_t rtm_type, struct 
> imsg_configure_interface *imsg)
>  
>   addrnet = imsg->addr.s_addr & imsg->mask.s_addr;
>  
> + for (i = 0; i < imsg->routes_len; i++) {
> + if (imsg->routes[i].dst.s_addr != INADDR_ANY) {
> + csr = 1;
> + break;
> + }
> + }
> +
>   for (i = 0; i < imsg->routes_len; i++) {
>   dst.sin_addr.s_addr = imsg->routes[i].dst.s_addr;
>   mask.sin_addr.s_addr = imsg->routes[i].mask.s_addr;
> @@ -872,6 +879,15 @@ configure_routes(uint8_t rtm_type, struct 
> imsg_configure_interface *imsg)
>   /* directly connected default */
>   configure_route(rtm_type, imsg->if_index,
>   imsg->rdomain, &dst, &mask, &gw, NULL, 0);
> + } else if (csr) {
> + /*
> + * RFC3442 states that if the DHCP server returns
> + * both a Classless Static Routes option and a
> + * Router option, the DHCP client MUST ignore the
> + * Router option.
> + */
> + log_warnx("%s: ignoring router option",
> + __func__);
>   } else {
>   /* default route via gateway */
>   configure_route(rtm_type, imsg->if_index,
> 



dhcpleased: default route with classless static routes option

2021-07-17 Thread Bjorn Ketelaars
An inconsistency exists between dhclient(8) and dhcpleased(8) when
receiving the Classless Static Routes option: dhcpleased creates a
default route, while dhclient does not.

If I'm not mistaken, the behaviour of dhclient is correct. From rfc3442:
"If the DHCP server returns both a Classless Static Routes option and a
Router option, the DHCP client MUST ignore the Router option."

Comments?


diff --git sbin/dhcpleased/dhcpleased.c sbin/dhcpleased/dhcpleased.c
index 2157406e71a..93df9f55e7f 100644
--- sbin/dhcpleased/dhcpleased.c
+++ sbin/dhcpleased/dhcpleased.c
@@ -819,7 +819,7 @@ configure_routes(uint8_t rtm_type, struct 
imsg_configure_interface *imsg)
 {
struct sockaddr_in   dst, mask, gw, ifa;
in_addr_taddrnet, gwnet;
-   int  i;
+   int  csr = 0, i;
 
memset(&ifa, 0, sizeof(ifa));
ifa.sin_family = AF_INET;
@@ -840,6 +840,13 @@ configure_routes(uint8_t rtm_type, struct 
imsg_configure_interface *imsg)
 
addrnet = imsg->addr.s_addr & imsg->mask.s_addr;
 
+   for (i = 0; i < imsg->routes_len; i++) {
+   if (imsg->routes[i].dst.s_addr != INADDR_ANY) {
+   csr = 1;
+   break;
+   }
+   }
+
for (i = 0; i < imsg->routes_len; i++) {
dst.sin_addr.s_addr = imsg->routes[i].dst.s_addr;
mask.sin_addr.s_addr = imsg->routes[i].mask.s_addr;
@@ -872,6 +879,15 @@ configure_routes(uint8_t rtm_type, struct 
imsg_configure_interface *imsg)
/* directly connected default */
configure_route(rtm_type, imsg->if_index,
imsg->rdomain, &dst, &mask, &gw, NULL, 0);
+   } else if (csr) {
+   /*
+   * RFC3442 states that if the DHCP server returns
+   * both a Classless Static Routes option and a
+   * Router option, the DHCP client MUST ignore the
+   * Router option.
+   */
+   log_warnx("%s: ignoring router option",
+   __func__);
} else {
/* default route via gateway */
configure_route(rtm_type, imsg->if_index,