#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>
+#include <time.h>
#include "parse.h"
#include "config.h"
}
}
-bool clone_update_unexp_hooks(struct lxc_conf *c)
+bool clone_update_unexp_hooks(struct lxc_conf *conf, const char *oldpath,
+ const char *newpath, const char *oldname, const char *newname)
{
- struct lxc_list *it;
- int i;
-
- clear_unexp_config_line(c, "lxc.hook", true);
- for (i=0; i<NUM_LXC_HOOKS; i++) {
- lxc_list_for_each(it, &c->hooks[i]) {
- if (!do_append_unexp_config_line(c, lxchook_names[i], (char *)it->elem))
+ const char *key = "lxc.hook";
+ int ret;
+ char *lstart = conf->unexpanded_config, *lend, *p;
+ size_t newdirlen = strlen(newpath) + strlen(newname) + 1;
+ size_t olddirlen = strlen(oldpath) + strlen(oldname) + 1;
+ char *olddir = alloca(olddirlen + 1);
+ char *newdir = alloca(newdirlen + 1);
+
+ ret = snprintf(olddir, olddirlen+1, "%s/%s", oldpath, oldname);
+ if (ret < 0 || ret >= olddirlen+1) {
+ ERROR("Bug in %s", __func__);
+ return false;
+ }
+ ret = snprintf(newdir, newdirlen+1, "%s/%s", newpath, newname);
+ if (ret < 0 || ret >= newdirlen+1) {
+ ERROR("Bug in %s", __func__);
+ return false;
+ }
+ if (!conf->unexpanded_config)
+ return true;
+ while (*lstart) {
+ lend = strchr(lstart, '\n');
+ if (!lend)
+ lend = lstart + strlen(lstart);
+ else
+ lend++;
+ if (strncmp(lstart, key, strlen(key)) != 0) {
+ lstart = lend;
+ continue;
+ }
+ p = strchr(lstart+strlen(key), '=');
+ if (!p) {
+ lstart = lend;
+ continue;
+ }
+ p++;
+ while (isblank(*p))
+ p++;
+ if (!*p)
+ return true;
+ if (strncmp(p, olddir, strlen(olddir)) != 0) {
+ lstart = lend;
+ continue;
+ }
+ /* replace the olddir with newdir */
+ if (olddirlen >= newdirlen) {
+ size_t diff = olddirlen - newdirlen;
+ memcpy(p, newdir, newdirlen);
+ if (olddirlen != newdirlen) {
+ memmove(lend-diff, lend, strlen(lend)+1);
+ lend -= diff;
+ conf->unexpanded_len -= diff;
+ }
+ lstart = lend;
+ } else {
+ char *new;
+ size_t diff = newdirlen - olddirlen;
+ size_t oldlen = conf->unexpanded_len;
+ size_t newlen = oldlen + diff;
+ size_t poffset = p - conf->unexpanded_config;
+ new = realloc(conf->unexpanded_config, newlen);
+ if (!new) {
+ ERROR("Out of memory");
return false;
+ }
+ conf->unexpanded_len = newlen;
+ new[newlen-1] = '\0';
+ lend = new + (lend - conf->unexpanded_config);
+ /* move over the remainder, /$hookname\n$rest */
+ memmove(new+poffset+newdirlen,
+ new+poffset+olddirlen,
+ oldlen-poffset-olddirlen);
+ conf->unexpanded_config = new;
+ memcpy(new+poffset, newdir, newdirlen);
+ lstart = lend + diff;
}
}
return true;
} \
}
-bool clone_update_unexp_network(struct lxc_conf *c)
+static void new_hwaddr(char *hwaddr)
+{
+ FILE *f;
+ f = fopen("/dev/urandom", "r");
+ if (f) {
+ unsigned int seed;
+ int ret = fread(&seed, sizeof(seed), 1, f);
+ if (ret != 1)
+ seed = time(NULL);
+ fclose(f);
+ srand(seed);
+ } else
+ srand(time(NULL));
+ snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x",
+ rand() % 255, rand() % 255, rand() % 255);
+}
+
+/*
+ * This is called only from clone.
+ * We wish to update all hwaddrs in the unexpanded config file. We
+ * can't/don't want to update any which come from lxc.includes (there
+ * shouldn't be any).
+ * We can't just walk the c->lxc-conf->network list because that includes
+ * netifs from the include files. So we update the ones which we find in
+ * the unexp config file, then find the original macaddr in the
+ * conf->network, and update that to the same value.
+ */
+bool network_new_hwaddrs(struct lxc_conf *conf)
{
struct lxc_list *it;
- clear_unexp_config_line(c, "lxc.network", true);
+ const char *key = "lxc.network.hwaddr";
+ char *lstart = conf->unexpanded_config, *lend, *p, *p2;
- lxc_list_for_each(it, &c->network) {
- struct lxc_netdev *n = it->elem;
- const char *t = lxc_net_type_to_str(n->type);
- struct lxc_list *it2;
- DO(do_append_unexp_config_line(c, "lxc.network.type", t ? t : "(invalid)"));
- if (n->flags & IFF_UP)
- DO(do_append_unexp_config_line(c, "lxc.network.flags", "up"));
- if (n->link)
- DO(do_append_unexp_config_line(c, "lxc.network.link", n->link));
- if (n->name)
- DO(do_append_unexp_config_line(c, "lxc.network.name", n->name));
- if (n->type == LXC_NET_MACVLAN) {
- const char *mode;
- switch (n->priv.macvlan_attr.mode) {
- case MACVLAN_MODE_PRIVATE: mode = "private"; break;
- case MACVLAN_MODE_VEPA: mode = "vepa"; break;
- case MACVLAN_MODE_BRIDGE: mode = "bridge"; break;
- default: mode = "(invalid)"; break;
- }
- DO(do_append_unexp_config_line(c, "lxc.network.macvlan.mode", mode));
- } else if (n->type == LXC_NET_VETH) {
- if (n->priv.veth_attr.pair)
- DO(do_append_unexp_config_line(c, "lxc.network.veth.pair", n->priv.veth_attr.pair));
- } else if (n->type == LXC_NET_VLAN) {
- char vid[20];
- sprintf(vid, "%d", n->priv.vlan_attr.vid);
- DO(do_append_unexp_config_line(c, "lxc.network.vlan.id", vid));
- }
- if (n->upscript)
- DO(do_append_unexp_config_line(c, "lxc.network.script.up", n->upscript));
- if (n->downscript)
- DO(do_append_unexp_config_line(c, "lxc.network.script.down", n->downscript));
- if (n->hwaddr)
- DO(do_append_unexp_config_line(c, "lxc.network.hwaddr", n->hwaddr));
- if (n->mtu)
- DO(do_append_unexp_config_line(c, "lxc.network.mtu", n->mtu));
- if (n->ipv4_gateway_auto) {
- DO(do_append_unexp_config_line(c, "lxc.network.ipv4.gateway", "auto"));
- } else if (n->ipv4_gateway) {
- char buf[INET_ADDRSTRLEN];
- inet_ntop(AF_INET, n->ipv4_gateway, buf, sizeof(buf));
- DO(do_append_unexp_config_line(c, "lxc.network.ipv4.gateway", buf));
+ if (!conf->unexpanded_config)
+ return true;
+ while (*lstart) {
+ char newhwaddr[18], oldhwaddr[17];
+ lend = strchr(lstart, '\n');
+ if (!lend)
+ lend = lstart + strlen(lstart);
+ else
+ lend++;
+ if (strncmp(lstart, key, strlen(key)) != 0) {
+ lstart = lend;
+ continue;
}
- lxc_list_for_each(it2, &n->ipv4) {
- struct lxc_inetdev *i = it2->elem;
- char buf[2*INET_ADDRSTRLEN+20], buf2[INET_ADDRSTRLEN], prefix[20];
- inet_ntop(AF_INET, &i->addr, buf, INET_ADDRSTRLEN);
-
- if (i->prefix) {
- sprintf(prefix, "/%d", i->prefix);
- strcat(buf, prefix);
- }
-
- if (i->bcast.s_addr != (i->addr.s_addr |
- htonl(INADDR_BROADCAST >> i->prefix))) {
-
- inet_ntop(AF_INET, &i->bcast, buf2, sizeof(buf2));
- strcat(buf, " ");
- strcat(buf, buf2);
- }
- DO(do_append_unexp_config_line(c, "lxc.network.ipv4", buf));
+ p = strchr(lstart+strlen(key), '=');
+ if (!p) {
+ lstart = lend;
+ continue;
}
- if (n->ipv6_gateway_auto) {
- DO(do_append_unexp_config_line(c, "lxc.network.ipv6.gateway", "auto"));
- } else if (n->ipv6_gateway) {
- char buf[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET6, n->ipv6_gateway, buf, sizeof(buf));
- DO(do_append_unexp_config_line(c, "lxc.network.ipv6.gateway", buf));
+ p++;
+ while (isblank(*p))
+ p++;
+ if (!*p)
+ return true;
+ p2 = p;
+ while (*p2 && !isblank(*p2) && *p2 != '\n')
+ p2++;
+ if (p2-p != 17) {
+ WARN("Bad hwaddr entry");
+ lstart = lend;
+ continue;
}
- lxc_list_for_each(it2, &n->ipv6) {
- struct lxc_inet6dev *i = it2->elem;
- char buf[INET6_ADDRSTRLEN + 20], prefix[20];
- inet_ntop(AF_INET6, &i->addr, buf, INET6_ADDRSTRLEN);
- if (i->prefix) {
- sprintf(prefix, "/%d", i->prefix);
- strcat(buf, prefix);
- }
- DO(do_append_unexp_config_line(c, "lxc.network.ipv6", buf));
+ memcpy(oldhwaddr, p, 17);
+ new_hwaddr(newhwaddr);
+ memcpy(p, newhwaddr, 17);
+ lxc_list_for_each(it, &conf->network) {
+ struct lxc_netdev *n = it->elem;
+ if (n->hwaddr && memcmp(oldhwaddr, n->hwaddr, 17) == 0)
+ memcpy(n->hwaddr, newhwaddr, 17);
}
+
+ lstart = lend;
}
- lxc_list_for_each(it, &c->environment)
- DO(do_append_unexp_config_line(c, "lxc.environment", (char *)it->elem));
return true;
}
--- /dev/null
+#!/bin/bash
+
+# lxc: linux Container library
+
+# Authors:
+# Serge Hallyn <serge.hallyn@ubuntu.com>
+#
+# This is a test script for the lxc-user-nic program
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+set -e
+
+s=`mktemp -d /tmp/lxctest-XXXXXX`
+
+DONE=0
+
+verify_unchanged_number() {
+ key=$1
+ desc=$2
+ n1=`grep ^$key $CONTAINER_PATH/config | wc -l`
+ n2=`grep ^$key $CONTAINER2_PATH/config | wc -l`
+ if [ $n1 -ne $n2 ]; then
+ echo "Test $testnum failed"
+ echo "unequal number of $desc"
+ echo "Original has $n1, clone has $n2"
+ false
+ fi
+}
+
+cleanup() {
+ lxc-destroy -n lxctestb || true
+ lxc-destroy -n lxctestb2 || true
+ rm -rf $s
+ [ $DONE -eq 1 ] && echo "PASS" || echo "FAIL"
+}
+
+verify_numnics() {
+ verify_unchanged_number lxc.network.type "network defs"
+}
+
+verify_hwaddr() {
+ verify_unchanged_number lxc.network.hwaddr "hwaddr defs"
+ grep ^lxc.network.hwaddr $CONTAINER_PATH/config | while read line; do
+ addr=`echo $line | awk -F= { print $2 }`
+ echo "looking for $addr in $CONTAINER2_PATH/config"
+ if grep -q $addr $CONTAINER2_PATH/config; then
+ echo "Test $testnum failed"
+ echo "hwaddr $addr was not changed"
+ false
+ fi
+ done
+}
+
+verify_hooks() {
+ verify_unchanged_number lxc.hook "hooks"
+ grep ^lxc.hook $CONTAINER_PATH/config | while read line; do
+ nline=${line/$CONTAINER_PATH/$CONTAINER2_PATH}
+ if ! grep -q "$nline" $CONTAINER2_PATH/config; then
+ echo "Test $testnum failed"
+ echo "Failed to find $nline in:"
+ cat $CONTAINER2_PATH/config
+ fi
+ done
+}
+
+trap cleanup EXIT
+
+# Simple nic
+cat > $s/1.conf << EOF
+lxc.network.type = veth
+lxc.network.link = lxcbr0
+EOF
+
+# Simple nic with hwaddr; verify hwaddr changed
+cat > $s/2.conf << EOF
+lxc.network.type = veth
+lxc.network.link = lxcbr0
+EOF
+
+# No nics, but nic from include
+cat > $s/1.include << EOF
+lxc.network.type = veth
+lxc.network.link = lxcbr0
+lxc.hook.start = /bin/bash
+EOF
+cat > $s/3.conf << EOF
+lxc.include = $s/1.include
+EOF
+
+# No nics, one clone hook in /bin
+cat > $s/4.conf << EOF
+lxc.hook.start = /bin/bash
+EOF
+
+# Figure out container dirname
+# We need this in 5.conf
+lxc-destroy -n lxctestb || true
+lxc-create -t busybox -n lxctestb
+CONTAINER_PATH=$(dirname $(lxc-info -n lxctestb -c lxc.rootfs -H))
+lxc-destroy -n lxctestb
+
+# No nics, one clone hook in $container
+cat > $s/5.conf << EOF
+lxc.hook.start = $CONTAINER_PATH/x1
+EOF
+
+CONTAINER2_PATH="${CONTAINER_PATH}2"
+
+testnum=1
+for f in $s/*.conf; do
+ echo "Test $testnum starting ($f)"
+ lxc-create -t busybox -f $f -n lxctestb
+ touch $CONTAINER_PATH/x1
+ lxc-clone -s -o lxctestb -n lxctestb2
+ # verify that # nics did not change
+ verify_numnics
+ # verify hwaddr, if any changed
+ verify_hwaddr
+ # verify hooks are correct
+ verify_hooks
+ lxc-destroy -n lxctestb2 || true
+ lxc-destroy -n lxctestb || true
+ testnum=$((testnum+1))
+done
+
+DONE=1