diff --git a/Makefile b/Makefile index ac93fed74..4671759cd 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,7 @@ # USE_ZLIB : enable zlib library support. # USE_CPU_AFFINITY : enable pinning processes to CPU on Linux. Automatic. # USE_TFO : enable TCP fast open. Supported on Linux >= 3.7. +# USE_NS : enable network namespace support. Supported on Linux >= 2.6.24. # # Options can be forced by specifying "USE_xxx=1" or can be disabled by using # "USE_xxx=" (empty string). @@ -617,6 +618,11 @@ TRACE_COPTS := $(filter-out -O0 -O1 -O2 -pg -finstrument-functions,$(COPTS)) -O3 COPTS += -finstrument-functions endif +ifneq ($(USE_NS),) +OPTIONS_CFLAGS += -DCONFIG_HAP_NS +BUILD_OPTIONS += $(call ignore_implicit,USE_NS) +endif + #### Global link options # These options are added at the end of the "ld" command line. Use LDFLAGS to # add options at the beginning of the "ld" command line if needed. @@ -657,7 +663,8 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocol.o \ src/stream_interface.o src/dumpstats.o src/proto_tcp.o \ src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \ src/acl.o src/sample.o src/memory.o src/freq_ctr.o src/auth.o \ - src/compression.o src/payload.o src/hash.o src/pattern.o src/map.o + src/compression.o src/payload.o src/hash.o src/pattern.o src/map.o \ + src/namespace.o EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \ $(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \ diff --git a/doc/network-namespaces.txt b/doc/network-namespaces.txt new file mode 100644 index 000000000..9448f43ad --- /dev/null +++ b/doc/network-namespaces.txt @@ -0,0 +1,106 @@ +Linux network namespace support for HAProxy +=========================================== + +HAProxy supports proxying between Linux network namespaces. This +feature can be used, for example, in a multi-tenant networking +environment to proxy between different networks. HAProxy can also act +as a front-end proxy for non namespace-aware services. + +The proxy protocol has been extended to support transferring the +namespace information, so the originating namespace information can be +kept. This is useful when chaining multiple proxies and services. + +To enable Linux namespace support, compile HAProxy with the `USE_NS=1` +make option. + + +## Setting up namespaces on Linux + +To create network namespaces, use the 'ip netns' command. See the +manual page ip-netns(8) for details. + +Make sure that the file descriptors representing the network namespace +are located under `/var/run/netns`. + +For example, you can create a network namespace and assign one of the +networking interfaces to the new namespace: + +``` +$ ip netns add netns1 +$ ip link set eth7 netns netns1 +``` + + +## Listing namespaces in the configuration file + +HAProxy uses namespaces explicitly listed in its configuration file. +If you are not using namespace information received through the proxy +protocol, this usually means that you must specify namespaces for +listeners and servers in the configuration file with the 'namespace' +keyword. + +However, if you're using the namespace information received through +the proxy protocol to determine the namespace of servers (see +'namespace * below'), you have to explicitly list all allowed +namespaces in the namespace_list section of your configuration file: + +``` +namespace_list + namespace netns1 + namespace netns2 +``` + + +## Namespace information flow + +The haproxy process always runs in the namespace it was started on. +This is the default namespace. + +The bind addresses of listeners can have their namespace specified in +the configuration file. Unless specified, sockets associated with +listener bind addresses are created in the default namespace. For +example, this creates a listener in the netns2 namespace: + +``` +frontend f_example + bind 192.168.1.1:80 namespace netns2 + default_backend http +``` + +Each client connection is associated with its source namespace. By +default, this is the namespace of the bind socket it arrived on, but +can be overridden by information received through the proxy protocol. +Proxy protocol v2 supports transferring namespace information, so if +it is enabled for the listener, it can override the associated +namespace of the connection. + +Servers can have their namespaces specified in the configuration file +with the 'namespace' keyword: + +``` +backend b_example + server s1 192.168.1.100:80 namespace netns2 +``` + +If no namespace is set for a server, it is assumed that it is in the +default namespace. When specified, outbound sockets to the server are +created in the network namespace configured. To create the outbound +(server) connection in the namespace associated with the client, use +the '*' namespace. This is especially useful when using the +destination address and namespace received from the proxy protocol. + +``` +frontend f_example + bind 192.168.1.1:9990 accept-proxy + default_backend b_example + +backend b_example + mode tcp + source 0.0.0.0 usesrc clientip + server snodes * namespace * +``` + +If HAProxy is configured to send proxy protocol v2 headers to the +server, the outgoing header will always contain the namespace +associated with the client connection, not the namespace configured +for the server. diff --git a/include/common/namespace.h b/include/common/namespace.h new file mode 100644 index 000000000..e67742b1b --- /dev/null +++ b/include/common/namespace.h @@ -0,0 +1,24 @@ +#ifndef NAMESPACE_H + +#include +#include + +struct netns_entry; +int my_socketat(const struct netns_entry *ns, int domain, int type, int protocol); + +#ifdef CONFIG_HAP_NS + +struct netns_entry +{ + struct ebpt_node node; + size_t name_len; + int fd; +}; + +struct netns_entry* netns_store_insert(const char *ns_name); +const struct netns_entry* netns_store_lookup(const char *ns_name, size_t ns_name_len); + +int netns_init(void); +#endif /* CONFIG_HAP_NS */ + +#endif /* NAMESPACE_H */ diff --git a/include/proto/connection.h b/include/proto/connection.h index c9972b155..76093a51b 100644 --- a/include/proto/connection.h +++ b/include/proto/connection.h @@ -458,6 +458,7 @@ static inline void conn_init(struct connection *conn) conn->t.sock.fd = -1; /* just to help with debugging */ conn->err_code = CO_ER_NONE; conn->target = NULL; + conn->proxy_netns = NULL; } /* Tries to allocate a new connection and initialized its main fields. The diff --git a/include/types/connection.h b/include/types/connection.h index b31700737..4c610969e 100644 --- a/include/types/connection.h +++ b/include/types/connection.h @@ -261,6 +261,7 @@ struct connection { } sock; } t; enum obj_type *target; /* the target to connect to (server, proxy, applet, ...) */ + const struct netns_entry *proxy_netns; struct { struct sockaddr_storage from; /* client address, or address to spoof when connecting to the server */ struct sockaddr_storage to; /* address reached by the client, or address to connect to */ @@ -330,7 +331,9 @@ struct proxy_hdr_v2 { #define PP2_TYPE_SSL 0x20 #define PP2_TYPE_SSL_VERSION 0x21 #define PP2_TYPE_SSL_CN 0x22 +#define PP2_TYPE_NETNS 0x30 +#define TLV_HEADER_SIZE 3 struct tlv { uint8_t type; uint8_t length_hi; diff --git a/include/types/listener.h b/include/types/listener.h index 83b63afd7..725808b34 100644 --- a/include/types/listener.h +++ b/include/types/listener.h @@ -177,6 +177,8 @@ struct listener { int maxseg; /* for TCP, advertised MSS */ char *interface; /* interface name or NULL */ + const struct netns_entry *netns; /* network namespace of the listener*/ + struct list by_fe; /* chaining in frontend's list of listeners */ struct list by_bind; /* chaining in bind_conf's list of listeners */ struct bind_conf *bind_conf; /* "bind" line settings, include SSL settings among other things */ diff --git a/include/types/server.h b/include/types/server.h index 5798fab4e..4847defd1 100644 --- a/include/types/server.h +++ b/include/types/server.h @@ -85,6 +85,7 @@ enum srv_admin { #define SRV_F_BACKUP 0x0001 /* this server is a backup server */ #define SRV_F_MAPPORTS 0x0002 /* this server uses mapped ports */ #define SRV_F_NON_STICK 0x0004 /* never add connections allocated to this server to a stick table */ +#define SRV_F_USE_NS_FROM_PP 0x0008 /* use namespace associated with connection if present */ /* configured server options for send-proxy (server->pp_opts) */ #define SRV_PP_V1 0x0001 /* proxy protocol version 1 */ @@ -191,6 +192,7 @@ struct server { unsigned lb_nodes_now; /* number of lb_nodes placed in the tree (C-HASH) */ struct tree_occ *lb_nodes; /* lb_nodes_tot * struct tree_occ */ + const struct netns_entry *netns; /* contains network namespace name or NULL. Network namespace comes from configuration */ /* warning, these structs are huge, keep them at the bottom */ struct sockaddr_storage addr; /* the address to connect to */ struct protocol *proto; /* server address protocol */ diff --git a/src/backend.c b/src/backend.c index 6fe03f951..e2221603e 100644 --- a/src/backend.c +++ b/src/backend.c @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -720,7 +721,6 @@ int assign_server(struct session *s) return err; } - /* * This function assigns a server address to a session, and sets SN_ADDR_SET. * The address is taken from the currently assigned server, or from the @@ -803,11 +803,13 @@ int assign_server_address(struct session *s) return SRV_STATUS_INTERNAL; } + /* Copy network namespace from client connection */ + srv_conn->proxy_netns = cli_conn->proxy_netns; + s->flags |= SN_ADDR_SET; return SRV_STATUS_OK; } - /* This function assigns a server to session if required, and can add the * connection to either the assigned server's queue or to the proxy's queue. * If ->srv_conn is set, the session is first released from the server. diff --git a/src/cfgparse.c b/src/cfgparse.c index a6a00517d..c8b15467f 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -5644,6 +5645,48 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) return err_code; } +int +cfg_parse_netns(const char *file, int linenum, char **args, int kwm) +{ +#ifdef CONFIG_HAP_NS + const char *err; + const char *item = args[0]; + + if (!strcmp(item, "namespace_list")) { + return 0; + } + else if (!strcmp(item, "namespace")) { + size_t idx = 1; + const char *current; + while (*(current = args[idx++])) { + err = invalid_char(current); + if (err) { + Alert("parsing [%s:%d]: character '%c' is not permitted in '%s' name '%s'.\n", + file, linenum, *err, item, current); + return ERR_ALERT | ERR_FATAL; + } + + if (netns_store_lookup(current, strlen(current))) { + Alert("parsing [%s:%d]: Namespace '%s' is already added.\n", + file, linenum, current); + return ERR_ALERT | ERR_FATAL; + } + if (!netns_store_insert(current)) { + Alert("parsing [%s:%d]: Cannot open namespace '%s'.\n", + file, linenum, current); + return ERR_ALERT | ERR_FATAL; + } + } + } + + return 0; +#else + Alert("parsing [%s:%d]: namespace support is not compiled in.", + file, linenum); + return ERR_ALERT | ERR_FATAL; +#endif +} + int cfg_parse_users(const char *file, int linenum, char **args, int kwm) { @@ -5856,7 +5899,8 @@ int readcfgfile(const char *file) !cfg_register_section("defaults", cfg_parse_listen) || !cfg_register_section("global", cfg_parse_global) || !cfg_register_section("userlist", cfg_parse_users) || - !cfg_register_section("peers", cfg_parse_peers)) + !cfg_register_section("peers", cfg_parse_peers) || + !cfg_register_section("namespace_list", cfg_parse_netns)) return -1; if ((f=fopen(file,"r")) == NULL) diff --git a/src/connection.c b/src/connection.c index b9f5c42b4..c21c98bae 100644 --- a/src/connection.c +++ b/src/connection.c @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -217,6 +218,14 @@ void conn_update_sock_polling(struct connection *c) c->flags = f; } +/* + * Get data length from tlv + */ +static int get_tlv_length(const struct tlv *src) +{ + return (src->length_hi << 8) | src->length_lo; +} + /* This handshake handler waits a PROXY protocol header at the beginning of the * raw data stream. The header looks like this : * @@ -245,6 +254,7 @@ int conn_recv_proxy(struct connection *conn, int flag) char *line, *end; struct proxy_hdr_v2 *hdr_v2; const char v2sig[] = PP2_SIGNATURE; + int tlv_length = 0; /* we might have been called just after an asynchronous shutr */ if (conn->flags & CO_FL_SOCK_RD_SH) @@ -434,6 +444,7 @@ int conn_recv_proxy(struct connection *conn, int flag) ((struct sockaddr_in *)&conn->addr.to)->sin_addr.s_addr = hdr_v2->addr.ip4.dst_addr; ((struct sockaddr_in *)&conn->addr.to)->sin_port = hdr_v2->addr.ip4.dst_port; conn->flags |= CO_FL_ADDR_FROM_SET | CO_FL_ADDR_TO_SET; + tlv_length = ntohs(hdr_v2->len) - PP2_ADDR_LEN_INET; break; case 0x21: /* TCPv6 */ if (ntohs(hdr_v2->len) < PP2_ADDR_LEN_INET6) @@ -446,8 +457,35 @@ int conn_recv_proxy(struct connection *conn, int flag) memcpy(&((struct sockaddr_in6 *)&conn->addr.to)->sin6_addr, hdr_v2->addr.ip6.dst_addr, 16); ((struct sockaddr_in6 *)&conn->addr.to)->sin6_port = hdr_v2->addr.ip6.dst_port; conn->flags |= CO_FL_ADDR_FROM_SET | CO_FL_ADDR_TO_SET; + tlv_length = ntohs(hdr_v2->len) - PP2_ADDR_LEN_INET6; break; } + + /* TLV parsing */ + if (tlv_length > 0) { + int tlv_offset = trash.len - tlv_length; + + while (tlv_offset + TLV_HEADER_SIZE <= trash.len) { + const struct tlv *tlv_packet = (struct tlv *) &trash.str[tlv_offset]; + const int tlv_len = get_tlv_length(tlv_packet); + tlv_offset += tlv_len + TLV_HEADER_SIZE; + + switch (tlv_packet->type) { +#ifdef CONFIG_HAP_NS + case PP2_TYPE_NETNS: { + const struct netns_entry *ns; + ns = netns_store_lookup((char*)tlv_packet->value, tlv_len); + if (ns) + conn->proxy_netns = ns; + break; + } +#endif + default: + break; + } + } + } + /* unsupported protocol, keep local connection address */ break; case 0x00: /* LOCAL command */ @@ -597,8 +635,8 @@ int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src, str return ret; } -#ifdef USE_OPENSSL -static int make_tlv(char *dest, int dest_len, char type, uint16_t length, char *value) +#if defined(USE_OPENSSL) || defined(CONFIG_HAP_NS) +static int make_tlv(char *dest, int dest_len, char type, uint16_t length, const char *value) { struct tlv *tlv; @@ -623,8 +661,8 @@ int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connec struct sockaddr_storage null_addr = {0}; struct sockaddr_storage *src = &null_addr; struct sockaddr_storage *dst = &null_addr; + #ifdef USE_OPENSSL - int tlv_len = 0; char *value = NULL; struct tlv_ssl *tlv; int ssl_tlv_len = 0; @@ -639,6 +677,7 @@ int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connec src = &remote->addr.from; dst = &remote->addr.to; } + if (src && dst && src->ss_family == dst->ss_family && src->ss_family == AF_INET) { if (buf_len < PP2_HDR_LEN_INET) return 0; @@ -681,8 +720,7 @@ int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connec tlv->client |= PP2_CLIENT_SSL; value = ssl_sock_get_version(remote); if (value) { - tlv_len = make_tlv(&buf[ret+ssl_tlv_len], (buf_len-ret-ssl_tlv_len), PP2_TYPE_SSL_VERSION, strlen(value), value); - ssl_tlv_len += tlv_len; + ssl_tlv_len += make_tlv(&buf[ret+ssl_tlv_len], (buf_len-ret-ssl_tlv_len), PP2_TYPE_SSL_VERSION, strlen(value), value); } if (ssl_sock_get_cert_used_sess(remote)) { tlv->client |= PP2_CLIENT_CERT_SESS; @@ -693,8 +731,7 @@ int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connec if (srv->pp_opts & SRV_PP_V2_SSL_CN) { cn_trash = get_trash_chunk(); if (ssl_sock_get_remote_common_name(remote, cn_trash) > 0) { - tlv_len = make_tlv(&buf[ret+ssl_tlv_len], (buf_len - ret - ssl_tlv_len), PP2_TYPE_SSL_CN, cn_trash->len, cn_trash->str); - ssl_tlv_len += tlv_len; + ssl_tlv_len += make_tlv(&buf[ret+ssl_tlv_len], (buf_len - ret - ssl_tlv_len), PP2_TYPE_SSL_CN, cn_trash->len, cn_trash->str); } } } @@ -704,6 +741,14 @@ int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connec } #endif +#ifdef CONFIG_HAP_NS + if (remote && (remote->proxy_netns)) { + if ((buf_len - ret) < sizeof(struct tlv)) + return 0; + ret += make_tlv(&buf[ret], buf_len, PP2_TYPE_NETNS, remote->proxy_netns->name_len, remote->proxy_netns->node.key); + } +#endif + hdr->len = htons((uint16_t)(ret - PP2_HEADER_LEN)); return ret; diff --git a/src/haproxy.c b/src/haproxy.c index 13c3d265e..a3069529a 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -354,6 +355,10 @@ void display_build_opts() #endif "\n"); #endif + +#if defined(CONFIG_HAP_NS) + printf("Built with network namespace support\n"); +#endif putchar('\n'); list_pollers(stdout); @@ -721,6 +726,14 @@ void init(int argc, char **argv) exit(1); } +#ifdef CONFIG_HAP_NS + err_code |= netns_init(); + if (err_code & (ERR_ABORT|ERR_FATAL)) { + Alert("Failed to initialize namespace support.\n"); + exit(1); + } +#endif + if (global.mode & MODE_CHECK) { struct peers *pr; struct proxy *px; diff --git a/src/namespace.c b/src/namespace.c new file mode 100644 index 000000000..a22f1a5c4 --- /dev/null +++ b/src/namespace.c @@ -0,0 +1,114 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef CONFIG_HAP_NS + +/* Opens the namespace 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.str, O_RDONLY); +} + +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.str, O_RDONLY); + return default_namespace; +} + +static struct eb_root namespace_tree_root = EB_ROOT; + +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) { + Alert("Failed to open the default namespace.\n"); + err_code |= ERR_ALERT | ERR_FATAL; + } + } + + 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 = (struct netns_entry *)calloc(1, sizeof(struct netns_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; +} +#endif + +/* Opens a socket in the namespace described by with the parameters , + * and 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; + +#ifdef CONFIG_HAP_NS + if (default_namespace < 0 || + (ns && setns(ns->fd, CLONE_NEWNET) == -1)) + return -1; +#endif + sock = socket(domain, type, protocol); + +#ifdef CONFIG_HAP_NS + if (ns && setns(default_namespace, CLONE_NEWNET) == -1) { + close(sock); + return -1; + } +#endif + + return sock; +} diff --git a/src/proto_tcp.c b/src/proto_tcp.c index cfa62f722..afb9e155a 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -247,6 +248,15 @@ int tcp_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct so return 0; } +static int create_server_socket(struct connection *conn) +{ + const struct netns_entry *ns = objt_server(conn->target)->netns; + + if (objt_server(conn->target)->flags & SRV_F_USE_NS_FROM_PP) + ns = conn->proxy_netns; + + return my_socketat(ns, conn->addr.to.ss_family, SOCK_STREAM, IPPROTO_TCP); +} /* * This function initiates a TCP connection establishment to the target assigned @@ -301,7 +311,9 @@ int tcp_connect_server(struct connection *conn, int data, int delack) return SN_ERR_INTERNAL; } - if ((fd = conn->t.sock.fd = socket(conn->addr.to.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { + fd = conn->t.sock.fd = create_server_socket(conn); + + if (fd == -1) { qfprintf(stderr, "Cannot get a server socket.\n"); if (errno == ENFILE) { @@ -741,10 +753,14 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen) fd = listener->fd; ext = (fd >= 0); - if (!ext && (fd = socket(listener->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { - err |= ERR_RETRYABLE | ERR_ALERT; - msg = "cannot create listening socket"; - goto tcp_return; + if (!ext) { + fd = my_socketat(listener->netns, listener->addr.ss_family, SOCK_STREAM, IPPROTO_TCP); + + if (fd == -1) { + err |= ERR_RETRYABLE | ERR_ALERT; + msg = "cannot create listening socket"; + goto tcp_return; + } } if (fd >= global.maxsock) { @@ -2007,6 +2023,34 @@ static int bind_parse_interface(char **args, int cur_arg, struct proxy *px, stru } #endif +#ifdef CONFIG_HAP_NS +/* parse the "namespace" bind keyword */ +static int bind_parse_namespace(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err) +{ + struct listener *l; + char *namespace = NULL; + + if (!*args[cur_arg + 1]) { + memprintf(err, "'%s' : missing namespace id", args[cur_arg]); + return ERR_ALERT | ERR_FATAL; + } + namespace = args[cur_arg + 1]; + + list_for_each_entry(l, &conf->listeners, by_bind) { + l->netns = netns_store_lookup(namespace, strlen(namespace)); + + if (l->netns == NULL) + l->netns = netns_store_insert(namespace); + + if (l->netns == NULL) { + Alert("Cannot open namespace '%s'.\n", args[cur_arg + 1]); + return ERR_ALERT | ERR_FATAL; + } + } + return 0; +} +#endif + static struct cfg_kw_list cfg_kws = {ILH, { { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req }, { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep }, @@ -2065,6 +2109,9 @@ static struct bind_kw_list bind_kws = { "TCP", { }, { #ifdef IPV6_V6ONLY { "v4v6", bind_parse_v4v6, 0 }, /* force socket to bind to IPv4+IPv6 */ { "v6only", bind_parse_v6only, 0 }, /* force socket to bind to IPv6 only */ +#endif +#ifdef CONFIG_HAP_NS + { "namespace", bind_parse_namespace, 1 }, #endif /* the versions with the NULL parse function*/ { "defer-accept", NULL, 0 }, diff --git a/src/server.c b/src/server.c index 94a31b665..4f9fad859 100644 --- a/src/server.c +++ b/src/server.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -1501,6 +1502,31 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr err_code |= ERR_ALERT | ERR_FATAL; goto out; } + else if (!defsrv && !strcmp(args[cur_arg], "namespace")) { +#ifdef CONFIG_HAP_NS + char *arg = args[cur_arg + 1]; + if (!strcmp(arg, "*")) { + newsrv->flags |= SRV_F_USE_NS_FROM_PP; + } else { + newsrv->netns = netns_store_lookup(arg, strlen(arg)); + + if (newsrv->netns == NULL) + newsrv->netns = netns_store_insert(arg); + + if (newsrv->netns == NULL) { + Alert("Cannot open namespace '%s'.\n", args[cur_arg + 1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } +#else + Alert("parsing [%s:%d] : '%s' : '%s' option not implemented.\n", + file, linenum, args[0], args[cur_arg]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; +#endif + cur_arg += 2; + } else { static int srv_dumped; struct srv_kw *kw; diff --git a/src/session.c b/src/session.c index 675f26df3..0ceb031b9 100644 --- a/src/session.c +++ b/src/session.c @@ -90,6 +90,7 @@ int session_accept(struct listener *l, int cfd, struct sockaddr_storage *addr) cli_conn->addr.from = *addr; cli_conn->flags |= CO_FL_ADDR_FROM_SET; cli_conn->target = &l->obj_type; + cli_conn->proxy_netns = l->netns; if (unlikely((s = pool_alloc2(pool2_session)) == NULL)) goto out_free_conn;