Commit 0d128f9c authored by Robert Schmidt's avatar Robert Schmidt

Rewrite sctp_handle_new_association_req() to handle hostnames and IPv6

Use getaddrinfo() to look up IP (v4/v6) addresses. The old
interface of separately specifying IPv4 and IPv6 addresses is
preserved, but both fields allow to specify types of addresses, and we
will remove one field in the future.
parent e65cb0cf
...@@ -354,65 +354,67 @@ sctp_handle_new_association_req_multi( ...@@ -354,65 +354,67 @@ sctp_handle_new_association_req_multi(
ns, sctp_nb_cnx, assoc_id); ns, sctp_nb_cnx, assoc_id);
} }
//------------------------------------------------------------------------------ static const char *print_ip(struct addrinfo *p, char *buf, size_t buf_len)
static void
sctp_handle_new_association_req(
const instance_t instance,
const task_id_t requestor,
const sctp_new_association_req_t * const sctp_new_association_req_p)
{ {
int sd = 0; void *addr;
sctp_assoc_t assoc_id = 0; if (p->ai_family == AF_INET) {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
struct sctp_event_subscribe events={0}; addr = &ipv4->sin_addr;
} else {
struct sctp_cnx_list_elm_s *sctp_cnx = NULL; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
enum sctp_connection_type_e connection_type = SCTP_TYPE_CLIENT; addr = &ipv6->sin6_addr;
}
struct ifreq ifr;
struct ifaddrs *ifaddr = NULL; // convert the IP to a string and print it:
struct ifaddrs *ifa = NULL; return inet_ntop(p->ai_family, addr, buf, buf_len);
int family = 0; }
int s = 0;
struct in_addr in;
struct in6_addr in6;
/* Prepare a new SCTP association as requested by upper layer and try to connect
* to remote host.
*/
DevAssert(sctp_new_association_req_p != NULL);
/* Create new socket with IPv6 affinity */ static void sctp_handle_new_association_req(const instance_t instance,
//#warning "SCTP may Force IPv4 only, here" const task_id_t requestor,
#ifdef NO_VIRTUAL_MACHINE const sctp_new_association_req_t *const req)
{
// in init chunk appears a list of host addresses, IPv4 and IPv4 in an arbitrary (unsorted) order enum sctp_connection_type_e connection_type = SCTP_TYPE_CLIENT;
// SCTP hearbeats starts with first ipv4 addresses then stop triyng with other ipv4 addresses
// if it encounters an IPv6 address in list, so we can force the building of IPv4 addresses only /* local address: IPv4 has priority, but can also handle IPv6 */
// with AF_INET (the working IPv4 address can be the last in the list...) const char *local = NULL;
if ((sd = socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP)) < 0) { if (req->local_address.ipv6) {
#else local = req->local_address.ipv6_address;
SCTP_WARN("please specify IPv6 addresses in the IPv4 field, IPv4 handles both\n");
if ((sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) < 0) { }
#endif if (req->local_address.ipv4)
SCTP_ERROR("Socket creation failed: %s\n", strerror(errno)); local = req->local_address.ipv4_address;
return;
/* Prepare a new SCTP association as requested by upper layer and try to connect
* to remote host.
*/
DevAssert(req != NULL);
struct addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_SCTP};
if (local == NULL)
hints.ai_flags = AI_PASSIVE;
struct addrinfo *serv;
int status = getaddrinfo(local, NULL, &hints, &serv);
AssertFatal(status == 0, "getaddrinfo() failed: %s\n", gai_strerror(status));
int sd;
struct addrinfo *p = NULL;
for (p = serv; p != NULL; p = p->ai_next) {
char buf[512];
const char *ip = print_ip(p, buf, sizeof(buf));
SCTP_DEBUG("Trying %s for client socket creation\n", ip);
if ((sd = socket(serv->ai_family, serv->ai_socktype, serv->ai_protocol)) == -1) {
SCTP_WARN("Socket creation failed: %s\n", strerror(errno));
continue;
} }
/* Add the socket to list of fd monitored by ITTI */ /* we assume we can set options, or something is likely broken; the
itti_subscribe_event_fd(TASK_SCTP, sd); * connection won't operate properly */
int ret = sctp_set_init_opt(sd, req->in_streams, req->out_streams, SCTP_MAX_ATTEMPTS, SCTP_TIMEOUT);
if (sctp_set_init_opt(sd, AssertFatal(ret == 0, "sctp_set_init_opt() failed\n");
sctp_new_association_req_p->in_streams,
sctp_new_association_req_p->out_streams,
SCTP_MAX_ATTEMPTS, SCTP_TIMEOUT) != 0) {
SCTP_ERROR("Setsockopt IPPROTO_SCTP_INITMSG failed: %s\n",
strerror(errno));
itti_unsubscribe_event_fd(TASK_SCTP, sd);
close(sd);
return;
}
/* Subscribe to all events */ /* Subscribe to all events */
struct sctp_event_subscribe events = {0};
events.sctp_data_io_event = 1; events.sctp_data_io_event = 1;
events.sctp_association_event = 1; events.sctp_association_event = 1;
events.sctp_address_event = 1; events.sctp_address_event = 1;
...@@ -421,199 +423,115 @@ sctp_handle_new_association_req( ...@@ -421,199 +423,115 @@ sctp_handle_new_association_req(
events.sctp_shutdown_event = 1; events.sctp_shutdown_event = 1;
events.sctp_partial_delivery_event = 1; events.sctp_partial_delivery_event = 1;
if (setsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, &events, /* as above */
8) < 0) { ret = setsockopt(sd, serv->ai_protocol, SCTP_EVENTS, &events, 8);
SCTP_ERROR("Setsockopt IPPROTO_SCTP_EVENTS failed: %s\n", AssertFatal(ret == 0, "setsockopt() IPPROTO_SCTP_EVENTS failed: %s\n", strerror(errno));
strerror(errno));
close(sd);
return;
}
// Bind to device ... or we could bind to address also /* if that fails, we will try the next address */
if (getifaddrs(&ifaddr) == -1) { ret = sctp_bindx(sd, p->ai_addr, 1, SCTP_BINDX_ADD_ADDR);
SCTP_ERROR("getifaddrs failed: %s\n", strerror(errno)); if (ret != 0) {
close(sd); SCTP_WARN("sctp_bindx() SCTP_BINDX_ADD_ADDR failed: errno %d %s\n", errno, strerror(errno));
close(sd);
continue;
} }
/* Walk through linked list, maintaining head pointer so we SCTP_DEBUG("sctp_bindx() SCTP_BINDX_ADD_ADDR: socket bound to %s/%s\n", local, ip);
can free list later */ break;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { }
if (ifa->ifa_addr == NULL)
continue;
family = ifa->ifa_addr->sa_family; freeaddrinfo(serv);
/* For an AF_INET* interface address, display the address */
if (sctp_new_association_req_p->local_address.ipv4 && family == AF_INET) {
// compare address
s = inet_aton(sctp_new_association_req_p->local_address.ipv4_address,
&in);
if (s > 0 ) {
if (((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr == in.s_addr) {
struct sockaddr_in locaddr={0};
locaddr.sin_family = AF_INET;
locaddr.sin_port = 0;
locaddr.sin_addr.s_addr = in.s_addr;
if (sctp_bindx(sd, (struct sockaddr*)&locaddr, 1, SCTP_BINDX_ADD_ADDR) < 0) {
SCTP_ERROR("sctp_bindx SCTP_BINDX_ADD_ADDR failed: %s\n",
strerror(errno));
} else {
SCTP_DEBUG("sctp_bindx SCTP_BINDX_ADD_ADDR socket bound to : %s\n",
inet_ntoa(locaddr.sin_addr));
}
break;
} if (p == NULL) {
} SCTP_ERROR("could not open socket, no SCTP connection established\n");
} else if (sctp_new_association_req_p->local_address.ipv6 && family == AF_INET6) { return;
// compare address }
s = inet_pton(AF_INET6,
sctp_new_association_req_p->local_address.ipv6_address, /* SOCK_STREAM socket type requires an explicit connect to the remote host
&in6); * address and port. */
/* remote address: IPv4 has priority, but can also handle IPv6 */
if (s == 1 ) { const char *remote = NULL;
if (memcmp(&((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr, if (req->remote_address.ipv6) {
&in6, sizeof(in6)) == 0) { remote = req->remote_address.ipv6_address;
memset(&ifr, 0, sizeof(ifr)); SCTP_WARN("please specify IPv6 addresses in the IPv4 field, IPv4 handles both\n");
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifa->ifa_name); }
if (req->remote_address.ipv4)
if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) { remote = req->remote_address.ipv4_address;
SCTP_ERROR("Setsockopt SOL_SOCKET failed: %s\n", sctp_assoc_t assoc_id = 0;
strerror(errno)); if (remote != NULL) {
} else { struct addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_SCTP};
SCTP_DEBUG("Setsockopt SOL_SOCKET socket bound to : %s\n", struct addrinfo *serv;
ifa->ifa_name);
} char port[12];
snprintf(port, sizeof(port), "%d", req->port);
break; int status = getaddrinfo(remote, port, &hints, &serv);
} AssertFatal(status == 0, "getaddrinfo() failed: %s\n", gai_strerror(status));
}
struct addrinfo *p = NULL;
for (p = serv; p != NULL; p = p->ai_next) {
char buf[512];
const char *ip = print_ip(p, buf, sizeof(buf));
SCTP_DEBUG("Trying to connect to %s for remote end %s\n", ip, remote);
if (sctp_connectx(sd, p->ai_addr, 1, &assoc_id) < 0) {
/* sctp_connectx on non-blocking socket return EINPROGRESS */
if (errno != EINPROGRESS) {
SCTP_ERROR("Connect failed: %s\n", strerror(errno));
sctp_itti_send_association_resp(requestor, instance, -1, req->ulp_cnx_id, SCTP_STATE_UNREACHABLE, 0, 0);
close(sd);
return;
} else {
SCTP_DEBUG("sctp_connectx(): assoc_id %d in progress...\n", assoc_id);
} }
} else {
SCTP_DEBUG("sctp_connectx() SUCCESS: used assoc_id %d\n", assoc_id);
}
break;
} }
freeifaddrs(ifaddr); freeaddrinfo(serv);
} else {
/* Mark the socket as non-blocking */ /* I am not sure that this is relevant; we already did sctp_bindx() above */
if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) { connection_type = SCTP_TYPE_SERVER;
SCTP_ERROR("fcntl F_SETFL O_NONBLOCK failed: %s\n",
strerror(errno));
close(sd);
return;
}
/* SOCK_STREAM socket type requires an explicit connect to the remote host /* No remote address provided -> only bind the socket for now.
* address and port. * Connection will be accepted in the main event loop
* Only use IPv4 for the first connection attempt
*/ */
if ((sctp_new_association_req_p->remote_address.ipv6 != 0) || /*
(sctp_new_association_req_p->remote_address.ipv4 != 0)) { struct sockaddr_in6 addr6;
uint8_t address_index = 0;
uint8_t used_address = sctp_new_association_req_p->remote_address.ipv6 +
sctp_new_association_req_p->remote_address.ipv4;
struct sockaddr_in addr[used_address];
memset(addr, 0, used_address * sizeof(struct sockaddr_in));
if (sctp_new_association_req_p->remote_address.ipv6 == 1) { addr6.sin6_family = AF_INET6;
if (inet_pton(AF_INET6, sctp_new_association_req_p->remote_address.ipv6_address, addr6.sin6_addr = in6addr_any;
&addr[address_index].sin_addr.s_addr) != 1) { addr6.sin6_port = htons(req->port);
SCTP_ERROR("Failed to convert ipv6 address %*s to network type\n", addr6.sin6_flowinfo = 0;
(int)strlen(sctp_new_association_req_p->remote_address.ipv6_address),
sctp_new_association_req_p->remote_address.ipv6_address);
close(sd);
return;
}
SCTP_DEBUG("Converted ipv6 address %*s to network type\n", if (bind(sd, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
(int)strlen(sctp_new_association_req_p->remote_address.ipv6_address), SCTP_ERROR("Failed to bind the socket to address any (v4/v6): %s\n", strerror(errno));
sctp_new_association_req_p->remote_address.ipv6_address); close(sd);
return;
addr[address_index].sin_family = AF_INET6;
addr[address_index].sin_port = htons(sctp_new_association_req_p->port);
address_index++;
}
if (sctp_new_association_req_p->remote_address.ipv4 == 1) {
if (inet_pton(AF_INET, sctp_new_association_req_p->remote_address.ipv4_address,
&addr[address_index].sin_addr.s_addr) != 1) {
SCTP_ERROR("Failed to convert ipv4 address %*s to network type\n",
(int)strlen(sctp_new_association_req_p->remote_address.ipv4_address),
sctp_new_association_req_p->remote_address.ipv4_address);
close(sd);
return;
}
SCTP_DEBUG("Converted ipv4 address %*s to network type\n",
(int)strlen(sctp_new_association_req_p->remote_address.ipv4_address),
sctp_new_association_req_p->remote_address.ipv4_address);
addr[address_index].sin_family = AF_INET;
addr[address_index].sin_port = htons(sctp_new_association_req_p->port);
address_index++;
}
/* Connect to remote host and port */
if (sctp_connectx(sd, (struct sockaddr *)addr, 1, &assoc_id) < 0) {
/* sctp_connectx on non-blocking socket return EINPROGRESS */
if (errno != EINPROGRESS) {
SCTP_ERROR("Connect failed: %s\n", strerror(errno));
sctp_itti_send_association_resp(
requestor, instance, -1, sctp_new_association_req_p->ulp_cnx_id,
SCTP_STATE_UNREACHABLE, 0, 0);
/* Add the socket to list of fd monitored by ITTI */
itti_unsubscribe_event_fd(TASK_SCTP, sd);
close(sd);
return;
} else {
SCTP_DEBUG("connectx assoc_id %d in progress..., used %d addresses\n",
assoc_id, used_address);
}
} else {
SCTP_DEBUG("sctp_connectx SUCCESS, used %d addresses assoc_id %d\n",
used_address,
assoc_id);
}
} else {
/* No remote address provided -> only bind the socket for now.
* Connection will be accepted in the main event loop
*/
struct sockaddr_in6 addr6;
connection_type = SCTP_TYPE_SERVER;
/* For now bind to any interface */
addr6.sin6_family = AF_INET6;
addr6.sin6_addr = in6addr_any;
addr6.sin6_port = htons(sctp_new_association_req_p->port);
addr6.sin6_flowinfo = 0;
if (bind(sd, (struct sockaddr*)&addr6, sizeof(addr6)) < 0) {
SCTP_ERROR("Failed to bind the socket to address any (v4/v6): %s\n",
strerror(errno));
close(sd);
return;
}
} }
*/
}
sctp_cnx = calloc(1, sizeof(*sctp_cnx)); struct sctp_cnx_list_elm_s *sctp_cnx = calloc(1, sizeof(*sctp_cnx));
AssertFatal(sctp_cnx != NULL, "out of memory\n");
sctp_cnx->connection_type = connection_type;
sctp_cnx->connection_type = connection_type; sctp_cnx->sd = sd;
sctp_cnx->task_id = requestor;
sctp_cnx->cnx_id = req->ulp_cnx_id;
sctp_cnx->ppid = req->ppid;
sctp_cnx->instance = instance;
sctp_cnx->assoc_id = assoc_id;
sctp_cnx->sd = sd; /* Insert new element at end of list */
sctp_cnx->task_id = requestor; STAILQ_INSERT_TAIL(&sctp_cnx_list, sctp_cnx, entries);
sctp_cnx->cnx_id = sctp_new_association_req_p->ulp_cnx_id; sctp_nb_cnx++;
sctp_cnx->ppid = sctp_new_association_req_p->ppid;
sctp_cnx->instance = instance;
sctp_cnx->assoc_id = assoc_id;
/* Insert new element at end of list */ /* Add the socket to list of fd monitored by ITTI */
STAILQ_INSERT_TAIL(&sctp_cnx_list, sctp_cnx, entries); itti_subscribe_event_fd(TASK_SCTP, sd);
sctp_nb_cnx++;
SCTP_DEBUG("Inserted new descriptor for sd %d in list, nb elements %u, assoc_id %d\n", SCTP_DEBUG("Inserted new descriptor for sd %d in list, nb elements %u, assoc_id %d\n", sd, sctp_nb_cnx, assoc_id);
sd, sctp_nb_cnx, assoc_id);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment