brintos

brintos / linux-shallow public Read only

0
0
Text · 25.2 KiB · 2778494 Raw
1163 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2#define _GNU_SOURCE3 4#include <errno.h>5#include <stdbool.h>6#include <stdio.h>7#include <string.h>8#include <unistd.h>9#include <sched.h>10 11#include <arpa/inet.h>12#include <sys/mount.h>13#include <sys/stat.h>14#include <sys/types.h>15#include <sys/un.h>16#include <sys/eventfd.h>17 18#include <linux/err.h>19#include <linux/in.h>20#include <linux/in6.h>21#include <linux/limits.h>22 23#include <linux/ip.h>24#include <linux/udp.h>25#include <netinet/tcp.h>26#include <net/if.h>27 28#include "bpf_util.h"29#include "network_helpers.h"30#include "test_progs.h"31 32#ifdef TRAFFIC_MONITOR33/* Prevent pcap.h from including pcap/bpf.h and causing conflicts */34#define PCAP_DONT_INCLUDE_PCAP_BPF_H 135#include <pcap/pcap.h>36#include <pcap/dlt.h>37#endif38 39#ifndef IPPROTO_MPTCP40#define IPPROTO_MPTCP 26241#endif42 43#define clean_errno() (errno == 0 ? "None" : strerror(errno))44#define log_err(MSG, ...) ({						\45			int __save = errno;				\46			fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \47				__FILE__, __LINE__, clean_errno(),	\48				##__VA_ARGS__);				\49			errno = __save;					\50})51 52struct ipv4_packet pkt_v4 = {53	.eth.h_proto = __bpf_constant_htons(ETH_P_IP),54	.iph.ihl = 5,55	.iph.protocol = IPPROTO_TCP,56	.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),57	.tcp.urg_ptr = 123,58	.tcp.doff = 5,59};60 61struct ipv6_packet pkt_v6 = {62	.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),63	.iph.nexthdr = IPPROTO_TCP,64	.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),65	.tcp.urg_ptr = 123,66	.tcp.doff = 5,67};68 69static const struct network_helper_opts default_opts;70 71int settimeo(int fd, int timeout_ms)72{73	struct timeval timeout = { .tv_sec = 3 };74 75	if (timeout_ms > 0) {76		timeout.tv_sec = timeout_ms / 1000;77		timeout.tv_usec = (timeout_ms % 1000) * 1000;78	}79 80	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout,81		       sizeof(timeout))) {82		log_err("Failed to set SO_RCVTIMEO");83		return -1;84	}85 86	if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout,87		       sizeof(timeout))) {88		log_err("Failed to set SO_SNDTIMEO");89		return -1;90	}91 92	return 0;93}94 95#define save_errno_close(fd) ({ int __save = errno; close(fd); errno = __save; })96 97int start_server_addr(int type, const struct sockaddr_storage *addr, socklen_t addrlen,98		      const struct network_helper_opts *opts)99{100	int fd;101 102	if (!opts)103		opts = &default_opts;104 105	fd = socket(addr->ss_family, type, opts->proto);106	if (fd < 0) {107		log_err("Failed to create server socket");108		return -1;109	}110 111	if (settimeo(fd, opts->timeout_ms))112		goto error_close;113 114	if (opts->post_socket_cb &&115	    opts->post_socket_cb(fd, opts->cb_opts)) {116		log_err("Failed to call post_socket_cb");117		goto error_close;118	}119 120	if (bind(fd, (struct sockaddr *)addr, addrlen) < 0) {121		log_err("Failed to bind socket");122		goto error_close;123	}124 125	if (type == SOCK_STREAM) {126		if (listen(fd, opts->backlog ? MAX(opts->backlog, 0) : 1) < 0) {127			log_err("Failed to listed on socket");128			goto error_close;129		}130	}131 132	return fd;133 134error_close:135	save_errno_close(fd);136	return -1;137}138 139int start_server_str(int family, int type, const char *addr_str, __u16 port,140		     const struct network_helper_opts *opts)141{142	struct sockaddr_storage addr;143	socklen_t addrlen;144 145	if (!opts)146		opts = &default_opts;147 148	if (make_sockaddr(family, addr_str, port, &addr, &addrlen))149		return -1;150 151	return start_server_addr(type, &addr, addrlen, opts);152}153 154int start_server(int family, int type, const char *addr_str, __u16 port,155		 int timeout_ms)156{157	struct network_helper_opts opts = {158		.timeout_ms	= timeout_ms,159	};160 161	return start_server_str(family, type, addr_str, port, &opts);162}163 164static int reuseport_cb(int fd, void *opts)165{166	int on = 1;167 168	return setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));169}170 171int *start_reuseport_server(int family, int type, const char *addr_str,172			    __u16 port, int timeout_ms, unsigned int nr_listens)173{174	struct network_helper_opts opts = {175		.timeout_ms = timeout_ms,176		.post_socket_cb = reuseport_cb,177	};178	struct sockaddr_storage addr;179	unsigned int nr_fds = 0;180	socklen_t addrlen;181	int *fds;182 183	if (!nr_listens)184		return NULL;185 186	if (make_sockaddr(family, addr_str, port, &addr, &addrlen))187		return NULL;188 189	fds = malloc(sizeof(*fds) * nr_listens);190	if (!fds)191		return NULL;192 193	fds[0] = start_server_addr(type, &addr, addrlen, &opts);194	if (fds[0] == -1)195		goto close_fds;196	nr_fds = 1;197 198	if (getsockname(fds[0], (struct sockaddr *)&addr, &addrlen))199		goto close_fds;200 201	for (; nr_fds < nr_listens; nr_fds++) {202		fds[nr_fds] = start_server_addr(type, &addr, addrlen, &opts);203		if (fds[nr_fds] == -1)204			goto close_fds;205	}206 207	return fds;208 209close_fds:210	free_fds(fds, nr_fds);211	return NULL;212}213 214void free_fds(int *fds, unsigned int nr_close_fds)215{216	if (fds) {217		while (nr_close_fds)218			close(fds[--nr_close_fds]);219		free(fds);220	}221}222 223int fastopen_connect(int server_fd, const char *data, unsigned int data_len,224		     int timeout_ms)225{226	struct sockaddr_storage addr;227	socklen_t addrlen = sizeof(addr);228	struct sockaddr_in *addr_in;229	int fd, ret;230 231	if (getsockname(server_fd, (struct sockaddr *)&addr, &addrlen)) {232		log_err("Failed to get server addr");233		return -1;234	}235 236	addr_in = (struct sockaddr_in *)&addr;237	fd = socket(addr_in->sin_family, SOCK_STREAM, 0);238	if (fd < 0) {239		log_err("Failed to create client socket");240		return -1;241	}242 243	if (settimeo(fd, timeout_ms))244		goto error_close;245 246	ret = sendto(fd, data, data_len, MSG_FASTOPEN, (struct sockaddr *)&addr,247		     addrlen);248	if (ret != data_len) {249		log_err("sendto(data, %u) != %d\n", data_len, ret);250		goto error_close;251	}252 253	return fd;254 255error_close:256	save_errno_close(fd);257	return -1;258}259 260int client_socket(int family, int type,261		  const struct network_helper_opts *opts)262{263	int fd;264 265	if (!opts)266		opts = &default_opts;267 268	fd = socket(family, type, opts->proto);269	if (fd < 0) {270		log_err("Failed to create client socket");271		return -1;272	}273 274	if (settimeo(fd, opts->timeout_ms))275		goto error_close;276 277	if (opts->post_socket_cb &&278	    opts->post_socket_cb(fd, opts->cb_opts))279		goto error_close;280 281	return fd;282 283error_close:284	save_errno_close(fd);285	return -1;286}287 288int connect_to_addr(int type, const struct sockaddr_storage *addr, socklen_t addrlen,289		    const struct network_helper_opts *opts)290{291	int fd;292 293	if (!opts)294		opts = &default_opts;295 296	fd = client_socket(addr->ss_family, type, opts);297	if (fd < 0) {298		log_err("Failed to create client socket");299		return -1;300	}301 302	if (connect(fd, (const struct sockaddr *)addr, addrlen)) {303		log_err("Failed to connect to server");304		save_errno_close(fd);305		return -1;306	}307 308	return fd;309}310 311int connect_to_addr_str(int family, int type, const char *addr_str, __u16 port,312			const struct network_helper_opts *opts)313{314	struct sockaddr_storage addr;315	socklen_t addrlen;316 317	if (!opts)318		opts = &default_opts;319 320	if (make_sockaddr(family, addr_str, port, &addr, &addrlen))321		return -1;322 323	return connect_to_addr(type, &addr, addrlen, opts);324}325 326int connect_to_fd_opts(int server_fd, const struct network_helper_opts *opts)327{328	struct sockaddr_storage addr;329	socklen_t addrlen, optlen;330	int type;331 332	if (!opts)333		opts = &default_opts;334 335	optlen = sizeof(type);336	if (getsockopt(server_fd, SOL_SOCKET, SO_TYPE, &type, &optlen)) {337		log_err("getsockopt(SOL_TYPE)");338		return -1;339	}340 341	addrlen = sizeof(addr);342	if (getsockname(server_fd, (struct sockaddr *)&addr, &addrlen)) {343		log_err("Failed to get server addr");344		return -1;345	}346 347	return connect_to_addr(type, &addr, addrlen, opts);348}349 350int connect_to_fd(int server_fd, int timeout_ms)351{352	struct network_helper_opts opts = {353		.timeout_ms = timeout_ms,354	};355	socklen_t optlen;356	int protocol;357 358	optlen = sizeof(protocol);359	if (getsockopt(server_fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &optlen)) {360		log_err("getsockopt(SOL_PROTOCOL)");361		return -1;362	}363	opts.proto = protocol;364 365	return connect_to_fd_opts(server_fd, &opts);366}367 368int connect_fd_to_fd(int client_fd, int server_fd, int timeout_ms)369{370	struct sockaddr_storage addr;371	socklen_t len = sizeof(addr);372 373	if (settimeo(client_fd, timeout_ms))374		return -1;375 376	if (getsockname(server_fd, (struct sockaddr *)&addr, &len)) {377		log_err("Failed to get server addr");378		return -1;379	}380 381	if (connect(client_fd, (const struct sockaddr *)&addr, len)) {382		log_err("Failed to connect to server");383		return -1;384	}385 386	return 0;387}388 389int make_sockaddr(int family, const char *addr_str, __u16 port,390		  struct sockaddr_storage *addr, socklen_t *len)391{392	if (family == AF_INET) {393		struct sockaddr_in *sin = (void *)addr;394 395		memset(addr, 0, sizeof(*sin));396		sin->sin_family = AF_INET;397		sin->sin_port = htons(port);398		if (addr_str &&399		    inet_pton(AF_INET, addr_str, &sin->sin_addr) != 1) {400			log_err("inet_pton(AF_INET, %s)", addr_str);401			return -1;402		}403		if (len)404			*len = sizeof(*sin);405		return 0;406	} else if (family == AF_INET6) {407		struct sockaddr_in6 *sin6 = (void *)addr;408 409		memset(addr, 0, sizeof(*sin6));410		sin6->sin6_family = AF_INET6;411		sin6->sin6_port = htons(port);412		if (addr_str &&413		    inet_pton(AF_INET6, addr_str, &sin6->sin6_addr) != 1) {414			log_err("inet_pton(AF_INET6, %s)", addr_str);415			return -1;416		}417		if (len)418			*len = sizeof(*sin6);419		return 0;420	} else if (family == AF_UNIX) {421		/* Note that we always use abstract unix sockets to avoid having422		 * to clean up leftover files.423		 */424		struct sockaddr_un *sun = (void *)addr;425 426		memset(addr, 0, sizeof(*sun));427		sun->sun_family = family;428		sun->sun_path[0] = 0;429		strcpy(sun->sun_path + 1, addr_str);430		if (len)431			*len = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(addr_str);432		return 0;433	}434	return -1;435}436 437char *ping_command(int family)438{439	if (family == AF_INET6) {440		/* On some systems 'ping' doesn't support IPv6, so use ping6 if it is present. */441		if (!system("which ping6 >/dev/null 2>&1"))442			return "ping6";443		else444			return "ping -6";445	}446	return "ping";447}448 449int remove_netns(const char *name)450{451	char *cmd;452	int r;453 454	r = asprintf(&cmd, "ip netns del %s >/dev/null 2>&1", name);455	if (r < 0) {456		log_err("Failed to malloc cmd");457		return -1;458	}459 460	r = system(cmd);461	free(cmd);462	return r;463}464 465int make_netns(const char *name)466{467	char *cmd;468	int r;469 470	r = asprintf(&cmd, "ip netns add %s", name);471	if (r < 0) {472		log_err("Failed to malloc cmd");473		return -1;474	}475 476	r = system(cmd);477	free(cmd);478 479	if (r)480		return r;481 482	r = asprintf(&cmd, "ip -n %s link set lo up", name);483	if (r < 0) {484		log_err("Failed to malloc cmd for setting up lo");485		remove_netns(name);486		return -1;487	}488 489	r = system(cmd);490	free(cmd);491 492	return r;493}494 495struct nstoken {496	int orig_netns_fd;497};498 499struct nstoken *open_netns(const char *name)500{501	int nsfd;502	char nspath[PATH_MAX];503	int err;504	struct nstoken *token;505 506	token = calloc(1, sizeof(struct nstoken));507	if (!token) {508		log_err("Failed to malloc token");509		return NULL;510	}511 512	token->orig_netns_fd = open("/proc/self/ns/net", O_RDONLY);513	if (token->orig_netns_fd == -1) {514		log_err("Failed to open(/proc/self/ns/net)");515		goto fail;516	}517 518	snprintf(nspath, sizeof(nspath), "%s/%s", "/var/run/netns", name);519	nsfd = open(nspath, O_RDONLY | O_CLOEXEC);520	if (nsfd == -1) {521		log_err("Failed to open(%s)", nspath);522		goto fail;523	}524 525	err = setns(nsfd, CLONE_NEWNET);526	close(nsfd);527	if (err) {528		log_err("Failed to setns(nsfd)");529		goto fail;530	}531 532	return token;533fail:534	if (token->orig_netns_fd != -1)535		close(token->orig_netns_fd);536	free(token);537	return NULL;538}539 540void close_netns(struct nstoken *token)541{542	if (!token)543		return;544 545	if (setns(token->orig_netns_fd, CLONE_NEWNET))546		log_err("Failed to setns(orig_netns_fd)");547	close(token->orig_netns_fd);548	free(token);549}550 551int get_socket_local_port(int sock_fd)552{553	struct sockaddr_storage addr;554	socklen_t addrlen = sizeof(addr);555	int err;556 557	err = getsockname(sock_fd, (struct sockaddr *)&addr, &addrlen);558	if (err < 0)559		return err;560 561	if (addr.ss_family == AF_INET) {562		struct sockaddr_in *sin = (struct sockaddr_in *)&addr;563 564		return sin->sin_port;565	} else if (addr.ss_family == AF_INET6) {566		struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&addr;567 568		return sin->sin6_port;569	}570 571	return -1;572}573 574int get_hw_ring_size(char *ifname, struct ethtool_ringparam *ring_param)575{576	struct ifreq ifr = {0};577	int sockfd, err;578 579	sockfd = socket(AF_INET, SOCK_DGRAM, 0);580	if (sockfd < 0)581		return -errno;582 583	memcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));584 585	ring_param->cmd = ETHTOOL_GRINGPARAM;586	ifr.ifr_data = (char *)ring_param;587 588	if (ioctl(sockfd, SIOCETHTOOL, &ifr) < 0) {589		err = errno;590		close(sockfd);591		return -err;592	}593 594	close(sockfd);595	return 0;596}597 598int set_hw_ring_size(char *ifname, struct ethtool_ringparam *ring_param)599{600	struct ifreq ifr = {0};601	int sockfd, err;602 603	sockfd = socket(AF_INET, SOCK_DGRAM, 0);604	if (sockfd < 0)605		return -errno;606 607	memcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));608 609	ring_param->cmd = ETHTOOL_SRINGPARAM;610	ifr.ifr_data = (char *)ring_param;611 612	if (ioctl(sockfd, SIOCETHTOOL, &ifr) < 0) {613		err = errno;614		close(sockfd);615		return -err;616	}617 618	close(sockfd);619	return 0;620}621 622struct send_recv_arg {623	int		fd;624	uint32_t	bytes;625	int		stop;626};627 628static void *send_recv_server(void *arg)629{630	struct send_recv_arg *a = (struct send_recv_arg *)arg;631	ssize_t nr_sent = 0, bytes = 0;632	char batch[1500];633	int err = 0, fd;634 635	fd = accept(a->fd, NULL, NULL);636	while (fd == -1) {637		if (errno == EINTR)638			continue;639		err = -errno;640		goto done;641	}642 643	if (settimeo(fd, 0)) {644		err = -errno;645		goto done;646	}647 648	while (bytes < a->bytes && !READ_ONCE(a->stop)) {649		nr_sent = send(fd, &batch,650			       MIN(a->bytes - bytes, sizeof(batch)), 0);651		if (nr_sent == -1 && errno == EINTR)652			continue;653		if (nr_sent == -1) {654			err = -errno;655			break;656		}657		bytes += nr_sent;658	}659 660	if (bytes != a->bytes) {661		log_err("send %zd expected %u", bytes, a->bytes);662		if (!err)663			err = bytes > a->bytes ? -E2BIG : -EINTR;664	}665 666done:667	if (fd >= 0)668		close(fd);669	if (err) {670		WRITE_ONCE(a->stop, 1);671		return ERR_PTR(err);672	}673	return NULL;674}675 676int send_recv_data(int lfd, int fd, uint32_t total_bytes)677{678	ssize_t nr_recv = 0, bytes = 0;679	struct send_recv_arg arg = {680		.fd	= lfd,681		.bytes	= total_bytes,682		.stop	= 0,683	};684	pthread_t srv_thread;685	void *thread_ret;686	char batch[1500];687	int err = 0;688 689	err = pthread_create(&srv_thread, NULL, send_recv_server, (void *)&arg);690	if (err) {691		log_err("Failed to pthread_create");692		return err;693	}694 695	/* recv total_bytes */696	while (bytes < total_bytes && !READ_ONCE(arg.stop)) {697		nr_recv = recv(fd, &batch,698			       MIN(total_bytes - bytes, sizeof(batch)), 0);699		if (nr_recv == -1 && errno == EINTR)700			continue;701		if (nr_recv == -1) {702			err = -errno;703			break;704		}705		bytes += nr_recv;706	}707 708	if (bytes != total_bytes) {709		log_err("recv %zd expected %u", bytes, total_bytes);710		if (!err)711			err = bytes > total_bytes ? -E2BIG : -EINTR;712	}713 714	WRITE_ONCE(arg.stop, 1);715	pthread_join(srv_thread, &thread_ret);716	if (IS_ERR(thread_ret)) {717		log_err("Failed in thread_ret %ld", PTR_ERR(thread_ret));718		err = err ? : PTR_ERR(thread_ret);719	}720 721	return err;722}723 724#ifdef TRAFFIC_MONITOR725struct tmonitor_ctx {726	pcap_t *pcap;727	pcap_dumper_t *dumper;728	pthread_t thread;729	int wake_fd;730 731	volatile bool done;732	char pkt_fname[PATH_MAX];733	int pcap_fd;734};735 736/* Is this packet captured with a Ethernet protocol type? */737static bool is_ethernet(const u_char *packet)738{739	u16 arphdr_type;740 741	memcpy(&arphdr_type, packet + 8, 2);742	arphdr_type = ntohs(arphdr_type);743 744	/* Except the following cases, the protocol type contains the745	 * Ethernet protocol type for the packet.746	 *747	 * https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL2.html748	 */749	switch (arphdr_type) {750	case 770: /* ARPHRD_FRAD */751	case 778: /* ARPHDR_IPGRE */752	case 803: /* ARPHRD_IEEE80211_RADIOTAP */753		printf("Packet captured: arphdr_type=%d\n", arphdr_type);754		return false;755	}756	return true;757}758 759static const char * const pkt_types[] = {760	"In",761	"B",			/* Broadcast */762	"M",			/* Multicast */763	"C",			/* Captured with the promiscuous mode */764	"Out",765};766 767static const char *pkt_type_str(u16 pkt_type)768{769	if (pkt_type < ARRAY_SIZE(pkt_types))770		return pkt_types[pkt_type];771	return "Unknown";772}773 774/* Show the information of the transport layer in the packet */775static void show_transport(const u_char *packet, u16 len, u32 ifindex,776			   const char *src_addr, const char *dst_addr,777			   u16 proto, bool ipv6, u8 pkt_type)778{779	char *ifname, _ifname[IF_NAMESIZE];780	const char *transport_str;781	u16 src_port, dst_port;782	struct udphdr *udp;783	struct tcphdr *tcp;784 785	ifname = if_indextoname(ifindex, _ifname);786	if (!ifname) {787		snprintf(_ifname, sizeof(_ifname), "unknown(%d)", ifindex);788		ifname = _ifname;789	}790 791	if (proto == IPPROTO_UDP) {792		udp = (struct udphdr *)packet;793		src_port = ntohs(udp->source);794		dst_port = ntohs(udp->dest);795		transport_str = "UDP";796	} else if (proto == IPPROTO_TCP) {797		tcp = (struct tcphdr *)packet;798		src_port = ntohs(tcp->source);799		dst_port = ntohs(tcp->dest);800		transport_str = "TCP";801	} else if (proto == IPPROTO_ICMP) {802		printf("%-7s %-3s IPv4 %s > %s: ICMP, length %d, type %d, code %d\n",803		       ifname, pkt_type_str(pkt_type), src_addr, dst_addr, len,804		       packet[0], packet[1]);805		return;806	} else if (proto == IPPROTO_ICMPV6) {807		printf("%-7s %-3s IPv6 %s > %s: ICMPv6, length %d, type %d, code %d\n",808		       ifname, pkt_type_str(pkt_type), src_addr, dst_addr, len,809		       packet[0], packet[1]);810		return;811	} else {812		printf("%-7s %-3s %s %s > %s: protocol %d\n",813		       ifname, pkt_type_str(pkt_type), ipv6 ? "IPv6" : "IPv4",814		       src_addr, dst_addr, proto);815		return;816	}817 818	/* TCP or UDP*/819 820	flockfile(stdout);821	if (ipv6)822		printf("%-7s %-3s IPv6 %s.%d > %s.%d: %s, length %d",823		       ifname, pkt_type_str(pkt_type), src_addr, src_port,824		       dst_addr, dst_port, transport_str, len);825	else826		printf("%-7s %-3s IPv4 %s:%d > %s:%d: %s, length %d",827		       ifname, pkt_type_str(pkt_type), src_addr, src_port,828		       dst_addr, dst_port, transport_str, len);829 830	if (proto == IPPROTO_TCP) {831		if (tcp->fin)832			printf(", FIN");833		if (tcp->syn)834			printf(", SYN");835		if (tcp->rst)836			printf(", RST");837		if (tcp->ack)838			printf(", ACK");839	}840 841	printf("\n");842	funlockfile(stdout);843}844 845static void show_ipv6_packet(const u_char *packet, u32 ifindex, u8 pkt_type)846{847	char src_buf[INET6_ADDRSTRLEN], dst_buf[INET6_ADDRSTRLEN];848	struct ipv6hdr *pkt = (struct ipv6hdr *)packet;849	const char *src, *dst;850	u_char proto;851 852	src = inet_ntop(AF_INET6, &pkt->saddr, src_buf, sizeof(src_buf));853	if (!src)854		src = "<invalid>";855	dst = inet_ntop(AF_INET6, &pkt->daddr, dst_buf, sizeof(dst_buf));856	if (!dst)857		dst = "<invalid>";858	proto = pkt->nexthdr;859	show_transport(packet + sizeof(struct ipv6hdr),860		       ntohs(pkt->payload_len),861		       ifindex, src, dst, proto, true, pkt_type);862}863 864static void show_ipv4_packet(const u_char *packet, u32 ifindex, u8 pkt_type)865{866	char src_buf[INET_ADDRSTRLEN], dst_buf[INET_ADDRSTRLEN];867	struct iphdr *pkt = (struct iphdr *)packet;868	const char *src, *dst;869	u_char proto;870 871	src = inet_ntop(AF_INET, &pkt->saddr, src_buf, sizeof(src_buf));872	if (!src)873		src = "<invalid>";874	dst = inet_ntop(AF_INET, &pkt->daddr, dst_buf, sizeof(dst_buf));875	if (!dst)876		dst = "<invalid>";877	proto = pkt->protocol;878	show_transport(packet + sizeof(struct iphdr),879		       ntohs(pkt->tot_len),880		       ifindex, src, dst, proto, false, pkt_type);881}882 883static void *traffic_monitor_thread(void *arg)884{885	char *ifname, _ifname[IF_NAMESIZE];886	const u_char *packet, *payload;887	struct tmonitor_ctx *ctx = arg;888	pcap_dumper_t *dumper = ctx->dumper;889	int fd = ctx->pcap_fd, nfds, r;890	int wake_fd = ctx->wake_fd;891	struct pcap_pkthdr header;892	pcap_t *pcap = ctx->pcap;893	u32 ifindex;894	fd_set fds;895	u16 proto;896	u8 ptype;897 898	nfds = (fd > wake_fd ? fd : wake_fd) + 1;899	FD_ZERO(&fds);900 901	while (!ctx->done) {902		FD_SET(fd, &fds);903		FD_SET(wake_fd, &fds);904		r = select(nfds, &fds, NULL, NULL, NULL);905		if (!r)906			continue;907		if (r < 0) {908			if (errno == EINTR)909				continue;910			log_err("Fail to select on pcap fd and wake fd");911			break;912		}913 914		/* This instance of pcap is non-blocking */915		packet = pcap_next(pcap, &header);916		if (!packet)917			continue;918 919		/* According to the man page of pcap_dump(), first argument920		 * is the pcap_dumper_t pointer even it's argument type is921		 * u_char *.922		 */923		pcap_dump((u_char *)dumper, &header, packet);924 925		/* Not sure what other types of packets look like. Here, we926		 * parse only Ethernet and compatible packets.927		 */928		if (!is_ethernet(packet))929			continue;930 931		/* Skip SLL2 header932		 * https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL2.html933		 *934		 * Although the document doesn't mention that, the payload935		 * doesn't include the Ethernet header. The payload starts936		 * from the first byte of the network layer header.937		 */938		payload = packet + 20;939 940		memcpy(&proto, packet, 2);941		proto = ntohs(proto);942		memcpy(&ifindex, packet + 4, 4);943		ifindex = ntohl(ifindex);944		ptype = packet[10];945 946		if (proto == ETH_P_IPV6) {947			show_ipv6_packet(payload, ifindex, ptype);948		} else if (proto == ETH_P_IP) {949			show_ipv4_packet(payload, ifindex, ptype);950		} else {951			ifname = if_indextoname(ifindex, _ifname);952			if (!ifname) {953				snprintf(_ifname, sizeof(_ifname), "unknown(%d)", ifindex);954				ifname = _ifname;955			}956 957			printf("%-7s %-3s Unknown network protocol type 0x%x\n",958			       ifname, pkt_type_str(ptype), proto);959		}960	}961 962	return NULL;963}964 965/* Prepare the pcap handle to capture packets.966 *967 * This pcap is non-blocking and immediate mode is enabled to receive968 * captured packets as soon as possible.  The snaplen is set to 1024 bytes969 * to limit the size of captured content. The format of the link-layer970 * header is set to DLT_LINUX_SLL2 to enable handling various link-layer971 * technologies.972 */973static pcap_t *traffic_monitor_prepare_pcap(void)974{975	char errbuf[PCAP_ERRBUF_SIZE];976	pcap_t *pcap;977	int r;978 979	/* Listen on all NICs in the namespace */980	pcap = pcap_create("any", errbuf);981	if (!pcap) {982		log_err("Failed to open pcap: %s", errbuf);983		return NULL;984	}985	/* Limit the size of the packet (first N bytes) */986	r = pcap_set_snaplen(pcap, 1024);987	if (r) {988		log_err("Failed to set snaplen: %s", pcap_geterr(pcap));989		goto error;990	}991	/* To receive packets as fast as possible */992	r = pcap_set_immediate_mode(pcap, 1);993	if (r) {994		log_err("Failed to set immediate mode: %s", pcap_geterr(pcap));995		goto error;996	}997	r = pcap_setnonblock(pcap, 1, errbuf);998	if (r) {999		log_err("Failed to set nonblock: %s", errbuf);1000		goto error;1001	}1002	r = pcap_activate(pcap);1003	if (r) {1004		log_err("Failed to activate pcap: %s", pcap_geterr(pcap));1005		goto error;1006	}1007	/* Determine the format of the link-layer header */1008	r = pcap_set_datalink(pcap, DLT_LINUX_SLL2);1009	if (r) {1010		log_err("Failed to set datalink: %s", pcap_geterr(pcap));1011		goto error;1012	}1013 1014	return pcap;1015error:1016	pcap_close(pcap);1017	return NULL;1018}1019 1020static void encode_test_name(char *buf, size_t len, const char *test_name, const char *subtest_name)1021{1022	char *p;1023 1024	if (subtest_name)1025		snprintf(buf, len, "%s__%s", test_name, subtest_name);1026	else1027		snprintf(buf, len, "%s", test_name);1028	while ((p = strchr(buf, '/')))1029		*p = '_';1030	while ((p = strchr(buf, ' ')))1031		*p = '_';1032}1033 1034#define PCAP_DIR "/tmp/tmon_pcap"1035 1036/* Start to monitor the network traffic in the given network namespace.1037 *1038 * netns: the name of the network namespace to monitor. If NULL, the1039 *        current network namespace is monitored.1040 * test_name: the name of the running test.1041 * subtest_name: the name of the running subtest if there is. It should be1042 *               NULL if it is not a subtest.1043 *1044 * This function will start a thread to capture packets going through NICs1045 * in the give network namespace.1046 */1047struct tmonitor_ctx *traffic_monitor_start(const char *netns, const char *test_name,1048					   const char *subtest_name)1049{1050	struct nstoken *nstoken = NULL;1051	struct tmonitor_ctx *ctx;1052	char test_name_buf[64];1053	static int tmon_seq;1054	int r;1055 1056	if (netns) {1057		nstoken = open_netns(netns);1058		if (!nstoken)1059			return NULL;1060	}1061	ctx = malloc(sizeof(*ctx));1062	if (!ctx) {1063		log_err("Failed to malloc ctx");1064		goto fail_ctx;1065	}1066	memset(ctx, 0, sizeof(*ctx));1067 1068	encode_test_name(test_name_buf, sizeof(test_name_buf), test_name, subtest_name);1069	snprintf(ctx->pkt_fname, sizeof(ctx->pkt_fname),1070		 PCAP_DIR "/packets-%d-%d-%s-%s.log", getpid(), tmon_seq++,1071		 test_name_buf, netns ? netns : "unknown");1072 1073	r = mkdir(PCAP_DIR, 0755);1074	if (r && errno != EEXIST) {1075		log_err("Failed to create " PCAP_DIR);1076		goto fail_pcap;1077	}1078 1079	ctx->pcap = traffic_monitor_prepare_pcap();1080	if (!ctx->pcap)1081		goto fail_pcap;1082	ctx->pcap_fd = pcap_get_selectable_fd(ctx->pcap);1083	if (ctx->pcap_fd < 0) {1084		log_err("Failed to get pcap fd");1085		goto fail_dumper;1086	}1087 1088	/* Create a packet file */1089	ctx->dumper = pcap_dump_open(ctx->pcap, ctx->pkt_fname);1090	if (!ctx->dumper) {1091		log_err("Failed to open pcap dump: %s", ctx->pkt_fname);1092		goto fail_dumper;1093	}1094 1095	/* Create an eventfd to wake up the monitor thread */1096	ctx->wake_fd = eventfd(0, 0);1097	if (ctx->wake_fd < 0) {1098		log_err("Failed to create eventfd");1099		goto fail_eventfd;1100	}1101 1102	r = pthread_create(&ctx->thread, NULL, traffic_monitor_thread, ctx);1103	if (r) {1104		log_err("Failed to create thread");1105		goto fail;1106	}1107 1108	close_netns(nstoken);1109 1110	return ctx;1111 1112fail:1113	close(ctx->wake_fd);1114 1115fail_eventfd:1116	pcap_dump_close(ctx->dumper);1117	unlink(ctx->pkt_fname);1118 1119fail_dumper:1120	pcap_close(ctx->pcap);1121 1122fail_pcap:1123	free(ctx);1124 1125fail_ctx:1126	close_netns(nstoken);1127 1128	return NULL;1129}1130 1131static void traffic_monitor_release(struct tmonitor_ctx *ctx)1132{1133	pcap_close(ctx->pcap);1134	pcap_dump_close(ctx->dumper);1135 1136	close(ctx->wake_fd);1137 1138	free(ctx);1139}1140 1141/* Stop the network traffic monitor.1142 *1143 * ctx: the context returned by traffic_monitor_start()1144 */1145void traffic_monitor_stop(struct tmonitor_ctx *ctx)1146{1147	__u64 w = 1;1148 1149	if (!ctx)1150		return;1151 1152	/* Stop the monitor thread */1153	ctx->done = true;1154	/* Wake up the background thread. */1155	write(ctx->wake_fd, &w, sizeof(w));1156	pthread_join(ctx->thread, NULL);1157 1158	printf("Packet file: %s\n", strrchr(ctx->pkt_fname, '/') + 1);1159 1160	traffic_monitor_release(ctx);1161}1162#endif /* TRAFFIC_MONITOR */1163