* SCTP INIT chunks do not include multi-homing addresses if IPv6 is used
@ 2022-07-03 11:36 Aaron Jones
0 siblings, 0 replies; only message in thread
From: Aaron Jones @ 2022-07-03 11:36 UTC (permalink / raw)
To: linux-sctp
[-- Attachment #1: Type: text/plain, Size: 1392 bytes --]
Hello.
While debugging SCTP multi-homing in an application over the last couple
of days, I discovered that the cause is because the application randomly
prefers IPv4 or IPv6 (literally; it does the equivalent of a dice roll).
If the application passes an IPv4 address first to sctp_bindx() and
sctp_connectx(), followed by an IPv6 address, then multi-homing works;
the SCTP INIT chunk includes both IP addresses passed to sctp_bindx(),
and /proc/net/sctp/assocs contains both. Blocking IPv4 traffic does not
break the connection; it falls back to IPv6.
However, in the reverse situation (IPv6 address followed by an IPv4
address), it does not; there are no IP addresses in the INIT chunk at
all. Thus, /proc/net/sctp/assocs contains only IPv6 addresses, and
blocking IPv6 traffic breaks the connection.
I have written a minimal reproducer at [0] (which I am attaching to this
message), and you can observe the results at [1] (if you do not wish to
run it).
I have managed to reproduce it on the following software combinations:
- lksctp-tools 1.0.18 with Linux kernel 5.4 (Linux Mint 20.3)
- lksctp-tools 1.0.19 with Linux kernel 5.10 (Debian 11)
- lksctp-tools 1.0.19 with Linux kernel 4.14.278 (Gentoo)
I do not know whether this is a bug in libsctp or in the kernel.
Regards,
Aaron Jones
[0] <https://paste.debian.net/hidden/998cad29/>
[1] <https://imgur.com/a/qbGrXXV>
[-- Attachment #2: repro.c --]
[-- Type: text/x-csrc, Size: 2934 bytes --]
/* SPDX-License-Identifier: GPL-2.0
*
* Reproducer for SCTP multihoming bug where an IPv6 address given first
* in the list of addresses means that the SCTP INIT chunk does not
* include any IP addresses and so the association does not multi-home.
*
* Copyright (C) 2022 Aaron M. D. Jones <me@aaronmdjones.net>
*
* gcc -Wall -Wextra -Wpedantic -std=gnu99 -lsctp repro.c -o repro
* ./repro <port> <remote> <remote> [<local> <local>]
*/
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
int
main(int argc, char *argv[])
{
struct sockaddr_storage ss;
struct sockaddr_in *sa4 = (struct sockaddr_in *) &ss;
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &ss;
uint8_t laddrs[sizeof(struct sockaddr_storage) * 2];
uint8_t raddrs[sizeof(struct sockaddr_storage) * 2];
uint8_t *laddrp = laddrs;
uint8_t *raddrp = raddrs;
uint16_t port = 0;
memset(laddrs, 0x00, sizeof laddrs);
memset(raddrs, 0x00, sizeof raddrs);
const int fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP);
if (fd == -1)
{
perror("socket");
return EXIT_FAILURE;
}
if (argc < 4 || argc == 5 || argc > 6)
{
fprintf(stderr, "Usage: ./repro <port> <remote> <remote> [<local> <local>]\n");
return EXIT_FAILURE;
}
if (sscanf(argv[1], "%" SCNu16, &port) != 1 || port == 0)
{
fprintf(stderr, "Invalid port \"%s\"\n", argv[1]);
return EXIT_FAILURE;
}
for (int i = 2; i < 4; i++)
{
memset(&ss, 0x00, sizeof ss);
if (inet_pton(AF_INET, argv[i], &sa4->sin_addr) == 1)
{
sa4->sin_family = AF_INET;
sa4->sin_port = htons(port);
memcpy(raddrp, sa4, sizeof *sa4);
raddrp += sizeof *sa4;
}
else if (inet_pton(AF_INET6, argv[i], &sa6->sin6_addr) == 1)
{
sa6->sin6_family = AF_INET6;
sa6->sin6_port = htons(port);
memcpy(raddrp, sa6, sizeof *sa6);
raddrp += sizeof *sa6;
}
else
{
fprintf(stderr, "Invalid IP address \"%s\"\n", argv[i]);
return EXIT_FAILURE;
}
}
for (int i = 4; i < argc; i++)
{
memset(&ss, 0x00, sizeof ss);
if (inet_pton(AF_INET, argv[i], &sa4->sin_addr) == 1)
{
sa4->sin_family = AF_INET;
sa4->sin_port = htons(port);
memcpy(laddrp, sa4, sizeof *sa4);
laddrp += sizeof *sa4;
}
else if (inet_pton(AF_INET6, argv[i], &sa6->sin6_addr) == 1)
{
sa6->sin6_family = AF_INET6;
sa6->sin6_port = htons(port);
memcpy(laddrp, sa6, sizeof *sa6);
laddrp += sizeof *sa6;
}
else
{
fprintf(stderr, "Invalid IP address \"%s\"\n", argv[i]);
return EXIT_FAILURE;
}
}
if (argc == 6 && sctp_bindx(fd, (struct sockaddr *) laddrs, 2, SCTP_BINDX_ADD_ADDR) == -1)
{
perror("sctp_bindx");
return EXIT_FAILURE;
}
if (sctp_connectx(fd, (struct sockaddr *) raddrs, 2, NULL) == -1)
{
perror("sctp_connectx");
return EXIT_FAILURE;
}
close(fd);
return EXIT_SUCCESS;
}
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2022-07-03 11:36 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-03 11:36 SCTP INIT chunks do not include multi-homing addresses if IPv6 is used Aaron Jones
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).