1 #define _GNU_SOURCE
2 
3 #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>
12 
13 #include <common/namespace.h>
14 #include <common/compiler.h>
15 #include <common/hash.h>
16 #include <common/errors.h>
17 #include <proto/log.h>
18 #include <proto/signal.h>
19 #include <types/global.h>
20 
21 /* Opens the namespace <ns_name> and returns the FD or -1 in case of error
22  * (check errno).
23  */
open_named_namespace(const char * ns_name)24 static int open_named_namespace(const char *ns_name)
25 {
26 	if (chunk_printf(&trash, "/var/run/netns/%s", ns_name) < 0)
27 		return -1;
28 	return open(trash.area, O_RDONLY | O_CLOEXEC);
29 }
30 
31 static int default_namespace = -1;
32 
init_default_namespace()33 static int init_default_namespace()
34 {
35 	if (chunk_printf(&trash, "/proc/%d/ns/net", getpid()) < 0)
36 		return -1;
37 	default_namespace = open(trash.area, O_RDONLY | O_CLOEXEC);
38 	return default_namespace;
39 }
40 
41 static struct eb_root namespace_tree_root = EB_ROOT;
42 
netns_sig_stop(struct sig_handler * sh)43 static 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);
57 		node = next;
58 	}
59 }
60 
netns_init(void)61 int netns_init(void)
62 {
63 	int err_code = 0;
64 
65 	/* if no namespaces have been defined in the config then
66 	 * there is no point in trying to initialize anything:
67 	 * my_socketat() will never be called with a valid namespace
68 	 * structure and thus switching back to the default namespace
69 	 * is not needed either */
70 	if (!eb_is_empty(&namespace_tree_root)) {
71 		if (init_default_namespace() < 0) {
72 			ha_alert("Failed to open the default namespace.\n");
73 			err_code |= ERR_ALERT | ERR_FATAL;
74 		}
75 	}
76 
77 	signal_register_fct(0, netns_sig_stop, 0);
78 
79 	return err_code;
80 }
81 
netns_store_insert(const char * ns_name)82 struct netns_entry* netns_store_insert(const char *ns_name)
83 {
84 	struct netns_entry *entry = NULL;
85 	int fd = open_named_namespace(ns_name);
86 	if (fd == -1)
87 		goto out;
88 
89 	entry = calloc(1, sizeof(*entry));
90 	if (!entry)
91 		goto out;
92 	entry->fd = fd;
93 	entry->node.key = strdup(ns_name);
94 	entry->name_len = strlen(ns_name);
95 	ebis_insert(&namespace_tree_root, &entry->node);
96 out:
97 	return entry;
98 }
99 
netns_store_lookup(const char * ns_name,size_t ns_name_len)100 const struct netns_entry* netns_store_lookup(const char *ns_name, size_t ns_name_len)
101 {
102 	struct ebpt_node *node;
103 
104 	node = ebis_lookup_len(&namespace_tree_root, ns_name, ns_name_len);
105 	if (node)
106 		return ebpt_entry(node, struct netns_entry, node);
107 	else
108 		return NULL;
109 }
110 
111 /* Opens a socket in the namespace described by <ns> with the parameters <domain>,
112  * <type> and <protocol> and returns the FD or -1 in case of error (check errno).
113  */
my_socketat(const struct netns_entry * ns,int domain,int type,int protocol)114 int my_socketat(const struct netns_entry *ns, int domain, int type, int protocol)
115 {
116 	int sock;
117 
118 	if (default_namespace >= 0 && ns && setns(ns->fd, CLONE_NEWNET) == -1)
119 		return -1;
120 
121 	sock = socket(domain, type, protocol);
122 
123 	if (default_namespace >= 0 && ns && setns(default_namespace, CLONE_NEWNET) == -1) {
124 		if (sock >= 0)
125 			close(sock);
126 		return -1;
127 	}
128 	return sock;
129 }
130 
131 REGISTER_BUILD_OPTS("Built with network namespace support.");
132