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;
}
}
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;
}
}
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;
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;
}
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;
}
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;