7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering/***
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering This file is part of systemd.
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering Copyright 2015 Lennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering systemd is free software; you can redistribute it and/or modify it
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering under the terms of the GNU Lesser General Public License as published by
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering (at your option) any later version.
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering systemd is distributed in the hope that it will be useful, but
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering Lesser General Public License for more details.
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering You should have received a copy of the GNU Lesser General Public License
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering***/
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering#include "sd-netlink.h"
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include "alloc-util.h"
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poettering#include "fd-util.h"
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering#include "firewall-util.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "in-addr-util.h"
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering#include "local-addresses.h"
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering#include "netlink-util.h"
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poettering#include "nspawn-expose-ports.h"
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering#include "parse-util.h"
2583fbea8e762d2e915582af60560f884d5093f5Lennart Poettering#include "socket-util.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "string-util.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "util.h"
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poetteringint expose_port_parse(ExposePort **l, const char *s) {
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering const char *split, *e;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering uint16_t container_port, host_port;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering int protocol;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering ExposePort *p;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering int r;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering assert(l);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering assert(s);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if ((e = startswith(s, "tcp:")))
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering protocol = IPPROTO_TCP;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering else if ((e = startswith(s, "udp:")))
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering protocol = IPPROTO_UDP;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering else {
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering e = s;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering protocol = IPPROTO_TCP;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering }
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering split = strchr(e, ':');
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (split) {
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering char v[split - e + 1];
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering memcpy(v, e, split - e);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering v[split - e] = 0;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering r = safe_atou16(v, &host_port);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (r < 0 || host_port <= 0)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return -EINVAL;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering r = safe_atou16(split + 1, &container_port);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering } else {
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering r = safe_atou16(e, &container_port);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering host_port = container_port;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering }
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (r < 0 || container_port <= 0)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return -EINVAL;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering LIST_FOREACH(ports, p, *l)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (p->protocol == protocol && p->host_port == host_port)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return -EEXIST;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering p = new(ExposePort, 1);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (!p)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return -ENOMEM;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering p->protocol = protocol;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering p->host_port = host_port;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering p->container_port = container_port;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering LIST_PREPEND(ports, *l, p);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return 0;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering}
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poetteringvoid expose_port_free_all(ExposePort *p) {
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering while (p) {
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering ExposePort *q = p;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering LIST_REMOVE(ports, p, q);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering free(q);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering }
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering}
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poetteringint expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering ExposePort *p;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering int r, af = AF_INET;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering assert(exposed);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (!l)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return 0;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (in_addr_is_null(af, exposed))
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return 0;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering log_debug("Lost IP address.");
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering LIST_FOREACH(ports, p, l) {
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering r = fw_add_local_dnat(false,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering af,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering p->protocol,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering NULL,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering NULL, 0,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering NULL, 0,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering p->host_port,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering exposed,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering p->container_port,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering NULL);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (r < 0)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering log_warning_errno(r, "Failed to modify firewall: %m");
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering }
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering *exposed = IN_ADDR_NULL;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return 0;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering}
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poetteringint expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) {
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering _cleanup_free_ struct local_address *addresses = NULL;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering _cleanup_free_ char *pretty = NULL;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering union in_addr_union new_exposed;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering ExposePort *p;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering bool add;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering int af = AF_INET, r;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering assert(exposed);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering /* Invoked each time an address is added or removed inside the
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering * container */
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (!l)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return 0;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering r = local_addresses(rtnl, 0, af, &addresses);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (r < 0)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return log_error_errno(r, "Failed to enumerate local addresses: %m");
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering add = r > 0 &&
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering addresses[0].family == af &&
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering addresses[0].scope < RT_SCOPE_LINK;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (!add)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return expose_port_flush(l, exposed);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering new_exposed = addresses[0].address;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (in_addr_equal(af, exposed, &new_exposed))
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return 0;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering in_addr_to_string(af, &new_exposed, &pretty);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering log_debug("New container IP is %s.", strna(pretty));
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering LIST_FOREACH(ports, p, l) {
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering r = fw_add_local_dnat(true,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering af,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering p->protocol,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering NULL,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering NULL, 0,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering NULL, 0,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering p->host_port,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering &new_exposed,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering p->container_port,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering in_addr_is_null(af, exposed) ? NULL : exposed);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (r < 0)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering log_warning_errno(r, "Failed to modify firewall: %m");
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering }
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering *exposed = new_exposed;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return 0;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering}
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poetteringint expose_port_send_rtnl(int send_fd) {
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering _cleanup_close_ int fd = -1;
d960371482d75711e61896f27ea0d3740ea69fe0David Herrmann int r;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering assert(send_fd >= 0);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (fd < 0)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return log_error_errno(errno, "Failed to allocate container netlink: %m");
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering /* Store away the fd in the socket, so that it stays open as
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering * long as we run the child */
3ee897d6c2401effbc82f5eef35fce405781d6c8Lennart Poettering r = send_one_fd(send_fd, fd, 0);
d960371482d75711e61896f27ea0d3740ea69fe0David Herrmann if (r < 0)
d960371482d75711e61896f27ea0d3740ea69fe0David Herrmann return log_error_errno(r, "Failed to send netlink fd: %m");
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return 0;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering}
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poetteringint expose_port_watch_rtnl(
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering sd_event *event,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering int recv_fd,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering sd_netlink_message_handler_t handler,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering union in_addr_union *exposed,
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering sd_netlink **ret) {
4afd3348c7506dd1d36305b7bcb9feb8952b9d6bLennart Poettering _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering int fd, r;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering assert(event);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering assert(recv_fd >= 0);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering assert(ret);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
3ee897d6c2401effbc82f5eef35fce405781d6c8Lennart Poettering fd = receive_one_fd(recv_fd, 0);
d960371482d75711e61896f27ea0d3740ea69fe0David Herrmann if (fd < 0)
d960371482d75711e61896f27ea0d3740ea69fe0David Herrmann return log_error_errno(fd, "Failed to recv netlink fd: %m");
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering r = sd_netlink_open_fd(&rtnl, fd);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (r < 0) {
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering safe_close(fd);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return log_error_errno(r, "Failed to create rtnl object: %m");
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering }
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering r = sd_netlink_add_match(rtnl, RTM_NEWADDR, handler, exposed);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (r < 0)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m");
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering r = sd_netlink_add_match(rtnl, RTM_DELADDR, handler, exposed);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (r < 0)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m");
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering r = sd_netlink_attach_event(rtnl, event, 0);
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering if (r < 0)
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return log_error_errno(r, "Failed to add to even loop: %m");
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering *ret = rtnl;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering rtnl = NULL;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering return 0;
7a8f63251df2202e59a76e537a688fe7500cb1adLennart Poettering}