Re: kernel bug? listening on same port with IPv4 and IPv6

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 07/01/2023 01.38, Gordon Messmer wrote:
On 2023-01-06 06:17, Sjoerd Mullender wrote:
I have a program that is supposed to listen to the same port on both IPv4 and IPv6 sockets.  In the past, what it did, was basically: create new socket for IPv6, set option IPV6_V6ONLY to off, bind, listen; then create a new socket for IPv4, and also bind and listen.


Do you have sample code that demonstrates this process?

I'm confused by your description, because setting IPV6_V6ONLY to 0 with setsockopt should result in a socket that's bound to a port that accepts both IPv4 and IPv6 connections.  You shouldn't have ever been able to bind to that port in IPv4 in the past.

See the attached program.  Can be compiled without any extra options.

This program creates an IPv6 listening socket and tells it to also do IPv4. Then it executes "netstat -anp | grep <pid>" to show what it did. After that it tries to create an IPv4 listening socket for the port that was assigned in the first round and if successful again executes netstat. If not successful, it will tell why.

When trying to bind to the wildcard address (specify "all" on the command line), the behavior is very different from when specifying "localhost".

Run as
$ ./a.out all 0
$ ./a.out localhost 0

and see the difference in behavior. When using "all", the second round (IPv4) says that bind returns 1 unexpectedly, and the port is also unexpected. When using "localhost", both IPv6 and IPv4 succeed and listen to the same port, but using two different sockets internally.

The behavior for the "all" case is different with the older kernel 6.0.15-300.fc37.x86_64 where bind will say "Address already in use".

--
Sjoerd Mullender
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int
main(int argc, char **argv)
{
	struct addrinfo hints = {
		.ai_family = AF_INET6,
		.ai_flags = AI_PASSIVE | AI_NUMERICSERV,
		.ai_socktype = SOCK_STREAM,
		.ai_protocol = IPPROTO_TCP,
	};
	struct addrinfo *result = NULL;
	/* 0: use IPv6 and IPv4, 1: only IPv6, -1: only IPv4 */
	int ipv6_vs6only = -1;	/* may get changed depending on addr */

	if (argc != 3) {
  usage:
		fprintf(stderr, "Usage: %s address port\n", argv[0]);
		fprintf(stderr, "where address is either all or localhost\n");
		fprintf(stderr, "and port is either 0 or a value between 1025 and 65535\n");
		exit(1);
	}
	const char *listenaddr = argv[1];
	int port = atoi(argv[2]);
	if (strcmp(listenaddr, "localhost") == 0) {
		hints.ai_family = AF_INET6;
		ipv6_vs6only = 0;
	} else if (strcmp(listenaddr, "all") == 0) {
		hints.ai_family = AF_INET6;
		ipv6_vs6only = 0;
		listenaddr = NULL;
	} else {
		goto usage;
	}
	char ports[8];		/* max "65535" */
	int socks[2];
	int nsocks = 0;
	for (;;) {
		snprintf(ports, sizeof(ports), "%u", port);
		int check = getaddrinfo(listenaddr, ports, &hints, &result);
		if (check != 0) {
			fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(check));
			exit(1);
		}
		int sock = -1;
		for (struct addrinfo *rp = result; rp; rp = rp->ai_next) {
			sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
			if (sock == -1) {
				fprintf(stderr, "socket: %s\n", gai_strerror(check));
				continue;
			}
			if (hints.ai_family == AF_INET6 && /* only if IPv6 */
			    setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_vs6only, sizeof(int)) == -1) {
				perror("setsockopt");
			}
			int e;
			e = bind(sock, rp->ai_addr, rp->ai_addrlen);
			switch (e) {
			case 0:
				break;
			case -1:
				perror("bind");
				close(sock);
				continue;
			default:
				fprintf(stderr, "bind: unexpected return: %d\n", e);
				break;
			}
			union {
				struct sockaddr_storage ss;
				struct sockaddr_in i4;
				struct sockaddr_in6 i6;
			} myaddr;
			socklen_t myaddrlen = sizeof(myaddr.ss);
			if (getsockname(sock, (struct sockaddr *) &myaddr.ss, &myaddrlen) == -1) {
				perror("getsockname");
				close(sock);
				continue;
			}
			int myport = myaddr.ss.ss_family == AF_INET6 ? htons(myaddr.i6.sin6_port) : htons(myaddr.i4.sin_port);
			if (port == 0)
				port = myport;
			else if (port != myport) {
				fprintf(stderr, "bound to unexpected port %d, expected %d\n", myport, port);
				close(sock);
				continue;
			}
			if (listen(sock, 2) == -1) {
				perror("listen");
				close(sock);
				continue;
			}
			char command[64];
			snprintf(command, sizeof(command), "netstat -anp | grep %d", (int) getpid());
			system(command);
			socks[nsocks++] = sock;
			break;
		}
		freeaddrinfo(result);
		if (ipv6_vs6only == 0) {
			ipv6_vs6only = -1; /* not 0! */
			hints.ai_family = AF_INET;
		} else
			break;
	}
	for (int i = 0; i < nsocks; i++)
		close(socks[i]);
	exit(0);
}
_______________________________________________
users mailing list -- users@xxxxxxxxxxxxxxxxxxxxxxx
To unsubscribe send an email to users-leave@xxxxxxxxxxxxxxxxxxxxxxx
Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: https://lists.fedoraproject.org/archives/list/users@xxxxxxxxxxxxxxxxxxxxxxx
Do not reply to spam, report it: https://pagure.io/fedora-infrastructure/new_issue
[Index of Archives]     [Older Fedora Users]     [Fedora Announce]     [Fedora Package Announce]     [EPEL Announce]     [EPEL Devel]     [Fedora Magazine]     [Fedora Summer Coding]     [Fedora Laptop]     [Fedora Cloud]     [Fedora Advisory Board]     [Fedora Education]     [Fedora Security]     [Fedora Scitech]     [Fedora Robotics]     [Fedora Infrastructure]     [Fedora Websites]     [Anaconda Devel]     [Fedora Devel Java]     [Fedora Desktop]     [Fedora Fonts]     [Fedora Marketing]     [Fedora Management Tools]     [Fedora Mentors]     [Fedora Package Review]     [Fedora R Devel]     [Fedora PHP Devel]     [Kickstart]     [Fedora Music]     [Fedora Packaging]     [Fedora SELinux]     [Fedora Legal]     [Fedora Kernel]     [Fedora OCaml]     [Coolkey]     [Virtualization Tools]     [ET Management Tools]     [Yum Users]     [Yosemite News]     [Gnome Users]     [KDE Users]     [Fedora Art]     [Fedora Docs]     [Fedora Sparc]     [Libvirt Users]     [Fedora ARM]

  Powered by Linux