openvswitch: delete ports intelligently

So far, when creating veth devices attached to openvswitch bridges we used to fork() off a thread on container startup. This thread was kept around until the container shut down. I have no good explanation why we did it that why but it's certainly not necessary. Instead, let's fork() off the thread on container shutdown to delete the veth. Signed-off-by: 's avatarChristian Brauner <christian.brauner@ubuntu.com>
parent 1efc1065
...@@ -2859,7 +2859,7 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd ...@@ -2859,7 +2859,7 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
} }
if (netdev->link) { if (netdev->link) {
err = lxc_bridge_attach(handler->lxcpath, handler->name, netdev->link, veth1); err = lxc_bridge_attach(netdev->link, veth1);
if (err) { if (err) {
ERROR("failed to attach \"%s\" to bridge \"%s\": %s", ERROR("failed to attach \"%s\" to bridge \"%s\": %s",
veth1, netdev->link, strerror(-err)); veth1, netdev->link, strerror(-err));
...@@ -3239,11 +3239,20 @@ bool lxc_delete_network(struct lxc_handler *handler) ...@@ -3239,11 +3239,20 @@ bool lxc_delete_network(struct lxc_handler *handler)
char *hostveth; char *hostveth;
if (netdev->priv.veth_attr.pair) { if (netdev->priv.veth_attr.pair) {
hostveth = netdev->priv.veth_attr.pair; hostveth = netdev->priv.veth_attr.pair;
ret = lxc_netdev_delete_by_name(hostveth); ret = lxc_netdev_delete_by_name(hostveth);
if (ret < 0) if (ret < 0)
WARN("Failed to remove interface \"%s\" from host: %s.", hostveth, strerror(-ret)); WARN("Failed to remove interface \"%s\" from host: %s.", hostveth, strerror(-ret));
else else
INFO("Removed interface \"%s\" from host.", hostveth); INFO("Removed interface \"%s\" from host.", hostveth);
if (is_ovs_bridge(netdev->link)) {
ret = lxc_ovs_delete_port(netdev->link, hostveth);
if (ret < 0)
WARN("Failed to remove port \"%s\" from openvswitch bridge \"%s\"", hostveth, netdev->link);
else
INFO("Removed port \"%s\" from openvswitch bridge \"%s\"", hostveth, netdev->link);
}
} else if (strlen(netdev->priv.veth_attr.veth1) > 0) { } else if (strlen(netdev->priv.veth_attr.veth1) > 0) {
hostveth = netdev->priv.veth_attr.veth1; hostveth = netdev->priv.veth_attr.veth1;
ret = lxc_netdev_delete_by_name(hostveth); ret = lxc_netdev_delete_by_name(hostveth);
...@@ -3251,7 +3260,16 @@ bool lxc_delete_network(struct lxc_handler *handler) ...@@ -3251,7 +3260,16 @@ bool lxc_delete_network(struct lxc_handler *handler)
WARN("Failed to remove \"%s\" from host: %s.", hostveth, strerror(-ret)); WARN("Failed to remove \"%s\" from host: %s.", hostveth, strerror(-ret));
} else { } else {
INFO("Removed interface \"%s\" from host.", hostveth); INFO("Removed interface \"%s\" from host.", hostveth);
memset((void *)&netdev->priv.veth_attr.veth1, 0, sizeof(netdev->priv.veth_attr.veth1));
if (is_ovs_bridge(netdev->link)) {
ret = lxc_ovs_delete_port(netdev->link, hostveth);
if (ret < 0) {
WARN("Failed to remove port \"%s\" from openvswitch bridge \"%s\"", hostveth, netdev->link);
} else {
INFO("Removed port \"%s\" from openvswitch bridge \"%s\"", hostveth, netdev->link);
memset((void *)&netdev->priv.veth_attr.veth1, 0, sizeof(netdev->priv.veth_attr.veth1));
}
}
} }
} }
} }
......
...@@ -483,7 +483,7 @@ static bool create_nic(char *nic, char *br, int pid, char **cnic) ...@@ -483,7 +483,7 @@ static bool create_nic(char *nic, char *br, int pid, char **cnic)
} }
/* attach veth1 to bridge */ /* attach veth1 to bridge */
ret = lxc_bridge_attach(lxcpath, lxcname, br, veth1buf); ret = lxc_bridge_attach(br, veth1buf);
if (ret < 0) { if (ret < 0) {
usernic_error("Error attaching %s to %s\n", veth1buf, br); usernic_error("Error attaching %s to %s\n", veth1buf, br);
goto out_del; goto out_del;
......
...@@ -1413,7 +1413,7 @@ int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest) ...@@ -1413,7 +1413,7 @@ int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest)
return ip_route_dest_add(AF_INET6, ifindex, dest); return ip_route_dest_add(AF_INET6, ifindex, dest);
} }
static bool is_ovs_bridge(const char *bridge) bool is_ovs_bridge(const char *bridge)
{ {
int ret; int ret;
struct stat sb; struct stat sb;
...@@ -1431,73 +1431,71 @@ static bool is_ovs_bridge(const char *bridge) ...@@ -1431,73 +1431,71 @@ static bool is_ovs_bridge(const char *bridge)
return false; return false;
} }
struct ovs_veth_args {
const char *bridge;
const char *nic;
};
/* Called from a background thread - when nic goes away, remove it from the /* Called from a background thread - when nic goes away, remove it from the
* bridge. * bridge.
*/ */
static void ovs_cleanup_nic(const char *lxcpath, const char *name, static int lxc_ovs_delete_port_exec(void *data)
const char *bridge, const char *nic)
{ {
int ret; struct ovs_veth_args *args = data;
ret = lxc_check_inherited(NULL, true, &(int){-1}, 1); execlp("ovs-vsctl", "ovs-vsctl", "del-port", args->bridge, args->nic,
if (ret < 0) (char *)NULL);
return; return -1;
TRACE("Registering cleanup thread to remove nic \"%s\" from "
"openvswitch bridge \"%s\"", nic, bridge);
ret = lxc_wait(name, "STOPPED", -1, lxcpath);
if (ret < 0) {
ERROR("Failed to register cleanup thread to remove nic \"%s\" "
"from openvswitch bridge \"%s\"", nic, bridge);
return;
}
execlp("ovs-vsctl", "ovs-vsctl", "del-port", bridge, nic, (char *)NULL);
exit(EXIT_FAILURE);
} }
static int attach_to_ovs_bridge(const char *lxcpath, const char *name, int lxc_ovs_delete_port(const char *bridge, const char *nic)
const char *bridge, const char *nic)
{ {
pid_t pid;
char *cmd;
int ret; int ret;
char cmd_output[MAXPATHLEN];
struct ovs_veth_args args;
cmd = on_path("ovs-vsctl", NULL); args.bridge = bridge;
if (!cmd) args.nic = nic;
ret = run_command(cmd_output, sizeof(cmd_output),
lxc_ovs_delete_port_exec, (void *)&args);
if (ret < 0) {
ERROR("Failed to delete \"%s\" from openvswitch bridge \"%s\": "
"%s", bridge, nic, cmd_output);
return -1; return -1;
free(cmd); }
pid = fork(); return 0;
if (pid < 0) }
return -1;
if (pid > 0) { static int lxc_ovs_attach_bridge_exec(void *data)
ret = wait_for_pid(pid); {
if (ret < 0) struct ovs_veth_args *args = data;
return ret;
pid = fork(); execlp("ovs-vsctl", "ovs-vsctl", "add-port", args->bridge, args->nic,
if (pid < 0) (char *)NULL);
return -1; return -1;
}
if (pid > 0) static int lxc_ovs_attach_bridge(const char *bridge, const char *nic)
return 0; {
int ret;
char cmd_output[MAXPATHLEN];
struct ovs_veth_args args;
ovs_cleanup_nic(lxcpath, name, bridge, nic); args.bridge = bridge;
exit(EXIT_SUCCESS); args.nic = nic;
ret = run_command(cmd_output, sizeof(cmd_output),
lxc_ovs_attach_bridge_exec, (void *)&args);
if (ret < 0) {
ERROR("Failed to attach \"%s\" to openvswitch bridge \"%s\": %s",
bridge, nic, cmd_output);
return -1;
} }
execlp("ovs-vsctl", "ovs-vsctl", "add-port", bridge, nic, (char *)NULL); return 0;
exit(EXIT_FAILURE);
} }
/* There is a lxc_bridge_attach, but no need of a bridge detach as automatically int lxc_bridge_attach(const char *bridge, const char *ifname)
* done by kernel when a netdev is deleted.
*/
int lxc_bridge_attach(const char *lxcpath, const char *name, const char *bridge,
const char *ifname)
{ {
int err, fd, index; int err, fd, index;
struct ifreq ifr; struct ifreq ifr;
...@@ -1510,7 +1508,7 @@ int lxc_bridge_attach(const char *lxcpath, const char *name, const char *bridge, ...@@ -1510,7 +1508,7 @@ int lxc_bridge_attach(const char *lxcpath, const char *name, const char *bridge,
return -EINVAL; return -EINVAL;
if (is_ovs_bridge(bridge)) if (is_ovs_bridge(bridge))
return attach_to_ovs_bridge(lxcpath, name, bridge, ifname); return lxc_ovs_attach_bridge(bridge, ifname);
fd = socket(AF_INET, SOCK_STREAM, 0); fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) if (fd < 0)
......
...@@ -87,8 +87,10 @@ extern int lxc_ipv4_gateway_add(int ifindex, struct in_addr *gw); ...@@ -87,8 +87,10 @@ extern int lxc_ipv4_gateway_add(int ifindex, struct in_addr *gw);
extern int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *gw); extern int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *gw);
/* Attach an interface to the bridge. */ /* Attach an interface to the bridge. */
extern int lxc_bridge_attach(const char *lxcpath, const char *name, extern int lxc_bridge_attach(const char *bridge, const char *ifname);
const char *bridge, const char *ifname); extern int lxc_ovs_delete_port(const char *bridge, const char *nic);
extern bool is_ovs_bridge(const char *bridge);
/* Create default gateway. */ /* Create default gateway. */
extern int lxc_route_create_default(const char *addr, const char *ifname, extern int lxc_route_create_default(const char *addr, const char *ifname,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment