From 7bddc92322b9be78bc99c715a5e6d978ac51a23e Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Mon, 28 Feb 2022 18:20:48 +0100 Subject: [PATCH] [GTP] Support binding socket to device This is useful, among other possible applications, to make use of VRFs [1], in this case for GTP-C and GTP-U traffic in the PGW. The bind_dev field is added to the ogs_socknode_t so that it's easy to extend its use into lots of other sockets being set up based on config file information. [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/networking/vrf.rst --- lib/core/ogs-socket.c | 19 +++++++++++++++++++ lib/core/ogs-socket.h | 1 + lib/core/ogs-socknode.c | 7 ++++++- lib/core/ogs-socknode.h | 4 +++- lib/core/ogs-udp.c | 30 ++++++++++++++++++++---------- lib/gtp/context.c | 35 +++++++++++++++++++++++++++-------- lib/pfcp/context.c | 4 ++-- lib/sbi/context.c | 4 ++-- src/amf/context.c | 4 ++-- src/mme/mme-context.c | 4 ++-- src/nssf/context.c | 2 +- tests/common/context.c | 8 ++++---- tests/core/socket-test.c | 2 +- 13 files changed, 90 insertions(+), 34 deletions(-) diff --git a/lib/core/ogs-socket.c b/lib/core/ogs-socket.c index 774999f52..4477a1db2 100644 --- a/lib/core/ogs-socket.c +++ b/lib/core/ogs-socket.c @@ -331,3 +331,22 @@ int ogs_listen_reusable(ogs_socket_t fd) return OGS_OK; } + +int ogs_bind_to_device(ogs_socket_t fd, const char *device) +{ +#if defined(SO_BINDTODEVICE) && !defined(_WIN32) + int rc; + + ogs_assert(fd != INVALID_SOCKET); + rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1); + if (rc != OGS_OK) { + ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, + "setsockopt(SOL_SOCKET, SO_BINDTODEVICE, %s) failed", + device); + return OGS_ERROR; + } + return OGS_OK; +#else + return OGS_ERROR; +#endif +} diff --git a/lib/core/ogs-socket.h b/lib/core/ogs-socket.h index 8294b3c07..bd1e6f202 100644 --- a/lib/core/ogs-socket.h +++ b/lib/core/ogs-socket.h @@ -74,6 +74,7 @@ int ogs_closesocket(ogs_socket_t fd); int ogs_nonblocking(ogs_socket_t fd); int ogs_closeonexec(ogs_socket_t fd); int ogs_listen_reusable(ogs_socket_t fd); +int ogs_bind_to_device(ogs_socket_t fd, const char *device); #ifdef __cplusplus } diff --git a/lib/core/ogs-socknode.c b/lib/core/ogs-socknode.c index 321a9b6db..663fd81e0 100644 --- a/lib/core/ogs-socknode.c +++ b/lib/core/ogs-socknode.c @@ -51,6 +51,8 @@ void ogs_socknode_free(ogs_socknode_t *node) ogs_assert(node); ogs_freeaddrinfo(node->addr); + if (node->bind_dev) + ogs_free(node->bind_dev); if (node->poll) ogs_pollset_remove(node->poll); if (node->sock) { @@ -101,7 +103,8 @@ void ogs_socknode_remove_all(ogs_list_t *list) } int ogs_socknode_probe( - ogs_list_t *list, ogs_list_t *list6, const char *dev, uint16_t port) + ogs_list_t *list, ogs_list_t *list6, const char *dev, uint16_t port, + const char *bind_device) { #if defined(HAVE_GETIFADDRS) ogs_socknode_t *node = NULL; @@ -164,6 +167,8 @@ int ogs_socknode_probe( node = ogs_calloc(1, sizeof(ogs_socknode_t)); node->addr = addr; + if (bind_device) + node->bind_dev = ogs_strdup(bind_device); if (addr->ogs_sa_family == AF_INET) { ogs_assert(list); diff --git a/lib/core/ogs-socknode.h b/lib/core/ogs-socknode.h index 6bb1cc0a3..196a58929 100644 --- a/lib/core/ogs-socknode.h +++ b/lib/core/ogs-socknode.h @@ -39,6 +39,7 @@ typedef struct ogs_socknode_s { ogs_sock_t *sock; void (*cleanup)(ogs_sock_t *sock); ogs_poll_t *poll; + char *bind_dev; /* !NULL: used with SO_BINDTODEVICE */ } ogs_socknode_t; ogs_socknode_t *ogs_socknode_new(ogs_sockaddr_t *addr); @@ -50,7 +51,8 @@ void ogs_socknode_remove(ogs_list_t *list, ogs_socknode_t *node); void ogs_socknode_remove_all(ogs_list_t *list); int ogs_socknode_probe( - ogs_list_t *list, ogs_list_t *list6, const char *dev, uint16_t port); + ogs_list_t *list, ogs_list_t *list6, const char *dev, uint16_t port, + const char *bind_device); int ogs_socknode_fill_scope_id_in_local(ogs_sockaddr_t *sa_list); void ogs_socknode_set_cleanup( diff --git a/lib/core/ogs-udp.c b/lib/core/ogs-udp.c index 2e6954379..1f645ee25 100644 --- a/lib/core/ogs-udp.c +++ b/lib/core/ogs-udp.c @@ -45,17 +45,27 @@ ogs_sock_t *ogs_udp_server(ogs_socknode_t *node) addr = node->addr; while (addr) { new = ogs_udp_socket(addr->ogs_sa_family, node); - if (new) { - if (ogs_sock_bind(new, addr) == OGS_OK) { - ogs_debug("udp_server() [%s]:%d", - OGS_ADDR(addr, buf), OGS_PORT(addr)); - break; - } - - ogs_sock_destroy(new); + if (!new) { + addr = addr->next; + continue; } - - addr = addr->next; + if (ogs_sock_bind(new, addr) != OGS_OK) { + ogs_sock_destroy(new); + addr = addr->next; + continue; + } + ogs_debug("udp_server() [%s]:%d", + OGS_ADDR(addr, buf), OGS_PORT(addr)); + if(node->bind_dev) { + if (ogs_bind_to_device(new->fd, node->bind_dev) != OGS_OK) { + ogs_sock_destroy(new); + addr = addr->next; + continue; + } + ogs_debug("udp_server() [%s]:%d bound to device %s", + OGS_ADDR(addr, buf), OGS_PORT(addr), node->bind_dev); + } + break; } if (addr == NULL) { diff --git a/lib/gtp/context.c b/lib/gtp/context.c index a90c255dd..1a25ef4ea 100644 --- a/lib/gtp/context.c +++ b/lib/gtp/context.c @@ -103,7 +103,9 @@ int ogs_gtp_context_parse_config(const char *local, const char *remote) const char *hostname[OGS_MAX_NUM_OF_HOSTNAME]; uint16_t port = self.gtpc_port; const char *dev = NULL; + const char *bind_dev = NULL; ogs_sockaddr_t *addr = NULL; + ogs_socknode_t *node = NULL, *node6 = NULL; if (ogs_yaml_iter_type(>pc_array) == YAML_MAPPING_NODE) { @@ -161,6 +163,8 @@ int ogs_gtp_context_parse_config(const char *local, const char *remote) if (v) port = atoi(v); } else if (!strcmp(gtpc_key, "dev")) { dev = ogs_yaml_iter_value(>pc_iter); + } else if (!strcmp(gtpc_key, "bind_dev")) { + bind_dev = ogs_yaml_iter_value(>pc_iter); } else ogs_warn("unknown key `%s`", gtpc_key); } @@ -174,11 +178,17 @@ int ogs_gtp_context_parse_config(const char *local, const char *remote) if (addr) { if (ogs_app()->parameter.no_ipv4 == 0) - ogs_socknode_add( + node = ogs_socknode_add( &self.gtpc_list, AF_INET, addr); if (ogs_app()->parameter.no_ipv6 == 0) - ogs_socknode_add( + node6 = ogs_socknode_add( &self.gtpc_list6, AF_INET6, addr); + if (bind_dev) { + if (node) + node->bind_dev = ogs_strdup(bind_dev); + if (node6) + node6->bind_dev = ogs_strdup(bind_dev); + } ogs_freeaddrinfo(addr); } @@ -188,7 +198,7 @@ int ogs_gtp_context_parse_config(const char *local, const char *remote) NULL : &self.gtpc_list, ogs_app()->parameter.no_ipv6 ? NULL : &self.gtpc_list6, - dev, port); + dev, port, bind_dev); ogs_assert(rv == OGS_OK); } @@ -202,7 +212,7 @@ int ogs_gtp_context_parse_config(const char *local, const char *remote) NULL : &self.gtpc_list, ogs_app()->parameter.no_ipv6 ? NULL : &self.gtpc_list6, - NULL, self.gtpc_port); + NULL, self.gtpc_port, NULL); ogs_assert(rv == OGS_OK); } } else if (!strcmp(local_key, "gtpu")) { @@ -228,6 +238,7 @@ int ogs_gtp_context_parse_config(const char *local, const char *remote) const char *teid_range = NULL; const char *network_instance = NULL; const char *source_interface = NULL; + const char *bind_dev = NULL; if (ogs_yaml_iter_type(>pu_array) == YAML_MAPPING_NODE) { @@ -317,6 +328,8 @@ int ogs_gtp_context_parse_config(const char *local, const char *remote) if (v) port = atoi(v); } else if (!strcmp(gtpu_key, "dev")) { dev = ogs_yaml_iter_value(>pu_iter); + } else if (!strcmp(gtpu_key, "bind_dev")) { + bind_dev = ogs_yaml_iter_value(>pu_iter); } else if (!strcmp(gtpu_key, "teid_range_indication")) { teid_range_indication = @@ -348,9 +361,15 @@ int ogs_gtp_context_parse_config(const char *local, const char *remote) if (addr) { if (ogs_app()->parameter.no_ipv4 == 0) - ogs_socknode_add(&list, AF_INET, addr); + node = ogs_socknode_add(&list, AF_INET, addr); if (ogs_app()->parameter.no_ipv6 == 0) - ogs_socknode_add(&list6, AF_INET6, addr); + node6 = ogs_socknode_add(&list6, AF_INET6, addr); + if (bind_dev) { + if (node) + node->bind_dev = ogs_strdup(bind_dev); + if (node6) + node6->bind_dev = ogs_strdup(bind_dev); + } ogs_freeaddrinfo(addr); } @@ -358,7 +377,7 @@ int ogs_gtp_context_parse_config(const char *local, const char *remote) rv = ogs_socknode_probe( ogs_app()->parameter.no_ipv4 ? NULL : &list, ogs_app()->parameter.no_ipv6 ? NULL : &list6, - dev, port); + dev, port, bind_dev); ogs_assert(rv == OGS_OK); } @@ -454,7 +473,7 @@ int ogs_gtp_context_parse_config(const char *local, const char *remote) rv = ogs_socknode_probe( ogs_app()->parameter.no_ipv4 ? NULL : &list, ogs_app()->parameter.no_ipv6 ? NULL : &list6, - NULL, self.gtpu_port); + NULL, self.gtpu_port, NULL); ogs_assert(rv == OGS_OK); /* diff --git a/lib/pfcp/context.c b/lib/pfcp/context.c index 9fbc6ab4f..c3e59604c 100644 --- a/lib/pfcp/context.c +++ b/lib/pfcp/context.c @@ -274,7 +274,7 @@ int ogs_pfcp_context_parse_config(const char *local, const char *remote) NULL : &self.pfcp_list, ogs_app()->parameter.no_ipv6 ? NULL : &self.pfcp_list6, - dev, self.pfcp_port); + dev, self.pfcp_port, NULL); ogs_assert(rv == OGS_OK); } @@ -288,7 +288,7 @@ int ogs_pfcp_context_parse_config(const char *local, const char *remote) NULL : &self.pfcp_list, ogs_app()->parameter.no_ipv6 ? NULL : &self.pfcp_list6, - NULL, self.pfcp_port); + NULL, self.pfcp_port, NULL); ogs_assert(rv == OGS_OK); } } else if (!strcmp(local_key, "subnet")) { diff --git a/lib/sbi/context.c b/lib/sbi/context.c index 16d20d0ea..0959b7634 100644 --- a/lib/sbi/context.c +++ b/lib/sbi/context.c @@ -280,7 +280,7 @@ int ogs_sbi_context_parse_config(const char *local, const char *remote) rv = ogs_socknode_probe( ogs_app()->parameter.no_ipv4 ? NULL : &list, ogs_app()->parameter.no_ipv6 ? NULL : &list6, - dev, port); + dev, port, NULL); ogs_assert(rv == OGS_OK); } @@ -334,7 +334,7 @@ int ogs_sbi_context_parse_config(const char *local, const char *remote) rv = ogs_socknode_probe( ogs_app()->parameter.no_ipv4 ? NULL : &list, ogs_app()->parameter.no_ipv6 ? NULL : &list6, - NULL, self.sbi_port); + NULL, self.sbi_port, NULL); ogs_assert(rv == OGS_OK); node = ogs_list_first(&list); diff --git a/src/amf/context.c b/src/amf/context.c index a4f982c3d..280df1b73 100644 --- a/src/amf/context.c +++ b/src/amf/context.c @@ -296,7 +296,7 @@ int amf_context_parse_config(void) NULL : &self.ngap_list, ogs_app()->parameter.no_ipv6 ? NULL : &self.ngap_list6, - dev, port); + dev, port, NULL); ogs_assert(rv == OGS_OK); } @@ -310,7 +310,7 @@ int amf_context_parse_config(void) NULL : &self.ngap_list, ogs_app()->parameter.no_ipv6 ? NULL : &self.ngap_list6, - NULL, self.ngap_port); + NULL, self.ngap_port, NULL); ogs_assert(rv == OGS_OK); } } else if (!strcmp(amf_key, "guami")) { diff --git a/src/mme/mme-context.c b/src/mme/mme-context.c index 6be71cd24..0de3075a1 100644 --- a/src/mme/mme-context.c +++ b/src/mme/mme-context.c @@ -506,7 +506,7 @@ int mme_context_parse_config() NULL : &self.s1ap_list, ogs_app()->parameter.no_ipv6 ? NULL : &self.s1ap_list6, - dev, port); + dev, port, NULL); ogs_assert(rv == OGS_OK); } @@ -520,7 +520,7 @@ int mme_context_parse_config() NULL : &self.s1ap_list, ogs_app()->parameter.no_ipv6 ? NULL : &self.s1ap_list6, - NULL, self.s1ap_port); + NULL, self.s1ap_port, NULL); ogs_assert(rv == OGS_OK); } } else if (!strcmp(mme_key, "gtpc")) { diff --git a/src/nssf/context.c b/src/nssf/context.c index 7dac31af9..8c7658275 100644 --- a/src/nssf/context.c +++ b/src/nssf/context.c @@ -233,7 +233,7 @@ int nssf_context_parse_config(void) rv = ogs_socknode_probe( ogs_app()->parameter.no_ipv4 ? NULL : &list, ogs_app()->parameter.no_ipv6 ? NULL : &list6, - dev, port); + dev, port, NULL); ogs_assert(rv == OGS_OK); } diff --git a/tests/common/context.c b/tests/common/context.c index 3fe9aab13..9178f94ce 100644 --- a/tests/common/context.c +++ b/tests/common/context.c @@ -244,7 +244,7 @@ int test_context_parse_config(void) NULL : &self.ngap_list, ogs_app()->parameter.no_ipv6 ? NULL : &self.ngap_list6, - dev, port); + dev, port, NULL); ogs_assert(rv == OGS_OK); } @@ -258,7 +258,7 @@ int test_context_parse_config(void) NULL : &self.ngap_list, ogs_app()->parameter.no_ipv6 ? NULL : &self.ngap_list6, - NULL, self.ngap_port); + NULL, self.ngap_port, NULL); ogs_assert(rv == OGS_OK); } } if (!strcmp(amf_key, "tai")) { @@ -628,7 +628,7 @@ int test_context_parse_config(void) NULL : &self.s1ap_list, ogs_app()->parameter.no_ipv6 ? NULL : &self.s1ap_list6, - dev, port); + dev, port, NULL); ogs_assert(rv == OGS_OK); } @@ -642,7 +642,7 @@ int test_context_parse_config(void) NULL : &self.s1ap_list, ogs_app()->parameter.no_ipv6 ? NULL : &self.s1ap_list6, - NULL, self.s1ap_port); + NULL, self.s1ap_port, NULL); ogs_assert(rv == OGS_OK); } } else if (!strcmp(mme_key, "tai")) { diff --git a/tests/core/socket-test.c b/tests/core/socket-test.c index 60be7234c..10ea7a0b8 100644 --- a/tests/core/socket-test.c +++ b/tests/core/socket-test.c @@ -396,7 +396,7 @@ static void test7_func(abts_case *tc, void *data) ogs_socknode_remove_all(&list); - rv = ogs_socknode_probe(&list, &list6, NULL, PORT); + rv = ogs_socknode_probe(&list, &list6, NULL, PORT, NULL); ABTS_INT_EQUAL(tc, OGS_OK, rv); ogs_socknode_remove_all(&list);