blob: 8b7190ca0ee59e004e20dc8b6fd61a0bc646dcb6 [file] [log] [blame]
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <haproxy/api.h>
#include <haproxy/namespace.h>
#include <haproxy/hash.h>
#include <haproxy/errors.h>
#include <haproxy/log.h>
#include <haproxy/signal.h>
/* Opens the namespace <ns_name> and returns the FD or -1 in case of error
* (check errno).
*/
static int open_named_namespace(const char *ns_name)
{
if (chunk_printf(&trash, "/var/run/netns/%s", ns_name) < 0)
return -1;
return open(trash.area, O_RDONLY | O_CLOEXEC);
}
static int default_namespace = -1;
static int init_default_namespace()
{
if (chunk_printf(&trash, "/proc/%d/ns/net", getpid()) < 0)
return -1;
default_namespace = open(trash.area, O_RDONLY | O_CLOEXEC);
return default_namespace;
}
static struct eb_root namespace_tree_root = EB_ROOT;
static void netns_sig_stop(struct sig_handler *sh)
{
struct ebpt_node *node, *next;
struct netns_entry *entry;
/* close namespace file descriptors and remove registered namespaces from the
* tree when stopping */
node = ebpt_first(&namespace_tree_root);
while (node) {
next = ebpt_next(node);
ebpt_delete(node);
entry = container_of(node, struct netns_entry, node);
free(entry->node.key);
close(entry->fd);
node = next;
}
}
int netns_init(void)
{
int err_code = 0;
/* if no namespaces have been defined in the config then
* there is no point in trying to initialize anything:
* my_socketat() will never be called with a valid namespace
* structure and thus switching back to the default namespace
* is not needed either */
if (!eb_is_empty(&namespace_tree_root)) {
if (init_default_namespace() < 0) {
ha_alert("Failed to open the default namespace.\n");
err_code |= ERR_ALERT | ERR_FATAL;
}
}
signal_register_fct(0, netns_sig_stop, 0);
return err_code;
}
struct netns_entry* netns_store_insert(const char *ns_name)
{
struct netns_entry *entry = NULL;
int fd = open_named_namespace(ns_name);
if (fd == -1)
goto out;
entry = calloc(1, sizeof(*entry));
if (!entry)
goto out;
entry->fd = fd;
entry->node.key = strdup(ns_name);
entry->name_len = strlen(ns_name);
ebis_insert(&namespace_tree_root, &entry->node);
out:
return entry;
}
const struct netns_entry* netns_store_lookup(const char *ns_name, size_t ns_name_len)
{
struct ebpt_node *node;
node = ebis_lookup_len(&namespace_tree_root, ns_name, ns_name_len);
if (node)
return ebpt_entry(node, struct netns_entry, node);
else
return NULL;
}
/* Opens a socket in the namespace described by <ns> with the parameters <domain>,
* <type> and <protocol> and returns the FD or -1 in case of error (check errno).
*/
int my_socketat(const struct netns_entry *ns, int domain, int type, int protocol)
{
int sock;
if (default_namespace >= 0 && ns && setns(ns->fd, CLONE_NEWNET) == -1)
return -1;
sock = socket(domain, type, protocol);
if (default_namespace >= 0 && ns && setns(default_namespace, CLONE_NEWNET) == -1) {
if (sock >= 0)
close(sock);
return -1;
}
return sock;
}
REGISTER_BUILD_OPTS("Built with network namespace support.");