59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen/***
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner This file is part of systemd.
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye Copyright 2015 Lennart Poettering
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye systemd is free software; you can redistribute it and/or modify it
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen under the terms of the GNU Lesser General Public License as published by
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye the Free Software Foundation; either version 2.1 of the License, or
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye (at your option) any later version.
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye systemd is distributed in the hope that it will be useful, but
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye WITHOUT ANY WARRANTY; without even the implied warranty of
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye Lesser General Public License for more details.
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye You should have received a copy of the GNU Lesser General Public License
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye along with systemd; If not, see <http://www.gnu.org/licenses/>.
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye***/
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye#include <linux/veth.h>
8d5daad4d9da04018b562f0dd12044f5f53c1a66Kryštof Tulinger#include <net/if.h>
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner#include "libudev.h"
02508b90a0eace220eabd5ec85ff37e9009c6b16Lubos Kosco#include "sd-id128.h"
d8d744ec4741226cba1ed387037d5f8dafc3d813Kryštof Tulinger#include "sd-netlink.h"
d8d744ec4741226cba1ed387037d5f8dafc3d813Kryštof Tulinger
838924562130977ca1a3d3839d146fcda39ea1afKryštof Tulinger#include "alloc-util.h"
838924562130977ca1a3d3839d146fcda39ea1afKryštof Tulinger#include "ether-addr-util.h"
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger#include "netlink-util.h"
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger#include "nspawn-network.h"
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger#include "siphash24.h"
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger#include "string-util.h"
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger#include "udev-util.h"
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz#include "util.h"
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz
be72dc8ed6dfce2445cea09638f601a08b9175e2Trond Norbye#define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1)
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner#define CONTAINER_HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2)
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner#define VETH_EXTRA_HOST_HASH_KEY SD_ID128_MAKE(48,c7,f6,b7,ea,9d,4c,9e,b7,28,d4,de,91,d5,bf,66)
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner#define VETH_EXTRA_CONTAINER_HASH_KEY SD_ID128_MAKE(af,50,17,61,ce,f9,4d,35,84,0d,2b,20,54,be,ce,59)
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner#define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f)
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner
14a41f02433890d19b2f871156271e3388cd0845Jens Elknerstatic int generate_mac(
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner const char *machine_name,
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner struct ether_addr *mac,
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner sd_id128_t hash_key,
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner uint64_t idx) {
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner uint64_t result;
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner size_t l, sz;
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner uint8_t *v, *i;
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen int r;
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger l = strlen(machine_name);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen sz = sizeof(sd_id128_t) + l;
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger if (idx > 0)
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen sz += sizeof(idx);
cd9fbd3b531043a6a754f7013f542087727b9321Kryštof Tulinger
d8d744ec4741226cba1ed387037d5f8dafc3d813Kryštof Tulinger v = alloca(sz);
cd9fbd3b531043a6a754f7013f542087727b9321Kryštof Tulinger
cd9fbd3b531043a6a754f7013f542087727b9321Kryštof Tulinger /* fetch some persistent data unique to the host */
cd9fbd3b531043a6a754f7013f542087727b9321Kryštof Tulinger r = sd_id128_get_machine((sd_id128_t*) v);
cd9fbd3b531043a6a754f7013f542087727b9321Kryštof Tulinger if (r < 0)
cd9fbd3b531043a6a754f7013f542087727b9321Kryštof Tulinger return r;
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen /* combine with some data unique (on this host) to this
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen * container instance */
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen i = mempcpy(v + sizeof(sd_id128_t), machine_name, l);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen if (idx > 0) {
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen idx = htole64(idx);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen memcpy(i, &idx, sizeof(idx));
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen }
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen /* Let's hash the host machine ID plus the container name. We
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen * use a fixed, but originally randomly created hash key here. */
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen result = htole64(siphash24(v, sz, hash_key.bytes));
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen assert_cc(ETH_ALEN <= sizeof(result));
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen /* see eth_random_addr in the kernel */
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
981e542f40f5acaf95b69c5854e5ffb080204242Lubos Kosco mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
ff32508916722fc0e579f39b5bf22936116b8829Lubos Kosco
73100f4d1c412f00eb2568969d935cf3182b0d77Lubos Kosco return 0;
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen}
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
ff32508916722fc0e579f39b5bf22936116b8829Lubos Koscostatic int add_veth(
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner sd_netlink *rtnl,
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen pid_t pid,
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner const char *ifname_host,
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner const struct ether_addr *mac_host,
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner const char *ifname_container,
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner const struct ether_addr *mac_container) {
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
99b4056e2c5b0a51f7f480ebcefb1f917613ce2aLubos Kosco _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
8d5daad4d9da04018b562f0dd12044f5f53c1a66Kryštof Tulinger int r;
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen assert(rtnl);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen assert(ifname_host);
9a4361e23046cda58b9a5b8f4e11910dc433badaLubos Kosco assert(mac_host);
8d5daad4d9da04018b562f0dd12044f5f53c1a66Kryštof Tulinger assert(ifname_container);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen assert(mac_container);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
9a4361e23046cda58b9a5b8f4e11910dc433badaLubos Kosco if (r < 0)
8d5daad4d9da04018b562f0dd12044f5f53c1a66Kryštof Tulinger return log_error_errno(r, "Failed to allocate netlink message: %m");
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_host);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen if (r < 0)
9a4361e23046cda58b9a5b8f4e11910dc433badaLubos Kosco return log_error_errno(r, "Failed to add netlink interface name: %m");
8d5daad4d9da04018b562f0dd12044f5f53c1a66Kryštof Tulinger
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_host);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen if (r < 0)
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen return log_error_errno(r, "Failed to add netlink MAC address: %m");
9a4361e23046cda58b9a5b8f4e11910dc433badaLubos Kosco
8d5daad4d9da04018b562f0dd12044f5f53c1a66Kryštof Tulinger r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen if (r < 0)
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen return log_error_errno(r, "Failed to open netlink container: %m");
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "veth");
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz if (r < 0)
73189ea86c6fb0af01e16eaa5b0da3f2bb775c41Harry Pan return log_error_errno(r, "Failed to open netlink container: %m");
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz r = sd_netlink_message_open_container(m, VETH_INFO_PEER);
73189ea86c6fb0af01e16eaa5b0da3f2bb775c41Harry Pan if (r < 0)
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz return log_error_errno(r, "Failed to open netlink container: %m");
ceddad3a2fabdcc5b60631501187f9e8b4b43a3bKnut Anders Hatlen
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_container);
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz if (r < 0)
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz return log_error_errno(r, "Failed to add netlink interface name: %m");
ceddad3a2fabdcc5b60631501187f9e8b4b43a3bKnut Anders Hatlen
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_container);
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz if (r < 0)
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz return log_error_errno(r, "Failed to add netlink MAC address: %m");
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz
0660813d1476ce33b006694581155bf28067687bSven-Kristofer Pilz r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid);
ff32508916722fc0e579f39b5bf22936116b8829Lubos Kosco if (r < 0)
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner return log_error_errno(r, "Failed to add netlink namespace field: %m");
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen r = sd_netlink_message_close_container(m);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen if (r < 0)
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen return log_error_errno(r, "Failed to close netlink container: %m");
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen r = sd_netlink_message_close_container(m);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen if (r < 0)
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen return log_error_errno(r, "Failed to close netlink container: %m");
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen r = sd_netlink_message_close_container(m);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen if (r < 0)
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen return log_error_errno(r, "Failed to close netlink container: %m");
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen r = sd_netlink_call(rtnl, m, 0, NULL);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen if (r < 0)
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen return log_error_errno(r, "Failed to add new veth interfaces (%s:%s): %m", ifname_host, ifname_container);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen return 0;
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen}
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlenint setup_veth(const char *machine_name,
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen pid_t pid,
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen char iface_name[IFNAMSIZ],
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen bool bridge) {
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen struct ether_addr mac_host, mac_container;
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen int r, i;
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner assert(machine_name);
ff32508916722fc0e579f39b5bf22936116b8829Lubos Kosco assert(pid > 0);
9ec7787531611654e8f50932473aa48963eaba55Trond Norbye assert(iface_name);
117599e68158f1cdc904c452d5948136744e3e4dKryštof Tulinger
117599e68158f1cdc904c452d5948136744e3e4dKryštof Tulinger /* Use two different interface name prefixes depending whether
117599e68158f1cdc904c452d5948136744e3e4dKryštof Tulinger * we are in bridge mode or not. */
117599e68158f1cdc904c452d5948136744e3e4dKryštof Tulinger snprintf(iface_name, IFNAMSIZ - 1, "%s-%s",
117599e68158f1cdc904c452d5948136744e3e4dKryštof Tulinger bridge ? "vb" : "ve", machine_name);
117599e68158f1cdc904c452d5948136744e3e4dKryštof Tulinger
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen if (r < 0)
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m");
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner
3df9409a61b23dd736d9ce7bea6e4256bc449ff2Kryštof Tulinger r = generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0);
3df9409a61b23dd736d9ce7bea6e4256bc449ff2Kryštof Tulinger if (r < 0)
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m");
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen r = sd_netlink_open(&rtnl);
3df9409a61b23dd736d9ce7bea6e4256bc449ff2Kryštof Tulinger if (r < 0)
3df9409a61b23dd736d9ce7bea6e4256bc449ff2Kryštof Tulinger return log_error_errno(r, "Failed to connect to netlink: %m");
3df9409a61b23dd736d9ce7bea6e4256bc449ff2Kryštof Tulinger
3df9409a61b23dd736d9ce7bea6e4256bc449ff2Kryštof Tulinger r = add_veth(rtnl, pid, iface_name, &mac_host, "host0", &mac_container);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen if (r < 0)
cd9fbd3b531043a6a754f7013f542087727b9321Kryštof Tulinger return r;
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger i = (int) if_nametoindex(iface_name);
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger if (i <= 0)
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger return log_error_errno(errno, "Failed to resolve interface %s: %m", iface_name);
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger return i;
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger}
3df9409a61b23dd736d9ce7bea6e4256bc449ff2Kryštof Tulinger
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulingerint setup_veth_extra(
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger const char *machine_name,
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger pid_t pid,
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger char **pairs) {
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger
d8d744ec4741226cba1ed387037d5f8dafc3d813Kryštof Tulinger _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
838924562130977ca1a3d3839d146fcda39ea1afKryštof Tulinger uint64_t idx = 0;
d8d744ec4741226cba1ed387037d5f8dafc3d813Kryštof Tulinger char **a, **b;
838924562130977ca1a3d3839d146fcda39ea1afKryštof Tulinger int r;
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger assert(machine_name);
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger assert(pid > 0);
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen if (strv_isempty(pairs))
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger return 0;
d8d744ec4741226cba1ed387037d5f8dafc3d813Kryštof Tulinger
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger r = sd_netlink_open(&rtnl);
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger if (r < 0)
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger return log_error_errno(r, "Failed to connect to netlink: %m");
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger STRV_FOREACH_PAIR(a, b, pairs) {
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger struct ether_addr mac_host, mac_container;
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger
d8d744ec4741226cba1ed387037d5f8dafc3d813Kryštof Tulinger r = generate_mac(machine_name, &mac_container, VETH_EXTRA_CONTAINER_HASH_KEY, idx);
d8d744ec4741226cba1ed387037d5f8dafc3d813Kryštof Tulinger if (r < 0)
d8d744ec4741226cba1ed387037d5f8dafc3d813Kryštof Tulinger return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m");
838924562130977ca1a3d3839d146fcda39ea1afKryštof Tulinger
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger r = generate_mac(machine_name, &mac_host, VETH_EXTRA_HOST_HASH_KEY, idx);
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger if (r < 0)
d8d744ec4741226cba1ed387037d5f8dafc3d813Kryštof Tulinger return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m");
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger r = add_veth(rtnl, pid, *a, &mac_host, *b, &mac_container);
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger if (r < 0)
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger return r;
d470e59c0405a31b7e5f194bd9b705e91b12bf0aKryštof Tulinger
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner idx ++;
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen }
59b6a8c0cc6ef741a7180504b3c371e67c2aa338Knut Anders Hatlen
3df9409a61b23dd736d9ce7bea6e4256bc449ff2Kryštof Tulinger return 0;
3df9409a61b23dd736d9ce7bea6e4256bc449ff2Kryštof Tulinger}
3df9409a61b23dd736d9ce7bea6e4256bc449ff2Kryštof Tulinger
14a41f02433890d19b2f871156271e3388cd0845Jens Elknerint setup_bridge(const char *veth_name, const char *bridge_name) {
117599e68158f1cdc904c452d5948136744e3e4dKryštof Tulinger _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner int r, bridge_ifi;
14a41f02433890d19b2f871156271e3388cd0845Jens Elkner
d80679a08aa7384b018e1b44cb0741a6721ecb68Lubos Kosco assert(veth_name);
assert(bridge_name);
bridge_ifi = (int) if_nametoindex(bridge_name);
if (bridge_ifi <= 0)
return log_error_errno(errno, "Failed to resolve interface %s: %m", bridge_name);
r = sd_netlink_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0);
if (r < 0)
return log_error_errno(r, "Failed to allocate netlink message: %m");
r = sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP);
if (r < 0)
return log_error_errno(r, "Failed to set IFF_UP flag: %m");
r = sd_netlink_message_append_string(m, IFLA_IFNAME, veth_name);
if (r < 0)
return log_error_errno(r, "Failed to add netlink interface name field: %m");
r = sd_netlink_message_append_u32(m, IFLA_MASTER, bridge_ifi);
if (r < 0)
return log_error_errno(r, "Failed to add netlink master field: %m");
r = sd_netlink_call(rtnl, m, 0, NULL);
if (r < 0)
return log_error_errno(r, "Failed to add veth interface to bridge: %m");
return bridge_ifi;
}
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)
return log_error_errno(errno, "Failed to resolve interface %s: %m", name);
sprintf(ifi_str, "n%i", ifi);
d = udev_device_new_from_device_id(udev, ifi_str);
if (!d)
return log_error_errno(errno, "Failed to get udev device for interface %s: %m", name);
if (udev_device_get_is_initialized(d) <= 0) {
log_error("Network interface %s is not initialized yet.", name);
return -EBUSY;
}
return ifi;
}
int move_network_interfaces(pid_t pid, char **ifaces) {
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
char **i;
int r;
if (strv_isempty(ifaces))
return 0;
r = sd_netlink_open(&rtnl);
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, ifaces) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int ifi;
ifi = parse_interface(udev, *i);
if (ifi < 0)
return ifi;
r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, ifi);
if (r < 0)
return log_error_errno(r, "Failed to allocate netlink message: %m");
r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid);
if (r < 0)
return log_error_errno(r, "Failed to append namespace PID to netlink message: %m");
r = sd_netlink_call(rtnl, m, 0, NULL);
if (r < 0)
return log_error_errno(r, "Failed to move interface %s to namespace: %m", *i);
}
return 0;
}
int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
unsigned idx = 0;
char **i;
int r;
if (strv_isempty(ifaces))
return 0;
r = sd_netlink_open(&rtnl);
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, ifaces) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
_cleanup_free_ char *n = NULL;
struct ether_addr mac;
int ifi;
ifi = parse_interface(udev, *i);
if (ifi < 0)
return ifi;
r = generate_mac(machine_name, &mac, MACVLAN_HASH_KEY, idx++);
if (r < 0)
return log_error_errno(r, "Failed to create MACVLAN MAC address: %m");
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_netlink_message_append_u32(m, IFLA_LINK, ifi);
if (r < 0)
return log_error_errno(r, "Failed to add netlink interface index: %m");
n = strappend("mv-", *i);
if (!n)
return log_oom();
strshorten(n, IFNAMSIZ-1);
r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
if (r < 0)
return log_error_errno(r, "Failed to add netlink interface name: %m");
r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac);
if (r < 0)
return log_error_errno(r, "Failed to add netlink MAC address: %m");
r = sd_netlink_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_netlink_message_open_container(m, IFLA_LINKINFO);
if (r < 0)
return log_error_errno(r, "Failed to open netlink container: %m");
r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "macvlan");
if (r < 0)
return log_error_errno(r, "Failed to open netlink container: %m");
r = sd_netlink_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE);
if (r < 0)
return log_error_errno(r, "Failed to append macvlan mode: %m");
r = sd_netlink_message_close_container(m);
if (r < 0)
return log_error_errno(r, "Failed to close netlink container: %m");
r = sd_netlink_message_close_container(m);
if (r < 0)
return log_error_errno(r, "Failed to close netlink container: %m");
r = sd_netlink_call(rtnl, m, 0, NULL);
if (r < 0)
return log_error_errno(r, "Failed to add new macvlan interfaces: %m");
}
return 0;
}
int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
char **i;
int r;
if (strv_isempty(ifaces))
return 0;
r = sd_netlink_open(&rtnl);
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, ifaces) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_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_netlink_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_netlink_message_append_string(m, IFLA_IFNAME, n);
if (r < 0)
return log_error_errno(r, "Failed to add netlink interface name: %m");
r = sd_netlink_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_netlink_message_open_container(m, IFLA_LINKINFO);
if (r < 0)
return log_error_errno(r, "Failed to open netlink container: %m");
r = sd_netlink_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_netlink_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_netlink_message_close_container(m);
if (r < 0)
return log_error_errno(r, "Failed to close netlink container: %m");
r = sd_netlink_message_close_container(m);
if (r < 0)
return log_error_errno(r, "Failed to close netlink container: %m");
r = sd_netlink_call(rtnl, m, 0, NULL);
if (r < 0)
return log_error_errno(r, "Failed to add new ipvlan interfaces: %m");
}
return 0;
}
int veth_extra_parse(char ***l, const char *p) {
_cleanup_free_ char *a = NULL, *b = NULL;
int r;
r = extract_first_word(&p, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0 || isempty(a))
return -EINVAL;
r = extract_first_word(&p, &b, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0 || isempty(b)) {
free(b);
b = strdup(a);
if (!b)
return -ENOMEM;
}
if (p)
return -EINVAL;
r = strv_push_pair(l, a, b);
if (r < 0)
return -ENOMEM;
a = b = NULL;
return 0;
}