blob: 9cc85a38477919d8a0f0c65a324b5719d6142075 [file] [log] [blame]
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +01001#define _GNU_SOURCE
2
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +01003#include <sched.h>
4#include <stdio.h>
5#include <sys/stat.h>
6#include <fcntl.h>
7#include <sys/types.h>
8#include <unistd.h>
9#include <sys/socket.h>
10
11#include <string.h>
Willy Tarreau3fa0e2a2016-03-17 05:39:53 +010012
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020013#include <haproxy/api.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020014#include <haproxy/chunk.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020015#include <haproxy/errors.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020016#include <haproxy/global.h>
17#include <haproxy/hash.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020018#include <haproxy/namespace.h>
Willy Tarreau3727a8a2020-06-04 17:37:26 +020019#include <haproxy/signal.h>
Willy Tarreau3fa0e2a2016-03-17 05:39:53 +010020
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +010021/* Opens the namespace <ns_name> and returns the FD or -1 in case of error
22 * (check errno).
23 */
24static int open_named_namespace(const char *ns_name)
25{
26 if (chunk_printf(&trash, "/var/run/netns/%s", ns_name) < 0)
27 return -1;
Krisztián Kovács (kkovacs)538aa712019-09-20 14:48:19 +000028 return open(trash.area, O_RDONLY | O_CLOEXEC);
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +010029}
30
31static int default_namespace = -1;
32
33static int init_default_namespace()
34{
35 if (chunk_printf(&trash, "/proc/%d/ns/net", getpid()) < 0)
36 return -1;
Krisztián Kovács (kkovacs)538aa712019-09-20 14:48:19 +000037 default_namespace = open(trash.area, O_RDONLY | O_CLOEXEC);
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +010038 return default_namespace;
39}
40
41static struct eb_root namespace_tree_root = EB_ROOT;
42
Krisztian Kovacs710d9872019-09-24 14:12:13 +020043static void netns_sig_stop(struct sig_handler *sh)
44{
45 struct ebpt_node *node, *next;
46 struct netns_entry *entry;
47
48 /* close namespace file descriptors and remove registered namespaces from the
49 * tree when stopping */
50 node = ebpt_first(&namespace_tree_root);
51 while (node) {
52 next = ebpt_next(node);
53 ebpt_delete(node);
54 entry = container_of(node, struct netns_entry, node);
55 free(entry->node.key);
56 close(entry->fd);
Aurelien DARRAGONd4a38922023-06-14 10:11:13 +020057 free(entry);
Krisztian Kovacs710d9872019-09-24 14:12:13 +020058 node = next;
59 }
60}
61
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +010062int netns_init(void)
63{
64 int err_code = 0;
65
66 /* if no namespaces have been defined in the config then
67 * there is no point in trying to initialize anything:
68 * my_socketat() will never be called with a valid namespace
69 * structure and thus switching back to the default namespace
70 * is not needed either */
71 if (!eb_is_empty(&namespace_tree_root)) {
72 if (init_default_namespace() < 0) {
Christopher Faulet767a84b2017-11-24 16:50:31 +010073 ha_alert("Failed to open the default namespace.\n");
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +010074 err_code |= ERR_ALERT | ERR_FATAL;
75 }
76 }
77
Krisztian Kovacs710d9872019-09-24 14:12:13 +020078 signal_register_fct(0, netns_sig_stop, 0);
79
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +010080 return err_code;
81}
82
83struct netns_entry* netns_store_insert(const char *ns_name)
84{
85 struct netns_entry *entry = NULL;
86 int fd = open_named_namespace(ns_name);
87 if (fd == -1)
88 goto out;
89
Vincent Bernat02779b62016-04-03 13:48:43 +020090 entry = calloc(1, sizeof(*entry));
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +010091 if (!entry)
92 goto out;
93 entry->fd = fd;
94 entry->node.key = strdup(ns_name);
95 entry->name_len = strlen(ns_name);
96 ebis_insert(&namespace_tree_root, &entry->node);
97out:
98 return entry;
99}
100
101const struct netns_entry* netns_store_lookup(const char *ns_name, size_t ns_name_len)
102{
103 struct ebpt_node *node;
104
105 node = ebis_lookup_len(&namespace_tree_root, ns_name, ns_name_len);
106 if (node)
107 return ebpt_entry(node, struct netns_entry, node);
108 else
109 return NULL;
110}
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +0100111
112/* Opens a socket in the namespace described by <ns> with the parameters <domain>,
113 * <type> and <protocol> and returns the FD or -1 in case of error (check errno).
114 */
115int my_socketat(const struct netns_entry *ns, int domain, int type, int protocol)
116{
117 int sock;
118
Willy Tarreau70f289c2015-10-20 15:14:07 +0200119 if (default_namespace >= 0 && ns && setns(ns->fd, CLONE_NEWNET) == -1)
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +0100120 return -1;
Willy Tarreau7520e4f2018-11-11 14:38:09 +0100121
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +0100122 sock = socket(domain, type, protocol);
123
Willy Tarreau70f289c2015-10-20 15:14:07 +0200124 if (default_namespace >= 0 && ns && setns(default_namespace, CLONE_NEWNET) == -1) {
William Dauchyf7dcdc82020-02-12 21:23:20 +0100125 if (sock >= 0)
126 close(sock);
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +0100127 return -1;
128 }
KOVACS Krisztianb3e54fe2014-11-17 15:11:45 +0100129 return sock;
130}
Willy Tarreaudba50022016-12-21 18:51:45 +0100131
Willy Tarreau80713382018-11-26 10:19:54 +0100132REGISTER_BUILD_OPTS("Built with network namespace support.");