[klibc] [klibc:master] ipconfig: Implement classless static routes

klibc-bot for Benjamin Drung benjamin.drung at profitbricks.com
Fri Jan 18 08:42:10 PST 2019


Commit-ID:  ee59de58cd3ebe9e98d19aeaadb39915b0b235fc
Gitweb:     http://git.kernel.org/?p=libs/klibc/klibc.git;a=commit;h=ee59de58cd3ebe9e98d19aeaadb39915b0b235fc
Author:     Benjamin Drung <benjamin.drung at profitbricks.com>
AuthorDate: Wed, 13 Dec 2017 23:04:29 +0100
Committer:  Ben Hutchings <ben at decadent.org.uk>
CommitDate: Wed, 2 Jan 2019 03:08:04 +0000

[klibc] ipconfig: Implement classless static routes

Implement classless static routes support as specified in RFC3442.

Bug-Debian: https://bugs.debian.org/884716
Bug-Ubuntu: https://launchpad.net/bugs/1526956
Signed-off-by: Benjamin Drung <benjamin.drung at profitbricks.com>
Link: https://salsa.debian.org/kernel-team/klibc/merge_requests/2
Link: https://www.zytor.com/pipermail/klibc/2018-June/003993.html
Signed-off-by: Ben Hutchings <ben at decadent.org.uk>

---
 usr/kinit/ipconfig/bootp_proto.c | 109 +++++++++++++++++++++++++++++++++++++++
 usr/kinit/ipconfig/dhcp_proto.c  |   1 +
 usr/kinit/ipconfig/main.c        |  54 +++++++++++++++----
 usr/kinit/ipconfig/netdev.c      |  49 +++++++++++++-----
 usr/kinit/ipconfig/netdev.h      |  24 ++++++++-
 5 files changed, 212 insertions(+), 25 deletions(-)

diff --git a/usr/kinit/ipconfig/bootp_proto.c b/usr/kinit/ipconfig/bootp_proto.c
index 150ebfa..f6f9dd4 100644
--- a/usr/kinit/ipconfig/bootp_proto.c
+++ b/usr/kinit/ipconfig/bootp_proto.c
@@ -267,6 +267,87 @@ static char *bootp_ext119_decode(const void *ext, int16_t ext_size, void *tmp)
 	return decoded_str;
 }
 
+/*
+ * DESCRIPTION
+ *  bootp_ext121_decode() decodes Classless Route Option data.
+ *
+ * ARGUMENTS
+ *  const uint8_t *ext
+ *   *ext is a pointer to a DHCP Classless Route Option data.
+ *   For example, if *ext is {16, 192, 168, 192, 168, 42, 1},
+ *   this function returns a pointer to
+ *   {
+ *     subnet = 192.168.0.0;
+ *     netmask_width = 16;
+ *     gateway = 192.168.42.1;
+ *     next = NULL;
+ *   }
+ *
+ *  int16_t ext_size
+ *   ext_size is the memory size of *ext. For example,
+ *   if *ext is {16, 192, 168, 192, 168, 42, 1}, ext_size must be 7.
+ *
+ * RETURN VALUE
+ *  if OK, a pointer to a decoded struct route malloc-ed
+ *  else , NULL
+ *
+ * SEE ALSO RFC3442
+ */
+struct route *bootp_ext121_decode(const uint8_t *ext, int16_t ext_size)
+{
+	int16_t index = 0;
+	uint8_t netmask_width;
+	uint8_t significant_octets;
+	struct route *routes = NULL;
+	struct route *prev_route = NULL;
+
+	while (index < ext_size) {
+		netmask_width = ext[index];
+		index++;
+		if (netmask_width > 32) {
+			printf("IP-Config: Given Classless Route Option subnet mask width '%u' "
+		            "exceeds IPv4 limit of 32. Ignoring remaining option.\n",
+			        netmask_width);
+			return routes;
+		}
+		significant_octets = netmask_width / 8 + (netmask_width % 8 > 0);
+		if (ext_size - index < significant_octets + 4) {
+			printf("IP-Config: Given Classless Route Option remaining lengths (%u octets) "
+			        "is shorter than the expected %u octets. Ignoring remaining options.\n",
+			        ext_size - index, significant_octets + 4);
+			return routes;
+		}
+
+		struct route *route = malloc(sizeof(struct route));
+		if (route == NULL)
+			return routes;
+
+		/* convert only significant octets from byte array into integer in network byte order */
+		route->subnet = 0;
+		memcpy(&route->subnet, &ext[index], significant_octets);
+		index += significant_octets;
+		/* RFC3442 demands: After deriving a subnet number and subnet mask from
+		   each destination descriptor, the DHCP client MUST zero any bits in
+		   the subnet number where the corresponding bit in the mask is zero. */
+		route->subnet &= netdev_genmask(netmask_width);
+
+		/* convert octet array into network byte order */
+		memcpy(&route->gateway, &ext[index], 4);
+		index += 4;
+
+		route->netmask_width = netmask_width;
+		route->next = NULL;
+
+		if (prev_route == NULL) {
+			routes = route;
+		} else {
+			prev_route->next = route;
+		}
+		prev_route = route;
+	}
+	return routes;
+}
+
 /*
  * Parse a bootp reply packet
  */
@@ -275,6 +356,8 @@ int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr,
 {
 	uint8_t ext119_buf[BOOTP_EXTS_SIZE];
 	int16_t ext119_len = 0;
+	uint8_t ext121_buf[BOOTP_EXTS_SIZE];
+	int16_t ext121_len = 0;
 
 	dev->bootp.gateway	= hdr->giaddr;
 	dev->ip_addr		= hdr->yiaddr;
@@ -367,6 +450,16 @@ int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr,
 				} else
 					ext119_len = -1;
 
+				break;
+			case 121:	/* Classless Static Route Option (RFC3442) */
+				if (ext121_len >= 0 &&
+				    ext121_len + len <= sizeof(ext121_buf)) {
+					memcpy(ext121_buf + ext121_len,
+					       ext, len);
+					ext121_len += len;
+				} else
+					ext121_len = -1;
+
 				break;
 			}
 
@@ -385,6 +478,22 @@ int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr,
 		}
 	}
 
+	if (ext121_len > 0) {
+		struct route *ret;
+
+		ret = bootp_ext121_decode(ext121_buf, ext121_len);
+		if (ret != NULL) {
+			struct route *cur = dev->routes;
+			struct route *next;
+			while (cur != NULL) {
+				next = cur->next;
+				free(cur);
+				cur = next;
+			}
+			dev->routes = ret;
+		}
+	}
+
 	/*
 	 * Got packet.
 	 */
diff --git a/usr/kinit/ipconfig/dhcp_proto.c b/usr/kinit/ipconfig/dhcp_proto.c
index d5b759b..4e560b8 100644
--- a/usr/kinit/ipconfig/dhcp_proto.c
+++ b/usr/kinit/ipconfig/dhcp_proto.c
@@ -26,6 +26,7 @@ static uint8_t dhcp_params[] = {
 	28,			/* broadcast addr */
 	40,			/* NIS domain name (why?) */
 	119,			/* Domain Search Option */
+	121,			/* Classless Static Route Option (RFC3442) */
 };
 
 static uint8_t dhcp_discover_hdr[] = {
diff --git a/usr/kinit/ipconfig/main.c b/usr/kinit/ipconfig/main.c
index c65fed5..a454022 100644
--- a/usr/kinit/ipconfig/main.c
+++ b/usr/kinit/ipconfig/main.c
@@ -71,6 +71,8 @@ static inline const char *my_inet_ntoa(uint32_t addr)
 
 static void print_device_config(struct netdev *dev)
 {
+	int dns0_spaces;
+	int dns1_spaces;
 	printf("IP-Config: %s complete", dev->name);
 	if (dev->proto == PROTO_BOOTP || dev->proto == PROTO_DHCP)
 		printf(" (%s from %s)", protoinfos[dev->proto].name,
@@ -80,9 +82,27 @@ static void print_device_config(struct netdev *dev)
 	printf(":\n address: %-16s ", my_inet_ntoa(dev->ip_addr));
 	printf("broadcast: %-16s ", my_inet_ntoa(dev->ip_broadcast));
 	printf("netmask: %-16s\n", my_inet_ntoa(dev->ip_netmask));
-	printf(" gateway: %-16s ", my_inet_ntoa(dev->ip_gateway));
-	printf("dns0     : %-16s ", my_inet_ntoa(dev->ip_nameserver[0]));
-	printf("dns1   : %-16s\n", my_inet_ntoa(dev->ip_nameserver[1]));
+	if (dev->routes != NULL) {
+		struct route *cur;
+		char *delim = "";
+		printf(" routes :");
+		for (cur = dev->routes; cur != NULL; cur = cur->next) {
+			printf("%s %s/%u", delim, my_inet_ntoa(cur->subnet), cur->netmask_width);
+			if (cur->gateway != 0) {
+				printf(" via %s", my_inet_ntoa(cur->gateway));
+			}
+			delim = ",";
+		}
+		printf("\n");
+		dns0_spaces = 3;
+		dns1_spaces = 5;
+	} else {
+		printf(" gateway: %-16s", my_inet_ntoa(dev->ip_gateway));
+		dns0_spaces = 5;
+		dns1_spaces = 3;
+	}
+	printf(" dns0%*c: %-16s", dns0_spaces, ' ', my_inet_ntoa(dev->ip_nameserver[0]));
+	printf(" dns1%*c: %-16s\n", dns1_spaces, ' ', my_inet_ntoa(dev->ip_nameserver[1]));
 	if (dev->hostname[0])
 		printf(" host   : %-64s\n", dev->hostname);
 	if (dev->dnsdomainname[0])
@@ -106,8 +126,8 @@ static void configure_device(struct netdev *dev)
 	if (netdev_setaddress(dev))
 		printf("IP-Config: failed to set addresses on %s\n",
 		       dev->name);
-	if (netdev_setdefaultroute(dev))
-		printf("IP-Config: failed to set default route on %s\n",
+	if (netdev_setroutes(dev))
+		printf("IP-Config: failed to set routes on %s\n",
 		       dev->name);
 	if (dev->hostname[0] &&
 			sethostname(dev->hostname, strlen(dev->hostname)))
@@ -161,8 +181,24 @@ static void dump_device_config(struct netdev *dev)
 				my_inet_ntoa(dev->ip_broadcast));
 		write_option(f, "IPV4NETMASK",
 				my_inet_ntoa(dev->ip_netmask));
-		write_option(f, "IPV4GATEWAY",
-				my_inet_ntoa(dev->ip_gateway));
+		if (dev->routes != NULL) {
+			/* Use 6 digits to encode the index */
+			char key[23];
+			char value[19];
+			int i = 0;
+			struct route *cur;
+			for (cur = dev->routes; cur != NULL; cur = cur->next) {
+				snprintf(key, sizeof(key), "IPV4ROUTE%iSUBNET", i);
+				snprintf(value, sizeof(value), "%s/%u", my_inet_ntoa(cur->subnet), cur->netmask_width);
+				write_option(f, key, value);
+				snprintf(key, sizeof(key), "IPV4ROUTE%iGATEWAY", i);
+				write_option(f, key, my_inet_ntoa(cur->gateway));
+				i++;
+			}
+		} else {
+			write_option(f, "IPV4GATEWAY",
+					my_inet_ntoa(dev->ip_gateway));
+		}
 		write_option(f, "IPV4DNS0",
 				my_inet_ntoa(dev->ip_nameserver[0]));
 		write_option(f, "IPV4DNS1",
@@ -546,7 +582,7 @@ bail:
 
 static int add_all_devices(struct netdev *template);
 
-static int parse_device(struct netdev *dev, const char *ip)
+static int parse_device(struct netdev *dev, char *ip)
 {
 	char *cp;
 	int opt;
@@ -659,7 +695,7 @@ static void bringup_one_dev(struct netdev *template, struct netdev *dev)
 	bringup_device(dev);
 }
 
-static struct netdev *add_device(const char *info)
+static struct netdev *add_device(char *info)
 {
 	struct netdev *dev;
 	int i;
diff --git a/usr/kinit/ipconfig/netdev.c b/usr/kinit/ipconfig/netdev.c
index e203d0c..de87f96 100644
--- a/usr/kinit/ipconfig/netdev.c
+++ b/usr/kinit/ipconfig/netdev.c
@@ -88,23 +88,44 @@ static void set_s_addr(struct sockaddr *saddr, uint32_t ipaddr)
 	memcpy(saddr, &sin, sizeof sin);
 }
 
-int netdev_setdefaultroute(struct netdev *dev)
+int netdev_setroutes(struct netdev *dev)
 {
 	struct rtentry r;
 
-	if (dev->ip_gateway == INADDR_ANY)
-		return 0;
-
-	memset(&r, 0, sizeof(r));
-
-	set_s_addr(&r.rt_dst, INADDR_ANY);
-	set_s_addr(&r.rt_gateway, dev->ip_gateway);
-	set_s_addr(&r.rt_genmask, INADDR_ANY);
-	r.rt_flags = RTF_UP | RTF_GATEWAY;
-
-	if (ioctl(cfd, SIOCADDRT, &r) == -1 && errno != EEXIST) {
-		perror("SIOCADDRT");
-		return -1;
+	/* RFC3442 demands:
+	   If the DHCP server returns both a Classless Static Routes option and
+	   a Router option, the DHCP client MUST ignore the Router option. */
+	if (dev->routes != NULL) {
+		struct route *cur;
+		for (cur = dev->routes; cur != NULL; cur = cur->next) {
+			memset(&r, 0, sizeof(r));
+
+			r.rt_dev = dev->name;
+			set_s_addr(&r.rt_dst, cur->subnet);
+			set_s_addr(&r.rt_gateway, cur->gateway);
+			set_s_addr(&r.rt_genmask, netdev_genmask(cur->netmask_width));
+			r.rt_flags = RTF_UP;
+			if (cur->gateway != 0) {
+				r.rt_flags |= RTF_GATEWAY;
+			}
+
+			if (ioctl(cfd, SIOCADDRT, &r) == -1 && errno != EEXIST) {
+				perror("SIOCADDRT");
+				return -1;
+			}
+		}
+	} else if (dev->ip_gateway != INADDR_ANY) {
+		memset(&r, 0, sizeof(r));
+
+		set_s_addr(&r.rt_dst, INADDR_ANY);
+		set_s_addr(&r.rt_gateway, dev->ip_gateway);
+		set_s_addr(&r.rt_genmask, INADDR_ANY);
+		r.rt_flags = RTF_UP | RTF_GATEWAY;
+
+		if (ioctl(cfd, SIOCADDRT, &r) == -1 && errno != EEXIST) {
+			perror("SIOCADDRT");
+			return -1;
+		}
 	}
 	return 0;
 }
diff --git a/usr/kinit/ipconfig/netdev.h b/usr/kinit/ipconfig/netdev.h
index 4b75a65..dbc80cd 100644
--- a/usr/kinit/ipconfig/netdev.h
+++ b/usr/kinit/ipconfig/netdev.h
@@ -1,14 +1,22 @@
 #ifndef IPCONFIG_NETDEV_H
 #define IPCONFIG_NETDEV_H
 
+#include <arpa/inet.h>
 #include <sys/utsname.h>
 #include <net/if.h>
 
 #define BPLEN		256
 #define FNLEN		128			/* from DHCP  RFC 2131 */
 
+struct route {
+	uint32_t subnet;			/* subnet            */
+	uint32_t netmask_width;	/* subnet mask width */
+	uint32_t gateway;		/* gateway           */
+	struct route *next;
+};
+
 struct netdev {
-	const char *name;	/* Device name          */
+	char *name;		/* Device name          */
 	unsigned int ifindex;	/* interface index      */
 	unsigned int hwtype;	/* ARPHRD_xxx           */
 	unsigned int hwlen;	/* HW address length    */
@@ -44,6 +52,7 @@ struct netdev {
 	char bootpath[BPLEN];	/* boot path            */
 	char filename[FNLEN];   /* filename             */
 	char *domainsearch;	/* decoded, NULL or malloc-ed  */
+	struct route *routes;	/* decoded, NULL or malloc-ed list */
 	long uptime;		/* when complete configuration */
 	int pkt_fd;		/* packet socket for this interface */
 	struct netdev *next;	/* next configured i/f  */
@@ -70,7 +79,7 @@ extern struct netdev *ifaces;
 
 int netdev_getflags(struct netdev *dev, short *flags);
 int netdev_setaddress(struct netdev *dev);
-int netdev_setdefaultroute(struct netdev *dev);
+int netdev_setroutes(struct netdev *dev);
 int netdev_up(struct netdev *dev);
 int netdev_down(struct netdev *dev);
 int netdev_init_if(struct netdev *dev);
@@ -84,4 +93,15 @@ static inline int netdev_running(struct netdev *dev)
 	return ret ? 0 : !!(flags & IFF_RUNNING);
 }
 
+static inline uint32_t netdev_genmask(uint32_t netmask_width)
+{
+	/* Map netmask width to network mask in network byte order.
+	   Example: 24 -> "255.255.255.0" -> htonl(0xFFFFFF00) */
+	if (netmask_width == 0) {
+		return 0;
+	} else {
+		return htonl(~((1u << (32 - netmask_width)) - 1));
+	}
+}
+
 #endif /* IPCONFIG_NETDEV_H */


More information about the klibc mailing list