[SCTP] Support setting local address (#3344)

Added support for binding to local IP addresses in ogs_sctp_client and
ogs_sctp_server, and correct SGsAP configuration

Implemented the ability to bind to one or multiple local IP addresses using
`sctp_bindx()` in both the `ogs_sctp_client()` and `ogs_sctp_server()` APIs.

Users can now specify local addresses in the configuration files under the new
`local_addresses` field, reducing unnecessary complexity and signaling caused
by binding to `ANY_ADDR`.

This update addresses issue https://osmocom.org/issues/6509 by ensuring
correct operation in multi-interface and complex networking setups.

Additionally, corrected the `sgsap` configuration by changing it
from `server` to `client`, and added support for specifying `local_addresses`
for local binding as follows:

```
sgsap:
  client:
    - address: msc.open5gs.org # SCTP server address configured on the MSC/VL
      local_address: 127.0.0.2 # SCTP local IP addresses to be bound in the M
```
This commit is contained in:
Sukchan Lee
2024-12-23 21:08:13 +09:00
parent b44d159c7b
commit b0bfd35c63
10 changed files with 427 additions and 153 deletions

View File

@@ -59,7 +59,7 @@ mme:
smf: smf:
- address: 127.0.0.4 - address: 127.0.0.4
sgsap: sgsap:
server: client:
- address: 127.0.0.2 - address: 127.0.0.2
map: map:
tai: tai:

View File

@@ -164,8 +164,9 @@ mme:
################################################################################ ################################################################################
# o MSC/VLR # o MSC/VLR
# sgsap: # sgsap:
# server: # client:
# - address: 127.0.0.2 # - address: msc.open5gs.org # SCTP server address configured on the MSC/VLR
# local_address: 127.0.0.2 # SCTP local IP addresses to be bound in the MME
# map: # map:
# tai: # tai:
# plmn_id: # plmn_id:
@@ -188,7 +189,15 @@ mme:
# mcc: 002 # mcc: 002
# mnc: 02 # mnc: 02
# lac: 43692 # lac: 43692
# - address: msc.open5gs.org # - address: # SCTP server address configured on the MSC/VLR
# - 127.0.0.88
# - 10.0.0.88
# - 172.16.0.88
# - 2001:db8:babe::88
# local_address: # SCTP local IP addresses to be bound in the MME
# - 127.0.0.2
# - 192.168.1.4
# - 2001:db8:cafe::2
# map: # map:
# tai: # tai:
# plmn_id: # plmn_id:

View File

@@ -53,138 +53,332 @@ ogs_sock_t *ogs_sctp_socket(int family, int type)
return new; return new;
} }
ogs_sock_t *ogs_sctp_server( /**
int type, ogs_sockaddr_t *sa_list, ogs_sockopt_t *socket_option) * @brief
* 1) Count the number of addresses in sa_list and determine the total
* buffer size.
* 2) Allocate a single continuous buffer (unsigned char).
* 3) Copy each address (sockaddr_in or sockaddr_in6) into this continuous
* buffer.
*
* @param sa_list Linked list of ogs_sockaddr_t structures.
* @param out_count [OUT] Receives the number of addresses.
* @param out_total_len [OUT] Receives the total bytes for the continuous
* buffer.
*
* @return
* On success, returns a pointer to the allocated buffer containing all
* addresses. On failure, logs an error and returns NULL.
*/
static unsigned char *create_continuous_address_buffer(
ogs_sockaddr_t *sa_list,
int *out_count,
int *out_total_len)
{ {
int rv;
char buf[OGS_ADDRSTRLEN];
ogs_sock_t *new = NULL;
ogs_sockaddr_t *addr; ogs_sockaddr_t *addr;
ogs_sockopt_t option; int addr_count = 0;
int total_len = 0;
unsigned char *addr_buf = NULL;
int offset = 0;
ogs_assert(sa_list); /* 1) Count addresses and total buffer size needed. */
for (addr = sa_list; addr; addr = addr->next) {
ogs_sockopt_init(&option); addr_count++;
if (socket_option) /* E.g., sizeof(sockaddr_in) or sizeof(sockaddr_in6). */
memcpy(&option, socket_option, sizeof option); total_len += ogs_sockaddr_len(addr);
addr = sa_list;
while (addr) {
new = ogs_sctp_socket(addr->ogs_sa_family, type);
if (new) {
rv = ogs_sctp_peer_addr_params(new, &option);
ogs_assert(rv == OGS_OK);
rv = ogs_sctp_rto_info(new, &option);
ogs_assert(rv == OGS_OK);
rv = ogs_sctp_initmsg(new, &option);
ogs_assert(rv == OGS_OK);
if (option.sctp_nodelay == true) {
rv = ogs_sctp_nodelay(new, true);
ogs_assert(rv == OGS_OK);
} else
ogs_warn("SCTP NO_DELAY Disabled");
if (option.so_linger.l_onoff == true) {
rv = ogs_sctp_so_linger(new, option.so_linger.l_linger);
ogs_assert(rv == OGS_OK);
}
rv = ogs_listen_reusable(new->fd, true);
ogs_assert(rv == OGS_OK);
if (ogs_sock_bind(new, addr) == OGS_OK) {
ogs_debug("sctp_server() [%s]:%d",
OGS_ADDR(addr, buf), OGS_PORT(addr));
break;
}
ogs_sock_destroy(new);
}
addr = addr->next;
} }
if (addr == NULL) { if (addr_count == 0) {
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, ogs_error("No valid address in sa_list");
"sctp_server() [%s]:%d failed",
OGS_ADDR(sa_list, buf), OGS_PORT(sa_list));
return NULL; return NULL;
} }
ogs_assert(new); /* 2) Allocate the continuous buffer (unsigned char). */
addr_buf = ogs_calloc(1, total_len);
if (!addr_buf) {
ogs_error("Failed to allocate memory for addr_buf");
return NULL;
}
rv = ogs_sock_listen(new); /* 3) Copy each address structure into addr_buf. */
offset = 0;
for (addr = sa_list; addr; addr = addr->next) {
socklen_t socklen = ogs_sockaddr_len(addr);
memcpy(addr_buf + offset, &addr->sa, socklen);
offset += socklen;
}
/* Pass back the number of addresses and total length. */
*out_count = addr_count;
*out_total_len = total_len;
return addr_buf;
}
/**
* @brief Create an SCTP server socket and bind multiple addresses at once
* using sctp_bindx().
*
* @param type SCTP socket type (e.g., SOCK_SEQPACKET or SOCK_STREAM)
* @param sa_list Linked list of ogs_sockaddr_t structures
* @param socket_option Additional socket/SCTP options
*
* @return
* On success, returns a pointer to an ogs_sock_t instance; on failure,
* returns NULL.
*/
ogs_sock_t *ogs_sctp_server(
int type,
ogs_sockaddr_t *sa_list,
ogs_sockopt_t *socket_option)
{
int rv;
char buf[OGS_ADDRSTRLEN];
ogs_sock_t *new_sock = NULL;
ogs_sockopt_t option;
/* Variables for sctp_bindx() usage. */
unsigned char *addr_buf = NULL;
int addr_count = 0;
int total_len = 0;
ogs_assert(sa_list);
/* Initialize socket options. */
ogs_sockopt_init(&option);
if (socket_option)
memcpy(&option, socket_option, sizeof(option));
/*
* Obtain a contiguous buffer for all addresses:
* 1) Count the addresses.
* 2) Allocate the buffer.
* 3) Copy the addresses into the buffer.
*/
addr_buf = create_continuous_address_buffer(
sa_list, &addr_count, &total_len);
if (!addr_buf) {
/* The helper logs errors, so just return. */
return NULL;
}
/*
* Create an SCTP socket using the family of the first address
* in sa_list.
*/
new_sock = ogs_sctp_socket(sa_list->ogs_sa_family, type);
if (!new_sock) {
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"sctp_server() Failed to create SCTP socket");
goto err;
}
/* Configure SCTP-specific options. */
rv = ogs_sctp_peer_addr_params(new_sock, &option);
ogs_assert(rv == OGS_OK); ogs_assert(rv == OGS_OK);
return new; rv = ogs_sctp_rto_info(new_sock, &option);
ogs_assert(rv == OGS_OK);
rv = ogs_sctp_initmsg(new_sock, &option);
ogs_assert(rv == OGS_OK);
if (option.sctp_nodelay == true) {
rv = ogs_sctp_nodelay(new_sock, true);
ogs_assert(rv == OGS_OK);
} else {
ogs_warn("SCTP NO_DELAY Disabled");
}
if (option.so_linger.l_onoff == true) {
rv = ogs_sctp_so_linger(new_sock, option.so_linger.l_linger);
ogs_assert(rv == OGS_OK);
}
/* Enable address reuse if needed. */
rv = ogs_listen_reusable(new_sock->fd, true);
ogs_assert(rv == OGS_OK);
/*
* Bind all addresses at once using sctp_bindx().
* (struct sockaddr *)addr_buf points to the contiguous buffer.
*/
rv = sctp_bindx(new_sock->fd, (struct sockaddr *)addr_buf,
addr_count, SCTP_BINDX_ADD_ADDR);
if (rv < 0) {
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"sctp_bindx() failed to bind multiple addresses");
goto err;
}
/*
* Log debug info: only the first address is shown here as an example.
*/
ogs_debug("sctp_server() [%s]:%d (bound %d addresses)",
OGS_ADDR(sa_list, buf), OGS_PORT(sa_list), addr_count);
/* Start listening for connections. */
rv = ogs_sock_listen(new_sock);
ogs_assert(rv == OGS_OK);
/* Success: free the buffer and return the socket. */
ogs_free(addr_buf);
return new_sock;
err:
if (addr_buf)
ogs_free(addr_buf);
if (new_sock)
ogs_sock_destroy(new_sock);
/*
* On failure, log an error based on the first address
* in sa_list (customize as needed).
*/
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"sctp_server() [%s]:%d failed",
OGS_ADDR(sa_list, buf), OGS_PORT(sa_list));
return NULL;
} }
ogs_sock_t *ogs_sctp_client( ogs_sock_t *ogs_sctp_client(
int type, ogs_sockaddr_t *sa_list, ogs_sockopt_t *socket_option) int type,
ogs_sockaddr_t *sa_list,
ogs_sockaddr_t *local_sa_list,
ogs_sockopt_t *socket_option)
{ {
int rv; int rv;
char buf[OGS_ADDRSTRLEN]; char buf[OGS_ADDRSTRLEN];
ogs_sock_t *new_sock = NULL;
ogs_sock_t *new = NULL;
ogs_sockaddr_t *addr;
ogs_sockopt_t option; ogs_sockopt_t option;
/* Buffers and counters for remote addresses. */
unsigned char *remote_buf = NULL;
int remote_count = 0;
int remote_len = 0;
/* Buffers and counters for local addresses (if provided). */
unsigned char *local_buf = NULL;
int local_count = 0;
int local_len = 0;
ogs_assert(sa_list); ogs_assert(sa_list);
/* Initialize socket options and copy user-provided options if present. */
ogs_sockopt_init(&option); ogs_sockopt_init(&option);
if (socket_option) if (socket_option)
memcpy(&option, socket_option, sizeof option); memcpy(&option, socket_option, sizeof(option));
addr = sa_list; /*
while (addr) { * Build the contiguous buffer for REMOTE addresses using our helper
new = ogs_sctp_socket(addr->ogs_sa_family, type); * function. This will be used later by sctp_connectx().
if (new) { */
rv = ogs_sctp_peer_addr_params(new, &option); remote_buf = create_continuous_address_buffer(
ogs_assert(rv == OGS_OK); sa_list, &remote_count, &remote_len);
if (!remote_buf) {
rv = ogs_sctp_rto_info(new, &option); /* Logs and returns NULL on failure. */
ogs_assert(rv == OGS_OK);
rv = ogs_sctp_initmsg(new, &option);
ogs_assert(rv == OGS_OK);
if (option.sctp_nodelay == true) {
rv = ogs_sctp_nodelay(new, true);
ogs_assert(rv == OGS_OK);
} else
ogs_warn("SCTP NO_DELAY Disabled");
if (option.so_linger.l_onoff == true) {
rv = ogs_sctp_so_linger(new, option.so_linger.l_linger);
ogs_assert(rv == OGS_OK);
}
if (ogs_sock_connect(new, addr) == OGS_OK) {
ogs_debug("sctp_client() [%s]:%d",
OGS_ADDR(addr, buf), OGS_PORT(addr));
break;
}
ogs_sock_destroy(new);
}
addr = addr->next;
}
if (addr == NULL) {
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"sctp_client() [%s]:%d failed",
OGS_ADDR(sa_list, buf), OGS_PORT(sa_list));
return NULL; return NULL;
} }
ogs_assert(new); /*
* Create the SCTP socket using the address family of the first remote
* address.
*/
new_sock = ogs_sctp_socket(sa_list->ogs_sa_family, type);
if (!new_sock) {
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"sctp_client() Failed to create SCTP socket");
goto err;
}
return new; /* Configure SCTP-specific options. */
rv = ogs_sctp_peer_addr_params(new_sock, &option);
ogs_assert(rv == OGS_OK);
rv = ogs_sctp_rto_info(new_sock, &option);
ogs_assert(rv == OGS_OK);
rv = ogs_sctp_initmsg(new_sock, &option);
ogs_assert(rv == OGS_OK);
if (option.sctp_nodelay == true) {
rv = ogs_sctp_nodelay(new_sock, true);
ogs_assert(rv == OGS_OK);
} else {
ogs_warn("SCTP NO_DELAY Disabled");
}
if (option.so_linger.l_onoff == true) {
rv = ogs_sctp_so_linger(new_sock, option.so_linger.l_linger);
ogs_assert(rv == OGS_OK);
}
/*
* If local_sa_list is provided, bind those addresses before connecting.
* (Optional: some clients do not need explicit local bind.)
*/
if (local_sa_list) {
local_buf = create_continuous_address_buffer(
local_sa_list, &local_count, &local_len);
if (!local_buf) {
/* Error already logged. */
goto err;
}
/* We can bind them using sctp_bindx() if desired. */
rv = sctp_bindx(new_sock->fd,
(struct sockaddr *)local_buf,
local_count,
SCTP_BINDX_ADD_ADDR);
if (rv < 0) {
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"sctp_client() bind local addresses failed");
goto err;
}
ogs_debug("sctp_client() bound %d local addresses", local_count);
}
/*
* Connect to the REMOTE addresses using sctp_connectx().
* (struct sockaddr *)remote_buf is the contiguous buffer.
*/
rv = sctp_connectx(new_sock->fd,
(struct sockaddr *)remote_buf,
remote_count,
NULL /* assoc_id */);
if (rv < 0) {
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"sctp_connectx() failed to connect");
goto err;
}
/* Debug log for the first remote address. */
ogs_debug("sctp_client() connected to [%s]:%d",
OGS_ADDR(sa_list, buf), OGS_PORT(sa_list));
/* Success: free buffers and return the new socket. */
if (local_buf)
ogs_free(local_buf);
if (remote_buf)
ogs_free(remote_buf);
return new_sock;
err:
if (local_buf)
ogs_free(local_buf);
if (remote_buf)
ogs_free(remote_buf);
if (new_sock)
ogs_sock_destroy(new_sock);
/*
* On failure, log an error based on the first remote address.
* Adjust to your needs, e.g., log local too if necessary.
*/
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"sctp_client() [%s]:%d failed",
OGS_ADDR(sa_list, buf),
OGS_PORT(sa_list));
return NULL;
} }
int ogs_sctp_connect(ogs_sock_t *sock, ogs_sockaddr_t *sa_list) int ogs_sctp_connect(ogs_sock_t *sock, ogs_sockaddr_t *sa_list)

View File

@@ -109,7 +109,9 @@ ogs_sock_t *ogs_sctp_socket(int family, int type);
ogs_sock_t *ogs_sctp_server( ogs_sock_t *ogs_sctp_server(
int type, ogs_sockaddr_t *sa_list, ogs_sockopt_t *socket_option); int type, ogs_sockaddr_t *sa_list, ogs_sockopt_t *socket_option);
ogs_sock_t *ogs_sctp_client( ogs_sock_t *ogs_sctp_client(
int type, ogs_sockaddr_t *sa_list, ogs_sockopt_t *socket_option); int type,
ogs_sockaddr_t *sa_list, ogs_sockaddr_t *local_sa_list,
ogs_sockopt_t *socket_option);
int ogs_sctp_bind(ogs_sock_t *sock, ogs_sockaddr_t *sa_list); int ogs_sctp_bind(ogs_sock_t *sock, ogs_sockaddr_t *sa_list);
int ogs_sctp_connect(ogs_sock_t *sock, ogs_sockaddr_t *sa_list); int ogs_sctp_connect(ogs_sock_t *sock, ogs_sockaddr_t *sa_list);

View File

@@ -156,7 +156,9 @@ ogs_sock_t *ogs_sctp_server(
} }
ogs_sock_t *ogs_sctp_client( ogs_sock_t *ogs_sctp_client(
int type, ogs_sockaddr_t *sa_list, ogs_sockopt_t *socket_option) int type,
ogs_sockaddr_t *sa_list, ogs_sockaddr_t *local_sa_list,
ogs_sockopt_t *socket_option)
{ {
int rv; int rv;
char buf[OGS_ADDRSTRLEN]; char buf[OGS_ADDRSTRLEN];

View File

@@ -2050,9 +2050,9 @@ int mme_context_parse_config(void)
while (ogs_yaml_iter_next(&sgsap_iter)) { while (ogs_yaml_iter_next(&sgsap_iter)) {
const char *sgsap_key = ogs_yaml_iter_key(&sgsap_iter); const char *sgsap_key = ogs_yaml_iter_key(&sgsap_iter);
ogs_assert(sgsap_key); ogs_assert(sgsap_key);
if (!strcmp(sgsap_key, "server")) { if (!strcmp(sgsap_key, "client")) {
ogs_yaml_iter_t server_iter, server_array; ogs_yaml_iter_t client_iter, client_array;
ogs_yaml_iter_recurse(&sgsap_iter, &server_array); ogs_yaml_iter_recurse(&sgsap_iter, &client_array);
do { do {
mme_vlr_t *vlr = NULL; mme_vlr_t *vlr = NULL;
ogs_plmn_id_t plmn_id; ogs_plmn_id_t plmn_id;
@@ -2063,38 +2063,39 @@ int mme_context_parse_config(void)
const char *tac, *lac; const char *tac, *lac;
} map[MAX_NUM_OF_CSMAP]; } map[MAX_NUM_OF_CSMAP];
int map_num = 0; int map_num = 0;
ogs_sockaddr_t *addr = NULL; ogs_sockaddr_t *addr = NULL, *local_addr = NULL;
int family = AF_UNSPEC; int family = AF_UNSPEC;
int i, hostname_num = 0; int i, hostname_num = 0, local_hostname_num = 0;
const char *hostname[OGS_MAX_NUM_OF_HOSTNAME]; const char *hostname[OGS_MAX_NUM_OF_HOSTNAME],
*local_hostname[OGS_MAX_NUM_OF_HOSTNAME];
uint16_t port = self.sgsap_port; uint16_t port = self.sgsap_port;
ogs_sockopt_t option; ogs_sockopt_t option;
bool is_option = false; bool is_option = false;
if (ogs_yaml_iter_type(&server_array) == if (ogs_yaml_iter_type(&client_array) ==
YAML_MAPPING_NODE) { YAML_MAPPING_NODE) {
memcpy(&server_iter, &server_array, memcpy(&client_iter, &client_array,
sizeof(ogs_yaml_iter_t)); sizeof(ogs_yaml_iter_t));
} else if (ogs_yaml_iter_type(&server_array) == } else if (ogs_yaml_iter_type(&client_array) ==
YAML_SEQUENCE_NODE) { YAML_SEQUENCE_NODE) {
if (!ogs_yaml_iter_next(&server_array)) if (!ogs_yaml_iter_next(&client_array))
break; break;
ogs_yaml_iter_recurse( ogs_yaml_iter_recurse(
&server_array, &server_iter); &client_array, &client_iter);
} else if (ogs_yaml_iter_type(&server_array) == } else if (ogs_yaml_iter_type(&client_array) ==
YAML_SCALAR_NODE) { YAML_SCALAR_NODE) {
break; break;
} else } else
ogs_assert_if_reached(); ogs_assert_if_reached();
while (ogs_yaml_iter_next(&server_iter)) { while (ogs_yaml_iter_next(&client_iter)) {
const char *server_key = const char *client_key =
ogs_yaml_iter_key(&server_iter); ogs_yaml_iter_key(&client_iter);
ogs_assert(server_key); ogs_assert(client_key);
if (!strcmp(server_key, "family")) { if (!strcmp(client_key, "family")) {
const char *v = const char *v =
ogs_yaml_iter_value(&server_iter); ogs_yaml_iter_value(&client_iter);
if (v) family = atoi(v); if (v) family = atoi(v);
if (family != AF_UNSPEC && if (family != AF_UNSPEC &&
family != AF_INET && family != AF_INET &&
@@ -2106,9 +2107,9 @@ int mme_context_parse_config(void)
AF_UNSPEC, AF_INET, AF_INET6); AF_UNSPEC, AF_INET, AF_INET6);
family = AF_UNSPEC; family = AF_UNSPEC;
} }
} else if (!strcmp(server_key, "address")) { } else if (!strcmp(client_key, "address")) {
ogs_yaml_iter_t hostname_iter; ogs_yaml_iter_t hostname_iter;
ogs_yaml_iter_recurse(&server_iter, ogs_yaml_iter_recurse(&client_iter,
&hostname_iter); &hostname_iter);
ogs_assert(ogs_yaml_iter_type( ogs_assert(ogs_yaml_iter_type(
&hostname_iter) != &hostname_iter) !=
@@ -2131,26 +2132,53 @@ int mme_context_parse_config(void)
} while (ogs_yaml_iter_type( } while (ogs_yaml_iter_type(
&hostname_iter) == &hostname_iter) ==
YAML_SEQUENCE_NODE); YAML_SEQUENCE_NODE);
} else if (!strcmp(server_key, "port")) { } else if (!strcmp(client_key,
"local_address")) {
ogs_yaml_iter_t local_hostname_iter;
ogs_yaml_iter_recurse(&client_iter,
&local_hostname_iter);
ogs_assert(ogs_yaml_iter_type(
&local_hostname_iter) !=
YAML_MAPPING_NODE);
do {
if (ogs_yaml_iter_type(
&local_hostname_iter) ==
YAML_SEQUENCE_NODE) {
if (!ogs_yaml_iter_next(
&local_hostname_iter))
break;
}
ogs_assert(local_hostname_num <
OGS_MAX_NUM_OF_HOSTNAME);
local_hostname
[local_hostname_num++] =
ogs_yaml_iter_value(
&local_hostname_iter);
} while (ogs_yaml_iter_type(
&local_hostname_iter) ==
YAML_SEQUENCE_NODE);
} else if (!strcmp(client_key, "port")) {
const char *v = const char *v =
ogs_yaml_iter_value(&server_iter); ogs_yaml_iter_value(&client_iter);
if (v) { if (v) {
port = atoi(v); port = atoi(v);
self.sgsap_port = port; self.sgsap_port = port;
} }
} else if (!strcmp(server_key, "option")) { } else if (!strcmp(client_key, "option")) {
rv = ogs_app_parse_sockopt_config( rv = ogs_app_parse_sockopt_config(
&server_iter, &option); &client_iter, &option);
if (rv != OGS_OK) { if (rv != OGS_OK) {
ogs_error("ogs_app_parse_sockopt_" ogs_error("ogs_app_parse_sockopt_"
"config() failed"); "config() failed");
return rv; return rv;
} }
is_option = true; is_option = true;
} else if (!strcmp(server_key, "map")) { } else if (!strcmp(client_key, "map")) {
ogs_yaml_iter_t map_iter; ogs_yaml_iter_t map_iter;
ogs_yaml_iter_recurse( ogs_yaml_iter_recurse(
&server_iter, &map_iter); &client_iter, &map_iter);
map[map_num].tai_mcc = NULL; map[map_num].tai_mcc = NULL;
map[map_num].tai_mnc = NULL; map[map_num].tai_mnc = NULL;
@@ -2320,13 +2348,13 @@ int mme_context_parse_config(void)
map_num++; map_num++;
} else if (!strcmp(server_key, "tai")) { } else if (!strcmp(client_key, "tai")) {
ogs_error( ogs_error(
"tai/lai configuraton changed to " "tai/lai configuraton changed to "
"map.tai/map.lai"); "map.tai/map.lai");
ogs_log_print(OGS_LOG_ERROR, ogs_log_print(OGS_LOG_ERROR,
"sgsap:\n" "sgsap:\n"
" server\n" " client\n"
" address: 127.0.0.2\n" " address: 127.0.0.2\n"
" map:\n" " map:\n"
" tai:\n" " tai:\n"
@@ -2340,13 +2368,13 @@ int mme_context_parse_config(void)
" mnc: 01\n" " mnc: 01\n"
" lac: 43691\n"); " lac: 43691\n");
return OGS_ERROR; return OGS_ERROR;
} else if (!strcmp(server_key, "lai")) { } else if (!strcmp(client_key, "lai")) {
ogs_error( ogs_error(
"tai/lai configuraton changed to " "tai/lai configuraton changed to "
"map.tai/map.lai"); "map.tai/map.lai");
ogs_log_print(OGS_LOG_ERROR, ogs_log_print(OGS_LOG_ERROR,
"sgsap:\n" "sgsap:\n"
" server\n" " client\n"
" address: 127.0.0.2\n" " address: 127.0.0.2\n"
" map:\n" " map:\n"
" tai:\n" " tai:\n"
@@ -2362,7 +2390,7 @@ int mme_context_parse_config(void)
return OGS_ERROR; return OGS_ERROR;
} else } else
ogs_warn("unknown key `%s`", ogs_warn("unknown key `%s`",
server_key); client_key);
} }
@@ -2386,7 +2414,20 @@ int mme_context_parse_config(void)
if (addr == NULL) continue; if (addr == NULL) continue;
vlr = mme_vlr_add(addr, local_addr = NULL;
for (i = 0; i < local_hostname_num; i++) {
rv = ogs_addaddrinfo(&local_addr,
family, local_hostname[i], port, 0);
ogs_assert(rv == OGS_OK);
}
ogs_filter_ip_version(&local_addr,
ogs_global_conf()->parameter.no_ipv4,
ogs_global_conf()->parameter.no_ipv6,
ogs_global_conf()->parameter.
prefer_ipv4);
vlr = mme_vlr_add(addr, local_addr,
is_option ? &option : NULL); is_option ? &option : NULL);
ogs_assert(vlr); ogs_assert(vlr);
@@ -2409,7 +2450,7 @@ int mme_context_parse_config(void)
&csmap->lai.nas_plmn_id, &plmn_id); &csmap->lai.nas_plmn_id, &plmn_id);
csmap->lai.lac = atoi(map[i].lac); csmap->lai.lac = atoi(map[i].lac);
} }
} while (ogs_yaml_iter_type(&server_array) == } while (ogs_yaml_iter_type(&client_array) ==
YAML_SEQUENCE_NODE); YAML_SEQUENCE_NODE);
} else } else
ogs_warn("unknown key `%s`", sgsap_key); ogs_warn("unknown key `%s`", sgsap_key);
@@ -2737,7 +2778,10 @@ ogs_sockaddr_t *mme_pgw_addr_find_by_apn_enb(
return NULL; return NULL;
} }
mme_vlr_t *mme_vlr_add(ogs_sockaddr_t *sa_list, ogs_sockopt_t *option) mme_vlr_t *mme_vlr_add(
ogs_sockaddr_t *sa_list,
ogs_sockaddr_t *local_sa_list,
ogs_sockopt_t *option)
{ {
mme_vlr_t *vlr = NULL; mme_vlr_t *vlr = NULL;
@@ -2751,6 +2795,7 @@ mme_vlr_t *mme_vlr_add(ogs_sockaddr_t *sa_list, ogs_sockopt_t *option)
vlr->ostream_id = 0; vlr->ostream_id = 0;
vlr->sa_list = sa_list; vlr->sa_list = sa_list;
vlr->local_sa_list = local_sa_list;
if (option) { if (option) {
vlr->max_num_of_ostreams = option->sctp.sinit_num_ostreams; vlr->max_num_of_ostreams = option->sctp.sinit_num_ostreams;
vlr->option = ogs_memdup(option, sizeof *option); vlr->option = ogs_memdup(option, sizeof *option);
@@ -2770,6 +2815,7 @@ void mme_vlr_remove(mme_vlr_t *vlr)
mme_vlr_close(vlr); mme_vlr_close(vlr);
ogs_freeaddrinfo(vlr->sa_list); ogs_freeaddrinfo(vlr->sa_list);
ogs_freeaddrinfo(vlr->local_sa_list);
if (vlr->option) if (vlr->option)
ogs_free(vlr->option); ogs_free(vlr->option);

View File

@@ -216,6 +216,7 @@ typedef struct mme_vlr_s {
uint16_t ostream_id; /* vlr_ostream_id generator */ uint16_t ostream_id; /* vlr_ostream_id generator */
ogs_sockaddr_t *sa_list; /* VLR SGsAP Socket Address List */ ogs_sockaddr_t *sa_list; /* VLR SGsAP Socket Address List */
ogs_sockaddr_t *local_sa_list; /* VLR SGsAP Socket Local Address List */
ogs_sock_t *sock; /* VLR SGsAP Socket */ ogs_sock_t *sock; /* VLR SGsAP Socket */
ogs_sockaddr_t *addr; /* VLR SGsAP Connected Socket Address */ ogs_sockaddr_t *addr; /* VLR SGsAP Connected Socket Address */
@@ -938,7 +939,10 @@ void mme_pgw_remove_all(void);
ogs_sockaddr_t *mme_pgw_addr_find_by_apn_enb( ogs_sockaddr_t *mme_pgw_addr_find_by_apn_enb(
ogs_list_t *list, int family, const mme_sess_t *sess); ogs_list_t *list, int family, const mme_sess_t *sess);
mme_vlr_t *mme_vlr_add(ogs_sockaddr_t *sa_list, ogs_sockopt_t *option); mme_vlr_t *mme_vlr_add(
ogs_sockaddr_t *sa_list,
ogs_sockaddr_t *local_sa_list,
ogs_sockopt_t *option);
void mme_vlr_remove(mme_vlr_t *vlr); void mme_vlr_remove(mme_vlr_t *vlr);
void mme_vlr_remove_all(void); void mme_vlr_remove_all(void);
void mme_vlr_close(mme_vlr_t *vlr); void mme_vlr_close(mme_vlr_t *vlr);

View File

@@ -39,7 +39,8 @@ ogs_sock_t *sgsap_client(mme_vlr_t *vlr)
ogs_assert(vlr); ogs_assert(vlr);
sock = ogs_sctp_client(SOCK_SEQPACKET, vlr->sa_list, vlr->option); sock = ogs_sctp_client(SOCK_SEQPACKET,
vlr->sa_list, vlr->local_sa_list, vlr->option);
if (sock) { if (sock) {
vlr->sock = sock; vlr->sock = sock;
#if HAVE_USRSCTP #if HAVE_USRSCTP
@@ -47,7 +48,23 @@ ogs_sock_t *sgsap_client(mme_vlr_t *vlr)
usrsctp_set_non_blocking((struct socket *)sock, 1); usrsctp_set_non_blocking((struct socket *)sock, 1);
usrsctp_set_upcall((struct socket *)sock, usrsctp_recv_handler, NULL); usrsctp_set_upcall((struct socket *)sock, usrsctp_recv_handler, NULL);
#else #else
vlr->addr = &sock->remote_addr;
/*
* Originally, the code assigned vlr->addr to the address of sock->remote_addr:
* vlr->addr = &sock->remote_addr;
* However, when using sctp_connectx, it was not easy to set remote_addr.
* Therefore, at this point, vlr->addr is now assigned to vlr->sa_list,
* utilizing the initially used address:
* vlr->addr = vlr->sa_list;
*
* This approach may lead to issues when connecting to multiple addresses,
* as subsequent addresses cannot be accurately compared using the 'from'
* parameter in sctp_recvmsg.
*
* Since a proper solution is deferred, it is recommended to avoid using
* sctp_connectx with multiple addresses for the time being.
*/
vlr->addr = vlr->sa_list;
vlr->poll = ogs_pollset_add(ogs_app()->pollset, vlr->poll = ogs_pollset_add(ogs_app()->pollset,
OGS_POLLIN, sock->fd, lksctp_recv_handler, sock); OGS_POLLIN, sock->fd, lksctp_recv_handler, sock);
ogs_assert(vlr->poll); ogs_assert(vlr->poll);

View File

@@ -54,7 +54,7 @@ ogs_socknode_t *testsctp_client(const char *ipstr, int port)
node = ogs_socknode_new(addr); node = ogs_socknode_new(addr);
ogs_assert(node); ogs_assert(node);
sock = ogs_sctp_client(SOCK_STREAM, node->addr, NULL); sock = ogs_sctp_client(SOCK_STREAM, node->addr, NULL, NULL);
ogs_assert(sock); ogs_assert(sock);
node->sock = sock; node->sock = sock;
@@ -82,7 +82,7 @@ ogs_socknode_t *tests1ap_client(int family)
node = ogs_socknode_new(addr); node = ogs_socknode_new(addr);
ogs_assert(node); ogs_assert(node);
sock = ogs_sctp_client(SOCK_STREAM, node->addr, NULL); sock = ogs_sctp_client(SOCK_STREAM, node->addr, NULL, NULL);
ogs_assert(sock); ogs_assert(sock);
node->sock = sock; node->sock = sock;
@@ -121,7 +121,7 @@ ogs_socknode_t *testngap_client(int index, int family)
node = ogs_socknode_new(addr); node = ogs_socknode_new(addr);
ogs_assert(node); ogs_assert(node);
sock = ogs_sctp_client(SOCK_STREAM, node->addr, NULL); sock = ogs_sctp_client(SOCK_STREAM, node->addr, NULL, NULL);
ogs_assert(sock); ogs_assert(sock);
node->sock = sock; node->sock = sock;

View File

@@ -53,7 +53,7 @@ static void test1_func(abts_case *tc, void *data)
rv = ogs_freeaddrinfo(addr); rv = ogs_freeaddrinfo(addr);
ABTS_INT_EQUAL(tc, OGS_OK, rv); ABTS_INT_EQUAL(tc, OGS_OK, rv);
rv = ogs_getaddrinfo(&addr, AF_UNSPEC, NULL, TEST1_PORT2, AI_PASSIVE); rv = ogs_getaddrinfo(&addr, AF_INET6, NULL, TEST1_PORT2, AI_PASSIVE);
ABTS_INT_EQUAL(tc, OGS_OK, rv); ABTS_INT_EQUAL(tc, OGS_OK, rv);
sctp = ogs_sctp_server(SOCK_SEQPACKET, addr, NULL); sctp = ogs_sctp_server(SOCK_SEQPACKET, addr, NULL);
ABTS_PTR_NOTNULL(tc, sctp); ABTS_PTR_NOTNULL(tc, sctp);
@@ -77,7 +77,7 @@ static void test2_main(void *data)
rv = ogs_getaddrinfo(&addr, AF_UNSPEC, NULL, TEST2_PORT, 0); rv = ogs_getaddrinfo(&addr, AF_UNSPEC, NULL, TEST2_PORT, 0);
ABTS_INT_EQUAL(tc, OGS_OK, rv); ABTS_INT_EQUAL(tc, OGS_OK, rv);
sctp = ogs_sctp_client(SOCK_SEQPACKET, addr, NULL); sctp = ogs_sctp_client(SOCK_SEQPACKET, addr, NULL, NULL);
ABTS_PTR_NOTNULL(tc, sctp); ABTS_PTR_NOTNULL(tc, sctp);
size = ogs_sctp_recvdata(sctp, str, STRLEN, &from, &sinfo); size = ogs_sctp_recvdata(sctp, str, STRLEN, &from, &sinfo);
@@ -192,7 +192,7 @@ static void test4_main(void *data)
rv = ogs_getaddrinfo(&addr, AF_UNSPEC, NULL, TEST4_PORT, 0); rv = ogs_getaddrinfo(&addr, AF_UNSPEC, NULL, TEST4_PORT, 0);
ABTS_INT_EQUAL(tc, OGS_OK, rv); ABTS_INT_EQUAL(tc, OGS_OK, rv);
sctp = ogs_sctp_client(SOCK_STREAM, addr, NULL); sctp = ogs_sctp_client(SOCK_STREAM, addr, NULL, NULL);
ABTS_PTR_NOTNULL(tc, sctp); ABTS_PTR_NOTNULL(tc, sctp);
size = ogs_sctp_sendmsg(sctp, DATASTR, strlen(DATASTR), NULL, PPID, 0); size = ogs_sctp_sendmsg(sctp, DATASTR, strlen(DATASTR), NULL, PPID, 0);