static bool arg_keep_unit = false;
static char **arg_network_interfaces = NULL;
static char **arg_network_macvlan = NULL;
+static char **arg_network_ipvlan = NULL;
static bool arg_network_veth = false;
static const char *arg_network_bridge = NULL;
static unsigned long arg_personality = 0xffffffffLU;
" --network-macvlan=INTERFACE\n"
" Create a macvlan network interface based on an\n"
" existing network interface to the container\n"
+ " --network-ipvlan=INTERFACE\n"
+ " Create a ipvlan network interface based on an\n"
+ " existing network interface to the container\n"
" -n --network-veth Add a virtual ethernet connection between host\n"
" and container\n"
" --network-bridge=INTERFACE\n"
ARG_KEEP_UNIT,
ARG_NETWORK_INTERFACE,
ARG_NETWORK_MACVLAN,
+ ARG_NETWORK_IPVLAN,
ARG_NETWORK_BRIDGE,
ARG_PERSONALITY,
ARG_VOLATILE,
{ "keep-unit", no_argument, NULL, ARG_KEEP_UNIT },
{ "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE },
{ "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN },
+ { "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN },
{ "network-veth", no_argument, NULL, 'n' },
{ "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE },
{ "personality", required_argument, NULL, ARG_PERSONALITY },
if (strv_extend(&arg_network_macvlan, optarg) < 0)
return log_oom();
+ arg_private_network = true;
+ break;
+
+ case ARG_NETWORK_IPVLAN:
+ if (strv_extend(&arg_network_ipvlan, optarg) < 0)
+ return log_oom();
+
/* fall through */
case ARG_PRIVATE_NETWORK:
r = stat(where, &dest_st);
if (r == 0) {
- if ((source_st.st_mode & S_IFMT) != (dest_st.st_mode & S_IFMT)) {
- log_error("The file types of %s and %s do not match. Refusing bind mount", *x, where);
+ if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) {
+ log_error("Cannot bind mount directory %s on file %s.", *x, where);
+ return -EINVAL;
+ }
+ if (!S_ISDIR(source_st.st_mode) && S_ISDIR(dest_st.st_mode)) {
+ log_error("Cannot bind mount file %s on directory %s.", *x, where);
return -EINVAL;
}
} else if (errno == ENOENT) {
return -errno;
}
- /* Create the mount point, but be conservative -- refuse to create block
- * and char devices. */
+ /* Create the mount point. Any non-directory file can be
+ * mounted on any non-directory file (regular, fifo, socket,
+ * char, block).
+ */
if (S_ISDIR(source_st.st_mode)) {
r = mkdir_label(where, 0755);
if (r < 0 && errno != EEXIST)
return log_error_errno(r, "Failed to create mount point %s: %m", where);
- } else if (S_ISFIFO(source_st.st_mode)) {
- r = mkfifo(where, 0644);
- if (r < 0 && errno != EEXIST)
- return log_error_errno(errno, "Failed to create mount point %s: %m", where);
- } else if (S_ISSOCK(source_st.st_mode)) {
- r = mknod(where, 0644 | S_IFSOCK, 0);
- if (r < 0 && errno != EEXIST)
- return log_error_errno(errno, "Failed to create mount point %s: %m", where);
- } else if (S_ISREG(source_st.st_mode)) {
+ } else {
r = touch(where);
if (r < 0)
return log_error_errno(r, "Failed to create mount point %s: %m", where);
- } else {
- log_error("Refusing to create mountpoint for file: %s", *x);
- return -ENOTSUP;
}
if (mount(*x, where, "bind", MS_BIND, NULL) < 0)
return 0;
}
+static int setup_ipvlan(pid_t pid) {
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
+ char **i;
+ int r;
+
+ if (!arg_private_network)
+ return 0;
+
+ if (strv_isempty(arg_network_ipvlan))
+ return 0;
+
+ r = sd_rtnl_open(&rtnl, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to netlink: %m");
+
+ udev = udev_new();
+ if (!udev) {
+ log_error("Failed to connect to udev.");
+ return -ENOMEM;
+ }
+
+ STRV_FOREACH(i, arg_network_ipvlan) {
+ _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
+ _cleanup_free_ char *n = NULL;
+ int ifi;
+
+ ifi = parse_interface(udev, *i);
+ if (ifi < 0)
+ return ifi;
+
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate netlink message: %m");
+
+ r = sd_rtnl_message_append_u32(m, IFLA_LINK, ifi);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface index: %m");
+
+ n = strappend("iv-", *i);
+ if (!n)
+ return log_oom();
+
+ strshorten(n, IFNAMSIZ-1);
+
+ r = sd_rtnl_message_append_string(m, IFLA_IFNAME, n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface name: %m");
+
+ r = sd_rtnl_message_append_u32(m, IFLA_NET_NS_PID, pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink namespace field: %m");
+
+ r = sd_rtnl_message_open_container(m, IFLA_LINKINFO);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open netlink container: %m");
+
+ r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA, "ipvlan");
+ if (r < 0)
+ return log_error_errno(r, "Failed to open netlink container: %m");
+
+ r = sd_rtnl_message_append_u16(m, IFLA_IPVLAN_MODE, IPVLAN_MODE_L2);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add ipvlan mode: %m");
+
+ r = sd_rtnl_message_close_container(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close netlink container: %m");
+
+ r = sd_rtnl_message_close_container(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close netlink container: %m");
+
+ r = sd_rtnl_call(rtnl, m, 0, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add new ipvlan interfaces: %m");
+ }
+
+ return 0;
+}
+
static int setup_seccomp(void) {
#ifdef HAVE_SECCOMP
return r;
}
-static int wait_for_block_device(struct udev *udev, dev_t devnum, struct udev_device **ret) {
- _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL;
- int r;
-
- assert(udev);
- assert(ret);
-
- for (;;) {
- _cleanup_udev_device_unref_ struct udev_device *d = NULL;
- struct pollfd pfd = {
- .events = POLLIN
- };
-
- d = udev_device_new_from_devnum(udev, 'b', devnum);
- if (!d)
- return log_oom();
-
- r = udev_device_get_is_initialized(d);
- if (r < 0)
- return log_error_errno(r, "Failed to check if device is initialized: %m");
- if (r > 0) {
- *ret = d;
- d = NULL;
- return 0;
- }
- d = udev_device_unref(d);
-
- if (!monitor) {
- monitor = udev_monitor_new_from_netlink(udev, "udev");
- if (!monitor)
- return log_oom();
-
- r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "block", NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to add block match: %m");
-
- r = udev_monitor_enable_receiving(monitor);
- if (r < 0)
- return log_error_errno(r, "Failed to turn on monitor: %m");
-
- continue;
- }
-
- pfd.fd = udev_monitor_get_fd(monitor);
- if (pfd.fd < 0)
- return log_error_errno(r, "Failed to get udev monitor fd: %m");
-
- r = poll(&pfd, 1, -1);
- if (r < 0)
- return log_error_errno(errno, "Failed to wait for device initialization: %m");
-
- d = udev_monitor_receive_device(monitor);
- }
-
- return 0;
-}
-
#define PARTITION_TABLE_BLURB \
"Note that the disk image needs to either contain only a single MBR partition of\n" \
"type 0x83 that is marked bootable, or a sinlge GPT partition of type" \
_cleanup_udev_unref_ struct udev *udev = NULL;
struct udev_list_entry *first, *item;
bool home_rw = true, root_rw = true, secondary_root_rw = true, srv_rw = true, generic_rw = true;
+ bool is_gpt, is_mbr, multiple_generic = false;
const char *pttype = NULL;
blkid_partlist pl;
struct stat st;
+ unsigned i;
int r;
- bool is_gpt, is_mbr, multiple_generic = false;
assert(fd >= 0);
assert(root_device);
if (fstat(fd, &st) < 0)
return log_error_errno(errno, "Failed to stat block device: %m");
- r = wait_for_block_device(udev, st.st_rdev, &d);
- if (r < 0)
- return r;
-
- e = udev_enumerate_new(udev);
- if (!e)
+ d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
+ if (!d)
return log_oom();
- r = udev_enumerate_add_match_parent(e, d);
- if (r < 0)
- return log_oom();
+ for (i = 0;; i++) {
+ int n, m;
- r = udev_enumerate_scan_devices(e);
- if (r < 0)
- return log_error_errno(r, "Failed to scan for partition devices of %s: %m", arg_image);
+ if (i >= 10) {
+ log_error("Kernel partitions never appeared.");
+ return -ENXIO;
+ }
+
+ e = udev_enumerate_new(udev);
+ if (!e)
+ return log_oom();
+
+ r = udev_enumerate_add_match_parent(e, d);
+ if (r < 0)
+ return log_oom();
+
+ r = udev_enumerate_scan_devices(e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to scan for partition devices of %s: %m", arg_image);
+
+ /* Count the partitions enumerated by the kernel */
+ n = 0;
+ first = udev_enumerate_get_list_entry(e);
+ udev_list_entry_foreach(item, first)
+ n++;
+
+ /* Count the partitions enumerated by blkid */
+ m = blkid_partlist_numof_partitions(pl);
+ if (n == m + 1)
+ break;
+ if (n > m + 1) {
+ log_error("blkid and kernel partition list do not match.");
+ return -EIO;
+ }
+ if (n < m + 1) {
+ unsigned j;
+
+ /* The kernel has probed fewer partitions than
+ * blkid? Maybe the kernel prober is still
+ * running or it got EBUSY because udev
+ * already opened the device. Let's reprobe
+ * the device, which is a synchronous call
+ * that waits until probing is complete. */
+
+ for (j = 0; j < 20; j++) {
+
+ r = ioctl(fd, BLKRRPART, 0);
+ if (r < 0)
+ r = -errno;
+ if (r >= 0 || r != -EBUSY)
+ break;
+
+ /* If something else has the device
+ * open, such as an udev rule, the
+ * ioctl will return EBUSY. Since
+ * there's no way to wait until it
+ * isn't busy anymore, let's just wait
+ * a bit, and try again.
+ *
+ * This is really something they
+ * should fix in the kernel! */
+
+ usleep(50 * USEC_PER_MSEC);
+ }
+
+ if (r < 0)
+ return log_error_errno(r, "Failed to reread partition table: %m");
+ }
+
+ e = udev_enumerate_unref(e);
+ }
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
if (r < 0)
goto finish;
+ r = setup_ipvlan(pid);
+ if (r < 0)
+ goto finish;
+
r = register_machine(pid, ifi);
if (r < 0)
goto finish;
strv_free(arg_setenv);
strv_free(arg_network_interfaces);
strv_free(arg_network_macvlan);
+ strv_free(arg_network_ipvlan);
strv_free(arg_bind);
strv_free(arg_bind_ro);
strv_free(arg_tmpfs);