]> git.proxmox.com Git - mirror_lxc.git/commitdiff
add autodetection of the gateway address
authorMatthijs Kooijman <matthijs@stdin.nl>
Tue, 30 Aug 2011 21:50:23 +0000 (23:50 +0200)
committerDaniel Lezcano <dlezcano@fr.ibm.com>
Tue, 30 Aug 2011 21:50:23 +0000 (23:50 +0200)
For veth and macvlan networks, this can look up the host address on the
bridge (link) interface and add a default route on the guest to that
address. This facilitates a typical setup where guests are bridged
together.

syntax:
lxc.ipv4.gateway = auto
lxc.ipv6.gateway = auto

Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com>
src/lxc/conf.c
src/lxc/conf.h
src/lxc/confile.c
src/lxc/network.c
src/lxc/network.h
src/lxc/start.c

index 751dce04d56900da137021adf49d2a840fcfbcc5..613e476d2f1f85b2a926ab8b1b571326315ecaf4 100644 (file)
@@ -1342,6 +1342,11 @@ static int setup_netdev(struct lxc_netdev *netdev)
                if (err) {
                        ERROR("failed to setup ipv4 gateway for '%s': %s",
                                      ifname, strerror(-err));
+                       if (netdev->ipv4_gateway_auto) {
+                               char buf[INET_ADDRSTRLEN];
+                               inet_ntop(AF_INET, netdev->ipv4_gateway, buf, sizeof(buf));
+                               ERROR("tried to set autodetected ipv4 gateway '%s'", buf);
+                       }
                        return -1;
                }
        }
@@ -1362,6 +1367,11 @@ static int setup_netdev(struct lxc_netdev *netdev)
                if (err) {
                        ERROR("failed to setup ipv6 gateway for '%s': %s",
                                      ifname, strerror(-err));
+                       if (netdev->ipv6_gateway_auto) {
+                               char buf[INET6_ADDRSTRLEN];
+                               inet_ntop(AF_INET, netdev->ipv6_gateway, buf, sizeof(buf));
+                               ERROR("tried to set autodetected ipv6 gateway '%s'", buf);
+                       }
                        return -1;
                }
        }
@@ -1684,6 +1694,54 @@ int lxc_assign_network(struct lxc_list *network, pid_t pid)
        return 0;
 }
 
+int lxc_find_gateway_addresses(struct lxc_handler *handler)
+{
+       struct lxc_list *network = &handler->conf->network;
+       struct lxc_list *iterator;
+       struct lxc_netdev *netdev;
+       int link_index;
+
+       lxc_list_for_each(iterator, network) {
+               netdev = iterator->elem;
+
+               if (!netdev->ipv4_gateway_auto && !netdev->ipv6_gateway_auto)
+                       continue;
+
+               if (netdev->type != LXC_NET_VETH && netdev->type != LXC_NET_MACVLAN) {
+                       ERROR("gateway = auto only supported for "
+                             "veth and macvlan");
+                       return -1;
+               }
+
+               if (!netdev->link) {
+                       ERROR("gateway = auto needs a link interface");
+                       return -1;
+               }
+
+               link_index = if_nametoindex(netdev->link);
+               if (!link_index)
+                       return -EINVAL;
+
+               if (netdev->ipv4_gateway_auto) {
+                       if (lxc_ipv4_addr_get(link_index, &netdev->ipv4_gateway)) {
+                               ERROR("failed to automatically find ipv4 gateway "
+                                     "address from link interface '%s'", netdev->link);
+                               return -1;
+                       }
+               }
+
+               if (netdev->ipv6_gateway_auto) {
+                       if (lxc_ipv6_addr_get(link_index, &netdev->ipv6_gateway)) {
+                               ERROR("failed to automatically find ipv6 gateway "
+                                     "address from link interface '%s'", netdev->link);
+                               return -1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 int lxc_create_tty(const char *name, struct lxc_conf *conf)
 {
        struct lxc_tty_info *tty_info = &conf->tty_info;
index 328d236dec69ff783fab4451101b15a6d07d9424..973f694c7adcd8655c161f07069d23140353fd11 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <netinet/in.h>
 #include <sys/param.h>
+#include <stdbool.h>
 
 #include <lxc/list.h>
 
@@ -115,7 +116,9 @@ struct lxc_netdev {
        struct lxc_list ipv4;
        struct lxc_list ipv6;
        struct in_addr *ipv4_gateway;
+       bool ipv4_gateway_auto;
        struct in6_addr *ipv6_gateway;
+       bool ipv6_gateway_auto;
        char *upscript;
 };
 
@@ -219,6 +222,7 @@ extern struct lxc_conf *lxc_conf_init(void);
 extern int lxc_create_network(struct lxc_handler *handler);
 extern void lxc_delete_network(struct lxc_list *networks);
 extern int lxc_assign_network(struct lxc_list *networks, pid_t pid);
+extern int lxc_find_gateway_addresses(struct lxc_handler *handler);
 
 extern int lxc_create_tty(const char *name, struct lxc_conf *conf);
 extern void lxc_delete_tty(struct lxc_tty_info *tty_info);
index 8e215f80746529629600a12905e3bbcb6f699020..550102cf2664dce57c9ea507189f0c488113bd0f 100644 (file)
@@ -458,12 +458,18 @@ static int config_network_ipv4_gateway(const char *key, char *value,
                return -1;
        }
 
-       if (!inet_pton(AF_INET, value, gw)) {
-               SYSERROR("invalid ipv4 gateway address: %s", value);
-               return -1;
-       }
+       if (!strcmp(value, "auto")) {
+               netdev->ipv4_gateway = NULL;
+               netdev->ipv4_gateway_auto = true;
+       } else {
+               if (!inet_pton(AF_INET, value, gw)) {
+                       SYSERROR("invalid ipv4 gateway address: %s", value);
+                       return -1;
+               }
 
-       netdev->ipv4_gateway = gw;
+               netdev->ipv4_gateway = gw;
+               netdev->ipv4_gateway_auto = false;
+       }
 
        return 0;
 }
@@ -536,12 +542,18 @@ static int config_network_ipv6_gateway(const char *key, char *value,
                return -1;
        }
 
-       if (!inet_pton(AF_INET6, value, gw)) {
-               SYSERROR("invalid ipv6 gateway address: %s", value);
-               return -1;
-       }
+       if (!strcmp(value, "auto")) {
+               netdev->ipv6_gateway = NULL;
+               netdev->ipv6_gateway_auto = true;
+       } else {
+               if (!inet_pton(AF_INET6, value, gw)) {
+                       SYSERROR("invalid ipv6 gateway address: %s", value);
+                       return -1;
+               }
 
-       netdev->ipv6_gateway = gw;
+               netdev->ipv6_gateway = gw;
+               netdev->ipv6_gateway_auto = false;
+       }
 
        return 0;
 }
index b1d60f2bbc2817a11ca3b86f6c2a74ac4bf6f31f..5110ca73bcc812b6563866f943c8c65b7210fb27 100644 (file)
@@ -746,6 +746,163 @@ int lxc_ipv4_addr_add(int ifindex, struct in_addr *addr,
        return ip_addr_add(AF_INET, ifindex, addr, bcast, NULL, prefix);
 }
 
+/* Find an IFA_LOCAL (or IFA_ADDRESS if not IFA_LOCAL is present)
+ * address from the given RTM_NEWADDR message.  Allocates memory for the
+ * address and stores that pointer in *res (so res should be an
+ * in_addr** or in6_addr**).
+ */
+static int ifa_get_local_ip(int family, struct ip_req *ip_info, void** res) {
+       struct rtattr *rta = IFA_RTA(&ip_info->ifa);
+       int attr_len = IFA_PAYLOAD(&ip_info->nlmsg.nlmsghdr);
+       int addrlen;
+
+       if (ip_info->ifa.ifa_family != family)
+               return 0;
+
+       addrlen = family == AF_INET ? sizeof(struct in_addr) :
+               sizeof(struct in6_addr);
+
+       /* Loop over the rtattr's in this message */
+       while(RTA_OK(rta, attr_len)) {
+               /* Found a local address for the requested interface,
+                * return it. */
+               if (rta->rta_type == IFA_LOCAL || rta->rta_type == IFA_ADDRESS) {
+                       /* Sanity check. The family check above should
+                        * make sure the address length is correct, but
+                        * check here just in case */
+                       if (RTA_PAYLOAD(rta) != addrlen)
+                               return -1;
+
+                       /* We might have found an IFA_ADDRESS before,
+                        * which we now overwrite with an IFA_LOCAL. */
+                       if (!*res)
+                               *res = malloc(addrlen);
+
+                       memcpy(*res, RTA_DATA(rta), addrlen);
+
+                       if (rta->rta_type == IFA_LOCAL)
+                               break;
+               }
+               rta = RTA_NEXT(rta, attr_len);
+       }
+       return 0;
+}
+
+static int ip_addr_get(int family, int ifindex, void **res)
+{
+       struct nl_handler nlh;
+       struct nlmsg *nlmsg = NULL, *answer = NULL;
+       struct ip_req *ip_req, *ip_info;
+       struct nlmsghdr *msg;
+       int err;
+       int recv_len = 0, answer_len;
+       int readmore = 0;
+
+       err = netlink_open(&nlh, NETLINK_ROUTE);
+       if (err)
+               return err;
+
+       err = -ENOMEM;
+       nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE);
+       if (!nlmsg)
+               goto out;
+
+       answer = nlmsg_alloc(NLMSG_GOOD_SIZE);
+       if (!answer)
+               goto out;
+
+       /* Save the answer buffer length, since it will be overwritten
+        * on the first receive (and we might need to receive more than
+        * once. */
+       answer_len = answer->nlmsghdr.nlmsg_len;
+
+       ip_req = (struct ip_req *)nlmsg;
+       ip_req->nlmsg.nlmsghdr.nlmsg_len =
+               NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+       ip_req->nlmsg.nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ROOT;
+       ip_req->nlmsg.nlmsghdr.nlmsg_type = RTM_GETADDR;
+       ip_req->ifa.ifa_family = family;
+
+       /* Send the request for addresses, which returns all addresses
+        * on all interfaces. */
+       err = netlink_send(&nlh, nlmsg);
+       if (err < 0)
+               goto out;
+       err = 0;
+
+       do {
+               /* Restore the answer buffer length, it might have been
+                * overwritten by a previous receive. */
+               answer->nlmsghdr.nlmsg_len = answer_len;
+
+               /* Get the (next) batch of reply messages */
+               err = netlink_rcv(&nlh, answer);
+               if (err < 0)
+                       goto out;
+
+               recv_len = err;
+               err = 0;
+
+               /* Satisfy the typing for the netlink macros */
+               msg = &answer->nlmsghdr;
+
+               while (NLMSG_OK(msg, recv_len)) {
+                       /* Stop reading if we see an error message */
+                       if (msg->nlmsg_type == NLMSG_ERROR) {
+                               struct nlmsgerr *errmsg = (struct nlmsgerr*)NLMSG_DATA(msg);
+                               err = errmsg->error;
+                               goto out;
+                       }
+
+                       /* Stop reading if we see a NLMSG_DONE message */
+                       if (msg->nlmsg_type == NLMSG_DONE) {
+                               readmore = 0;
+                               break;
+                       }
+
+                       if (msg->nlmsg_type != RTM_NEWADDR) {
+                               err = -1;
+                               goto out;
+                       }
+
+                       ip_info = (struct ip_req *)msg;
+                       if (ip_info->ifa.ifa_index == ifindex) {
+                               ifa_get_local_ip(family, ip_info, res);
+                               /* Found a result, stop searching */
+                               if (*res)
+                                       goto out;
+                       }
+
+                       /* Keep reading more data from the socket if the
+                        * last message had the NLF_F_MULTI flag set */
+                       readmore = (msg->nlmsg_flags & NLM_F_MULTI);
+
+                       /* Look at the next message received in this buffer */
+                       msg = NLMSG_NEXT(msg, recv_len);
+               }
+       } while (readmore);
+
+       /* If we end up here, we didn't find any result, so signal an
+        * error */
+       err = -1;
+
+out:
+       netlink_close(&nlh);
+       nlmsg_free(answer);
+       nlmsg_free(nlmsg);
+       return err;
+}
+
+int lxc_ipv6_addr_get(int ifindex, struct in6_addr **res)
+{
+       return ip_addr_get(AF_INET6, ifindex, (void**)res);
+}
+
+int lxc_ipv4_addr_get(int ifindex, struct in_addr** res)
+{
+       return ip_addr_get(AF_INET, ifindex, (void**)res);
+}
+
 static int ip_gateway_add(int family, int ifindex, void *gw)
 {
        struct nl_handler nlh;
index 5a1fd50ca71a9a76c575a4d89e0adb026ecda7db..0fd5e5d36a9f661874f9597648708362452dc310 100644 (file)
@@ -83,6 +83,12 @@ extern int lxc_ipv6_addr_add(int ifindex, struct in6_addr *addr,
 extern int lxc_ipv4_addr_add(int ifindex, struct in_addr *addr,
                             struct in_addr *bcast, int prefix);
 
+/*
+ * Get ip address
+ */
+extern int lxc_ipv4_addr_get(int ifindex, struct in_addr **res);
+extern int lxc_ipv6_addr_get(int ifindex, struct in6_addr **res);
+
 /*
  * Set default route.
  */
index b8ceff6bd38451f3a0414edde8e37cbf485537eb..6737a44d21df220ebefffa7cd684f541fbd1f2dd 100644 (file)
@@ -483,6 +483,16 @@ int lxc_spawn(struct lxc_handler *handler)
 
                clone_flags |= CLONE_NEWNET;
 
+               /* Find gateway addresses from the link device, which is
+                * no longer accessible inside the container. Do this
+                * before creating network interfaces, since goto
+                * out_delete_net does not work before lxc_clone. */
+               if (lxc_find_gateway_addresses(handler)) {
+                       ERROR("failed to find gateway addresses");
+                       lxc_sync_fini(handler);
+                       return -1;
+               }
+
                /* that should be done before the clone because we will
                 * fill the netdev index and use them in the child
                 */