Hello Everyone,

Today I've met a problem, when any attempts to create a new network
namespace hang up.
I see that one of previous namespaces can't be destroyed, because a
usage count for one
of its devices isn't zero. To reproduce the problem, you need to
executed an attached program
in a separate network namespace:

[root@fc24 net]# cat run.sh
ip link set up dev lo
./tcp-bug
[root@fc24 net]# unshare -n sh run.sh
[root@fc24 net]# echo $?
0
[root@fc24 ~]# cat /proc/40/stack
[<ffffffffa211989e>] msleep+0x3e/0x50
[<ffffffffa2786eea>] netdev_run_todo+0x12a/0x320
[<ffffffffa279542e>] rtnl_unlock+0xe/0x10
[<ffffffffa277d56a>] default_device_exit_batch+0x14a/0x170
[<ffffffffa2772d12>] ops_exit_list.isra.6+0x52/0x60
[<ffffffffa277402e>] cleanup_net+0x1ee/0x2f0
[<ffffffffa20ac9f5>] process_one_work+0x205/0x620
[<ffffffffa20ace5e>] worker_thread+0x4e/0x3b0
[<ffffffffa20b4344>] kthread+0x114/0x150
[<ffffffffa29619fa>] ret_from_fork+0x2a/0x40
[<ffffffffffffffff>] 0xffffffffffffffff
[root@fc24 ~]# dmesg | tail
[   97.071533] unregister_netdevice: waiting for lo to become free.
Usage count = 1
[   97.079561] systemd-journald[180]: Sent WATCHDOG=1 notification.
[  107.319260] unregister_netdevice: waiting for lo to become free.
Usage count = 1
[  117.567180] unregister_netdevice: waiting for lo to become free.
Usage count = 1
[  127.807401] unregister_netdevice: waiting for lo to become free.
Usage count = 1
[  138.055324] unregister_netdevice: waiting for lo to become free.
Usage count = 1
[  148.303308] unregister_netdevice: waiting for lo to become free.
Usage count = 1
[  158.559118] unregister_netdevice: waiting for lo to become free.
Usage count = 1
[  168.807423] unregister_netdevice: waiting for lo to become free.
Usage count = 1
[  179.055590] unregister_netdevice: waiting for lo to become free.
Usage count = 1

This program creates a server tcp socket, then it creates a pair of
connected tcp sockets
and then it does actions which trigger this problem. It calls
connect() with AF_UNSPEC
for one of connected sockets and then call connect() with the address
of the server socket.


Thanks,
Andrei
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>  /* for sockaddr_in and inet_ntoa() */

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>


#define pr_err(fmt, ...)                                           \
        printf("Error: " fmt, ##__VA_ARGS__)

#define pr_perror(fmt, ...)                                             \
        pr_err(fmt ": %s\n", ##__VA_ARGS__, strerror(errno))

#define fail(fmt, ...)                                             \
        pr_err(fmt ": %s\n", ##__VA_ARGS__, strerror(errno))

union sockaddr_inet {
	struct sockaddr addr;
	struct sockaddr_in v4;
	struct sockaddr_in6 v6;
};

int tcp_init_server(int family, int *port)
{
	union sockaddr_inet addr;
	int sock;
	int yes = 1, ret;

	memset(&addr,0,sizeof(addr));
	if (family == AF_INET) {
		addr.v4.sin_family = family;
		inet_pton(family, "0.0.0.0", &(addr.v4.sin_addr));
	} else if (family == AF_INET6){
		addr.v6.sin6_family = family;
		inet_pton(family, "::0", &(addr.v6.sin6_addr));
	} else
		return -1;

	sock = socket(family, SOCK_STREAM, IPPROTO_TCP);
	if (sock == -1) {
		pr_perror("socket() failed");
		return -1;
	}

	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1 ) {
		pr_perror("setsockopt() error");
		return -1;
	}

	while (1) {
		if (family == AF_INET)
			addr.v4.sin_port = htons(*port);
		else if (family == AF_INET6)
			addr.v6.sin6_port = htons(*port);

		ret = bind(sock, (struct sockaddr *) &addr, sizeof(addr));

		/* criu doesn't restore sock opts, so we need this hack */
		if (ret == -1 && errno == EADDRINUSE) {
			(*port)++;
			continue;
		}
		break;
	}

	if (ret == -1) {
		pr_perror("bind() failed");
		return -1;
	}

	if (listen(sock, 1) == -1) {
		pr_perror("listen() failed");
		return -1;
	}
	return sock;
}

int tcp_accept_server(int sock)
{
	struct sockaddr_in maddr;
	int sock2;
	socklen_t addrlen;
#ifdef DEBUG
	test_msg ("Waiting for connection..........\n");
#endif
	addrlen = sizeof(maddr);
	sock2 = accept(sock,(struct sockaddr *) &maddr, &addrlen);

	if (sock2 == -1) {
		pr_perror("accept() failed");
		return -1;
	}

#ifdef DEBUG
	test_msg ("Connection!!\n");
#endif
	return sock2;
}

int __tcp_init_client(int sock, int family, char *servIP, unsigned short servPort)
{
	union sockaddr_inet servAddr;

	/* Construct the server address structure */
	memset(&servAddr, 0, sizeof(servAddr));
	if (family == AF_INET) {
		servAddr.v4.sin_family      = AF_INET;
		servAddr.v4.sin_port        = htons(servPort);
		inet_pton(AF_INET, servIP, &servAddr.v4.sin_addr);
	} else {
		servAddr.v6.sin6_family      = AF_INET6;
		servAddr.v6.sin6_port        = htons(servPort);
		inet_pton(AF_INET6, servIP, &servAddr.v6.sin6_addr);
	}
	if (connect(sock, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0) {
		pr_perror("can't connect to server");
		return -1;
	}
	return sock;
}

int tcp_init_client(int family, char *servIP, unsigned short servPort)
{
	int sock;

	if ((sock = socket(family, SOCK_STREAM, IPPROTO_TCP)) < 0) {
		pr_perror("can't create socket");
		return -1;
	}
	return __tcp_init_client(sock, family, servIP, servPort);
}

#ifdef ZDTM_IPV6
#define ZDTM_FAMILY AF_INET6
#else
#define ZDTM_FAMILY AF_INET
#endif
static int port = 8880;

int main(int argc, char **argv)
{
	int fd, fd_s, clt, sk;
	union sockaddr_inet src_addr, dst_addr, addr;
	socklen_t aux;
	char c = 5;

	sk = socket(ZDTM_FAMILY, SOCK_STREAM, 0);
	if (sk < 0) {
		pr_perror("socket");
		return 1;
	}

	if ((fd_s = tcp_init_server(ZDTM_FAMILY, &port)) < 0) {
		pr_err("initializing server failed\n");
		return 1;
	}

	clt = tcp_init_client(ZDTM_FAMILY, "localhost", port);
	if (clt < 0)
		return 1;

	/*
	 * parent is server of TCP connection
	 */
	fd = tcp_accept_server(fd_s);
	if (fd < 0) {
		pr_err("can't accept client connection\n");
		return 1;
	}

	shutdown(clt, SHUT_WR);

	{
		union sockaddr_inet addr;
		int fd1;

		memset(&addr, 0, sizeof(addr));
		addr.v4.sin_family      = AF_UNSPEC;
		if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)))
			return 1;

		if (__tcp_init_client(fd, ZDTM_FAMILY, "localhost", port) < 0)
			return 1;

		return 0;
		fd1 = tcp_accept_server(fd_s);
		if (fd1 < 0) {
			pr_err("can't accept client connection\n");
			return 1;
		}
	}

	return 0;
}

Reply via email to