aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Drung <benjamin.drung@profitbricks.com>2017-12-13 23:04:29 +0100
committerBen Hutchings <ben@decadent.org.uk>2019-01-02 03:08:04 +0000
commitee59de58cd3ebe9e98d19aeaadb39915b0b235fc (patch)
treea82773fd1aaaec3e0b93f96bd5b0efdbbd691039
parent8b15382a33823d38599347e90022abfcdc70fc68 (diff)
downloadklibc-ee59de58cd3ebe9e98d19aeaadb39915b0b235fc.tar.gz
[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@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@decadent.org.uk>
-rw-r--r--usr/kinit/ipconfig/bootp_proto.c109
-rw-r--r--usr/kinit/ipconfig/dhcp_proto.c1
-rw-r--r--usr/kinit/ipconfig/main.c54
-rw-r--r--usr/kinit/ipconfig/netdev.c49
-rw-r--r--usr/kinit/ipconfig/netdev.h24
5 files changed, 212 insertions, 25 deletions
diff --git a/usr/kinit/ipconfig/bootp_proto.c b/usr/kinit/ipconfig/bootp_proto.c
index 150ebfa7952b81..f6f9dd43bc904e 100644
--- a/usr/kinit/ipconfig/bootp_proto.c
+++ b/usr/kinit/ipconfig/bootp_proto.c
@@ -268,6 +268,87 @@ static char *bootp_ext119_decode(const void *ext, int16_t ext_size, void *tmp)
}
/*
+ * 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
*/
int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr,
@@ -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;
@@ -368,6 +451,16 @@ int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr,
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;
}
ext += len;
@@ -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 d5b759bbc16ba8..4e560b84c1c9a2 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 c65fed5fb98e8b..a454022e102517 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 e203d0c69f95c3..de87f960285d96 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 4b75a65ad06702..dbc80cd09d116b 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 */