+ r = sd_rtnl_message_append_string(m, IFLA_IFNAME, veth_name);
+ if (r < 0) {
+ log_error("Failed to add netlink interface name field: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_message_append_u32(m, IFLA_MASTER, bridge);
+ if (r < 0) {
+ log_error("Failed to add netlink master field: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_call(rtnl, m, 0, NULL);
+ if (r < 0) {
+ log_error("Failed to add veth interface to bridge: %s", strerror(-r));
+ return r;
+ }
+
+ return 0;
+}
+
+static int parse_interface(struct udev *udev, const char *name) {
+ _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+ char ifi_str[2 + DECIMAL_STR_MAX(int)];
+ int ifi;
+
+ ifi = (int) if_nametoindex(name);
+ if (ifi <= 0) {
+ log_error("Failed to resolve interface %s: %m", name);
+ return -errno;
+ }
+
+ sprintf(ifi_str, "n%i", ifi);
+ d = udev_device_new_from_device_id(udev, ifi_str);
+ if (!d) {
+ log_error("Failed to get udev device for interface %s: %m", name);
+ return -errno;
+ }
+
+ if (udev_device_get_is_initialized(d) <= 0) {
+ log_error("Network interface %s is not initialized yet.", name);
+ return -EBUSY;
+ }
+
+ return ifi;
+}
+
+static int move_network_interfaces(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_interfaces))
+ return 0;
+
+ r = sd_rtnl_open(&rtnl, 0);
+ if (r < 0) {
+ log_error("Failed to connect to netlink: %s", strerror(-r));
+ return r;
+ }
+
+ udev = udev_new();
+ if (!udev) {
+ log_error("Failed to connect to udev.");
+ return -ENOMEM;
+ }
+
+ STRV_FOREACH(i, arg_network_interfaces) {
+ _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
+ int ifi;
+
+ ifi = parse_interface(udev, *i);
+ if (ifi < 0)
+ return ifi;
+
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, ifi);
+ if (r < 0) {
+ log_error("Failed to allocate netlink message: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_message_append_u32(m, IFLA_NET_NS_PID, pid);
+ if (r < 0) {
+ log_error("Failed to append namespace PID to netlink message: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_call(rtnl, m, 0, NULL);
+ if (r < 0) {
+ log_error("Failed to move interface %s to namespace: %s", *i, strerror(-r));
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int setup_macvlan(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_macvlan))
+ return 0;
+
+ r = sd_rtnl_open(&rtnl, 0);
+ if (r < 0) {
+ log_error("Failed to connect to netlink: %s", strerror(-r));
+ return r;
+ }
+
+ udev = udev_new();
+ if (!udev) {
+ log_error("Failed to connect to udev.");
+ return -ENOMEM;
+ }
+
+ STRV_FOREACH(i, arg_network_macvlan) {
+ _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) {
+ log_error("Failed to allocate netlink message: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_message_append_u32(m, IFLA_LINK, ifi);
+ if (r < 0) {
+ log_error("Failed to add netlink interface index: %s", strerror(-r));
+ return r;
+ }
+
+ n = strappend("mv-", *i);
+ if (!n)
+ return log_oom();
+
+ strshorten(n, IFNAMSIZ-1);
+
+ r = sd_rtnl_message_append_string(m, IFLA_IFNAME, n);
+ if (r < 0) {
+ log_error("Failed to add netlink interface name: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_message_append_u32(m, IFLA_NET_NS_PID, pid);
+ if (r < 0) {
+ log_error("Failed to add netlink namespace field: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_message_open_container(m, IFLA_LINKINFO);
+ if (r < 0) {
+ log_error("Failed to open netlink container: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA, "macvlan");
+ if (r < 0) {
+ log_error("Failed to open netlink container: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE);
+ if (r < 0) {
+ log_error("Failed to append macvlan mode: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_message_close_container(m);
+ if (r < 0) {
+ log_error("Failed to close netlink container: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_message_close_container(m);
+ if (r < 0) {
+ log_error("Failed to close netlink container: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_call(rtnl, m, 0, NULL);
+ if (r < 0) {
+ log_error("Failed to add new macvlan interfaces: %s", strerror(-r));
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int setup_seccomp(void) {
+
+#ifdef HAVE_SECCOMP
+ static const int blacklist[] = {
+ SCMP_SYS(kexec_load),
+ SCMP_SYS(open_by_handle_at),
+ SCMP_SYS(init_module),
+ SCMP_SYS(finit_module),
+ SCMP_SYS(delete_module),
+ SCMP_SYS(iopl),
+ SCMP_SYS(ioperm),
+ SCMP_SYS(swapon),
+ SCMP_SYS(swapoff),
+ };
+
+ scmp_filter_ctx seccomp;
+ unsigned i;
+ int r;
+
+ seccomp = seccomp_init(SCMP_ACT_ALLOW);
+ if (!seccomp)
+ return log_oom();
+
+ r = seccomp_add_secondary_archs(seccomp);
+ if (r < 0) {
+ log_error("Failed to add secondary archs to seccomp filter: %s", strerror(-r));
+ goto finish;
+ }
+
+ for (i = 0; i < ELEMENTSOF(blacklist); i++) {
+ r = seccomp_rule_add(seccomp, SCMP_ACT_ERRNO(EPERM), blacklist[i], 0);
+ if (r == -EFAULT)
+ continue; /* unknown syscall */
+ if (r < 0) {
+ log_error("Failed to block syscall: %s", strerror(-r));
+ goto finish;
+ }
+ }
+
+ /*
+ Audit is broken in containers, much of the userspace audit
+ hookup will fail if running inside a container. We don't
+ care and just turn off creation of audit sockets.
+
+ This will make socket(AF_NETLINK, *, NETLINK_AUDIT) fail
+ with EAFNOSUPPORT which audit userspace uses as indication
+ that audit is disabled in the kernel.
+ */
+
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EAFNOSUPPORT),
+ SCMP_SYS(socket),
+ 2,
+ SCMP_A0(SCMP_CMP_EQ, AF_NETLINK),
+ SCMP_A2(SCMP_CMP_EQ, NETLINK_AUDIT));
+ if (r < 0) {
+ log_error("Failed to add audit seccomp rule: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
+ if (r < 0) {
+ log_error("Failed to unset NO_NEW_PRIVS: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = seccomp_load(seccomp);
+ if (r < 0)
+ log_error("Failed to install seccomp audit filter: %s", strerror(-r));
+
+finish:
+ seccomp_release(seccomp);
+ return r;
+#else
+ return 0;
+#endif
+
+}
+
+static int setup_image(char **device_path, int *loop_nr) {
+ struct loop_info64 info = {
+ .lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN
+ };
+ _cleanup_close_ int fd = -1, control = -1, loop = -1;
+ _cleanup_free_ char* loopdev = NULL;
+ struct stat st;
+ int r, nr;
+
+ assert(device_path);
+ assert(loop_nr);
+
+ fd = open(arg_image, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY);
+ if (fd < 0) {
+ log_error("Failed to open %s: %m", arg_image);
+ return -errno;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ log_error("Failed to stat %s: %m", arg_image);
+ return -errno;
+ }
+
+ if (S_ISBLK(st.st_mode)) {
+ char *p;
+
+ p = strdup(arg_image);
+ if (!p)
+ return log_oom();
+
+ *device_path = p;
+
+ *loop_nr = -1;
+
+ r = fd;
+ fd = -1;
+
+ return r;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ log_error("%s is not a regular file or block device: %m", arg_image);
+ return -EINVAL;
+ }
+
+ control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (control < 0) {
+ log_error("Failed to open /dev/loop-control: %m");
+ return -errno;
+ }
+
+ nr = ioctl(control, LOOP_CTL_GET_FREE);
+ if (nr < 0) {
+ log_error("Failed to allocate loop device: %m");
+ return -errno;
+ }
+
+ if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
+ return log_oom();
+
+ loop = open(loopdev, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY);
+ if (loop < 0) {
+ log_error("Failed to open loop device %s: %m", loopdev);
+ return -errno;
+ }
+
+ if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
+ log_error("Failed to set loopback file descriptor on %s: %m", loopdev);
+ return -errno;
+ }
+
+ if (arg_read_only)
+ info.lo_flags |= LO_FLAGS_READ_ONLY;
+
+ if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
+ log_error("Failed to set loopback settings on %s: %m", loopdev);
+ return -errno;
+ }
+
+ *device_path = loopdev;
+ loopdev = NULL;
+
+ *loop_nr = nr;
+
+ r = loop;
+ loop = -1;
+
+ return r;
+}
+
+static int dissect_image(
+ int fd,
+ char **root_device, bool *root_device_rw,
+ char **home_device, bool *home_device_rw,
+ char **srv_device, bool *srv_device_rw,
+ bool *secondary) {
+
+#ifdef HAVE_BLKID
+ int home_nr = -1, root_nr = -1, secondary_root_nr = -1, srv_nr = -1;
+ _cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL;
+ _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
+ _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+ _cleanup_blkid_free_probe_ blkid_probe b = NULL;
+ _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;
+ const char *pttype = NULL;
+ blkid_partlist pl;
+ struct stat st;
+ int r;
+
+ assert(fd >= 0);
+ assert(root_device);
+ assert(home_device);
+ assert(srv_device);
+ assert(secondary);
+
+ b = blkid_new_probe();
+ if (!b)
+ return log_oom();
+
+ errno = 0;
+ r = blkid_probe_set_device(b, fd, 0, 0);
+ if (r != 0) {
+ if (errno == 0)
+ return log_oom();
+
+ log_error("Failed to set device on blkid probe: %m");
+ return -errno;
+ }
+
+ blkid_probe_enable_partitions(b, 1);
+ blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
+
+ errno = 0;
+ r = blkid_do_safeprobe(b);
+ if (r == -2 || r == 1) {
+ log_error("Failed to identify any partition table on %s.\n"
+ "Note that the disk image needs to follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/ to be supported by systemd-nspawn.", arg_image);
+ return -EINVAL;
+ } else if (r != 0) {
+ if (errno == 0)
+ errno = EIO;
+ log_error("Failed to probe: %m");
+ return -errno;
+ }
+
+ blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
+ if (!streq_ptr(pttype, "gpt")) {
+ log_error("Image %s does not carry a GUID Partition Table.\n"
+ "Note that the disk image needs to follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/ to be supported by systemd-nspawn.", arg_image);
+ return -EINVAL;
+ }
+
+ errno = 0;
+ pl = blkid_probe_get_partitions(b);
+ if (!pl) {
+ if (errno == 0)
+ return log_oom();
+
+ log_error("Failed to list partitions of %s", arg_image);
+ return -errno;
+ }
+
+ udev = udev_new();
+ if (!udev)
+ return log_oom();
+
+ if (fstat(fd, &st) < 0) {
+ log_error("Failed to stat block device: %m");
+ return -errno;
+ }
+
+ d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
+ if (!d)
+ return log_oom();
+
+ 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) {
+ log_error("Failed to scan for partition devices of %s: %s", arg_image, strerror(-r));
+ return r;
+ }
+
+ first = udev_enumerate_get_list_entry(e);
+ udev_list_entry_foreach(item, first) {
+ _cleanup_udev_device_unref_ struct udev_device *q;
+ const char *stype, *node;
+ unsigned long long flags;
+ sd_id128_t type_id;
+ blkid_partition pp;
+ dev_t qn;
+ int nr;
+
+ errno = 0;
+ q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
+ if (!q) {
+ if (!errno)
+ errno = ENOMEM;
+
+ log_error("Failed to get partition device of %s: %m", arg_image);
+ return -errno;