2345 lines · 55.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * ipsec.c - Check xfrm on veth inside a net-ns. * Copyright (c) 2018 Dmitry Safonov */ #define _GNU_SOURCE #include <arpa/inet.h>#include <asm/types.h>#include <errno.h>#include <fcntl.h>#include <limits.h>#include <linux/limits.h>#include <linux/netlink.h>#include <linux/random.h>#include <linux/rtnetlink.h>#include <linux/veth.h>#include <linux/xfrm.h>#include <netinet/in.h>#include <net/if.h>#include <sched.h>#include <stdbool.h>#include <stdint.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/mman.h>#include <sys/socket.h>#include <sys/stat.h>#include <sys/syscall.h>#include <sys/types.h>#include <sys/wait.h>#include <time.h>#include <unistd.h> #include "kselftest.h" #define printk(fmt, ...)						\	ksft_print_msg("%d[%u] " fmt "\n", getpid(), __LINE__, ##__VA_ARGS__) #define pr_err(fmt, ...)	printk(fmt ": %m", ##__VA_ARGS__) #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) #ifndef offsetof#define offsetof(TYPE, MEMBER)	__builtin_offsetof(TYPE, MEMBER)#endif #define IPV4_STR_SZ	16	/* xxx.xxx.xxx.xxx is longest + \0 */#define MAX_PAYLOAD	2048#define XFRM_ALGO_KEY_BUF_SIZE	512#define MAX_PROCESSES	(1 << 14) /* /16 mask divided by /30 subnets */#define INADDR_A	((in_addr_t) 0x0a000000) /* 10.0.0.0 */#define INADDR_B	((in_addr_t) 0xc0a80000) /* 192.168.0.0 */ /* /30 mask for one veth connection */#define PREFIX_LEN	30#define child_ip(nr)	(4*nr + 1)#define grchild_ip(nr)	(4*nr + 2) #define VETH_FMT	"ktst-%d"#define VETH_LEN	12 static int nsfd_parent	= -1;static int nsfd_childa	= -1;static int nsfd_childb	= -1;static long page_size; /* * ksft_cnt is static in kselftest, so isn't shared with children. * We have to send a test result back to parent and count there. * results_fd is a pipe with test feedback from children. */static int results_fd[2]; const unsigned int ping_delay_nsec	= 50 * 1000 * 1000;const unsigned int ping_timeout		= 300;const unsigned int ping_count		= 100;const unsigned int ping_success		= 80; struct xfrm_key_entry {	char algo_name[35];	int key_len;}; struct xfrm_key_entry xfrm_key_entries[] = {	{"digest_null", 0},	{"ecb(cipher_null)", 0},	{"cbc(des)", 64},	{"hmac(md5)", 128},	{"cmac(aes)", 128},	{"xcbc(aes)", 128},	{"cbc(cast5)", 128},	{"cbc(serpent)", 128},	{"hmac(sha1)", 160},	{"cbc(des3_ede)", 192},	{"hmac(sha256)", 256},	{"cbc(aes)", 256},	{"cbc(camellia)", 256},	{"cbc(twofish)", 256},	{"rfc3686(ctr(aes))", 288},	{"hmac(sha384)", 384},	{"cbc(blowfish)", 448},	{"hmac(sha512)", 512},	{"rfc4106(gcm(aes))-128", 160},	{"rfc4543(gcm(aes))-128", 160},	{"rfc4309(ccm(aes))-128", 152},	{"rfc4106(gcm(aes))-192", 224},	{"rfc4543(gcm(aes))-192", 224},	{"rfc4309(ccm(aes))-192", 216},	{"rfc4106(gcm(aes))-256", 288},	{"rfc4543(gcm(aes))-256", 288},	{"rfc4309(ccm(aes))-256", 280},	{"rfc7539(chacha20,poly1305)-128", 0}}; static void randomize_buffer(void *buf, size_t buflen){	int *p = (int *)buf;	size_t words = buflen / sizeof(int);	size_t leftover = buflen % sizeof(int); 	if (!buflen)		return; 	while (words--)		*p++ = rand(); 	if (leftover) {		int tmp = rand(); 		memcpy(buf + buflen - leftover, &tmp, leftover);	} 	return;} static int unshare_open(void){	const char *netns_path = "/proc/self/ns/net";	int fd; 	if (unshare(CLONE_NEWNET) != 0) {		pr_err("unshare()");		return -1;	} 	fd = open(netns_path, O_RDONLY);	if (fd <= 0) {		pr_err("open(%s)", netns_path);		return -1;	} 	return fd;} static int switch_ns(int fd){	if (setns(fd, CLONE_NEWNET)) {		pr_err("setns()");		return -1;	}	return 0;} /* * Running the test inside a new parent net namespace to bother less * about cleanup on error-path. */static int init_namespaces(void){	nsfd_parent = unshare_open();	if (nsfd_parent <= 0)		return -1; 	nsfd_childa = unshare_open();	if (nsfd_childa <= 0)		return -1; 	if (switch_ns(nsfd_parent))		return -1; 	nsfd_childb = unshare_open();	if (nsfd_childb <= 0)		return -1; 	if (switch_ns(nsfd_parent))		return -1;	return 0;} static int netlink_sock(int *sock, uint32_t *seq_nr, int proto){	if (*sock > 0) {		seq_nr++;		return 0;	} 	*sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, proto);	if (*sock <= 0) {		pr_err("socket(AF_NETLINK)");		return -1;	} 	randomize_buffer(seq_nr, sizeof(*seq_nr)); 	return 0;} static inline struct rtattr *rtattr_hdr(struct nlmsghdr *nh){	return (struct rtattr *)((char *)(nh) + RTA_ALIGN((nh)->nlmsg_len));} static int rtattr_pack(struct nlmsghdr *nh, size_t req_sz,		unsigned short rta_type, const void *payload, size_t size){	/* NLMSG_ALIGNTO == RTA_ALIGNTO, nlmsg_len already aligned */	struct rtattr *attr = rtattr_hdr(nh);	size_t nl_size = RTA_ALIGN(nh->nlmsg_len) + RTA_LENGTH(size); 	if (req_sz < nl_size) {		printk("req buf is too small: %zu < %zu", req_sz, nl_size);		return -1;	}	nh->nlmsg_len = nl_size; 	attr->rta_len = RTA_LENGTH(size);	attr->rta_type = rta_type;	if (payload)		memcpy(RTA_DATA(attr), payload, size); 	return 0;} static struct rtattr *_rtattr_begin(struct nlmsghdr *nh, size_t req_sz,		unsigned short rta_type, const void *payload, size_t size){	struct rtattr *ret = rtattr_hdr(nh); 	if (rtattr_pack(nh, req_sz, rta_type, payload, size))		return 0; 	return ret;} static inline struct rtattr *rtattr_begin(struct nlmsghdr *nh, size_t req_sz,		unsigned short rta_type){	return _rtattr_begin(nh, req_sz, rta_type, 0, 0);} static inline void rtattr_end(struct nlmsghdr *nh, struct rtattr *attr){	char *nlmsg_end = (char *)nh + nh->nlmsg_len; 	attr->rta_len = nlmsg_end - (char *)attr;} static int veth_pack_peerb(struct nlmsghdr *nh, size_t req_sz,		const char *peer, int ns){	struct ifinfomsg pi;	struct rtattr *peer_attr; 	memset(&pi, 0, sizeof(pi));	pi.ifi_family	= AF_UNSPEC;	pi.ifi_change	= 0xFFFFFFFF; 	peer_attr = _rtattr_begin(nh, req_sz, VETH_INFO_PEER, &pi, sizeof(pi));	if (!peer_attr)		return -1; 	if (rtattr_pack(nh, req_sz, IFLA_IFNAME, peer, strlen(peer)))		return -1; 	if (rtattr_pack(nh, req_sz, IFLA_NET_NS_FD, &ns, sizeof(ns)))		return -1; 	rtattr_end(nh, peer_attr); 	return 0;} static int netlink_check_answer(int sock){	struct nlmsgerror {		struct nlmsghdr hdr;		int error;		struct nlmsghdr orig_msg;	} answer; 	if (recv(sock, &answer, sizeof(answer), 0) < 0) {		pr_err("recv()");		return -1;	} else if (answer.hdr.nlmsg_type != NLMSG_ERROR) {		printk("expected NLMSG_ERROR, got %d", (int)answer.hdr.nlmsg_type);		return -1;	} else if (answer.error) {		printk("NLMSG_ERROR: %d: %s",			answer.error, strerror(-answer.error));		return answer.error;	} 	return 0;} static int veth_add(int sock, uint32_t seq, const char *peera, int ns_a,		const char *peerb, int ns_b){	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE;	struct {		struct nlmsghdr		nh;		struct ifinfomsg	info;		char			attrbuf[MAX_PAYLOAD];	} req;	const char veth_type[] = "veth";	struct rtattr *link_info, *info_data; 	memset(&req, 0, sizeof(req));	req.nh.nlmsg_len	= NLMSG_LENGTH(sizeof(req.info));	req.nh.nlmsg_type	= RTM_NEWLINK;	req.nh.nlmsg_flags	= flags;	req.nh.nlmsg_seq	= seq;	req.info.ifi_family	= AF_UNSPEC;	req.info.ifi_change	= 0xFFFFFFFF; 	if (rtattr_pack(&req.nh, sizeof(req), IFLA_IFNAME, peera, strlen(peera)))		return -1; 	if (rtattr_pack(&req.nh, sizeof(req), IFLA_NET_NS_FD, &ns_a, sizeof(ns_a)))		return -1; 	link_info = rtattr_begin(&req.nh, sizeof(req), IFLA_LINKINFO);	if (!link_info)		return -1; 	if (rtattr_pack(&req.nh, sizeof(req), IFLA_INFO_KIND, veth_type, sizeof(veth_type)))		return -1; 	info_data = rtattr_begin(&req.nh, sizeof(req), IFLA_INFO_DATA);	if (!info_data)		return -1; 	if (veth_pack_peerb(&req.nh, sizeof(req), peerb, ns_b))		return -1; 	rtattr_end(&req.nh, info_data);	rtattr_end(&req.nh, link_info); 	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {		pr_err("send()");		return -1;	}	return netlink_check_answer(sock);} static int ip4_addr_set(int sock, uint32_t seq, const char *intf,		struct in_addr addr, uint8_t prefix){	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE;	struct {		struct nlmsghdr		nh;		struct ifaddrmsg	info;		char			attrbuf[MAX_PAYLOAD];	} req; 	memset(&req, 0, sizeof(req));	req.nh.nlmsg_len	= NLMSG_LENGTH(sizeof(req.info));	req.nh.nlmsg_type	= RTM_NEWADDR;	req.nh.nlmsg_flags	= flags;	req.nh.nlmsg_seq	= seq;	req.info.ifa_family	= AF_INET;	req.info.ifa_prefixlen	= prefix;	req.info.ifa_index	= if_nametoindex(intf); #ifdef DEBUG	{		char addr_str[IPV4_STR_SZ] = {}; 		strncpy(addr_str, inet_ntoa(addr), IPV4_STR_SZ - 1); 		printk("ip addr set %s", addr_str);	}#endif 	if (rtattr_pack(&req.nh, sizeof(req), IFA_LOCAL, &addr, sizeof(addr)))		return -1; 	if (rtattr_pack(&req.nh, sizeof(req), IFA_ADDRESS, &addr, sizeof(addr)))		return -1; 	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {		pr_err("send()");		return -1;	}	return netlink_check_answer(sock);} static int link_set_up(int sock, uint32_t seq, const char *intf){	struct {		struct nlmsghdr		nh;		struct ifinfomsg	info;		char			attrbuf[MAX_PAYLOAD];	} req; 	memset(&req, 0, sizeof(req));	req.nh.nlmsg_len	= NLMSG_LENGTH(sizeof(req.info));	req.nh.nlmsg_type	= RTM_NEWLINK;	req.nh.nlmsg_flags	= NLM_F_REQUEST | NLM_F_ACK;	req.nh.nlmsg_seq	= seq;	req.info.ifi_family	= AF_UNSPEC;	req.info.ifi_change	= 0xFFFFFFFF;	req.info.ifi_index	= if_nametoindex(intf);	req.info.ifi_flags	= IFF_UP;	req.info.ifi_change	= IFF_UP; 	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {		pr_err("send()");		return -1;	}	return netlink_check_answer(sock);} static int ip4_route_set(int sock, uint32_t seq, const char *intf,		struct in_addr src, struct in_addr dst){	struct {		struct nlmsghdr	nh;		struct rtmsg	rt;		char		attrbuf[MAX_PAYLOAD];	} req;	unsigned int index = if_nametoindex(intf); 	memset(&req, 0, sizeof(req));	req.nh.nlmsg_len	= NLMSG_LENGTH(sizeof(req.rt));	req.nh.nlmsg_type	= RTM_NEWROUTE;	req.nh.nlmsg_flags	= NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE;	req.nh.nlmsg_seq	= seq;	req.rt.rtm_family	= AF_INET;	req.rt.rtm_dst_len	= 32;	req.rt.rtm_table	= RT_TABLE_MAIN;	req.rt.rtm_protocol	= RTPROT_BOOT;	req.rt.rtm_scope	= RT_SCOPE_LINK;	req.rt.rtm_type		= RTN_UNICAST; 	if (rtattr_pack(&req.nh, sizeof(req), RTA_DST, &dst, sizeof(dst)))		return -1; 	if (rtattr_pack(&req.nh, sizeof(req), RTA_PREFSRC, &src, sizeof(src)))		return -1; 	if (rtattr_pack(&req.nh, sizeof(req), RTA_OIF, &index, sizeof(index)))		return -1; 	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {		pr_err("send()");		return -1;	} 	return netlink_check_answer(sock);} static int tunnel_set_route(int route_sock, uint32_t *route_seq, char *veth,		struct in_addr tunsrc, struct in_addr tundst){	if (ip4_addr_set(route_sock, (*route_seq)++, "lo",			tunsrc, PREFIX_LEN)) {		printk("Failed to set ipv4 addr");		return -1;	} 	if (ip4_route_set(route_sock, (*route_seq)++, veth, tunsrc, tundst)) {		printk("Failed to set ipv4 route");		return -1;	} 	return 0;} static int init_child(int nsfd, char *veth, unsigned int src, unsigned int dst){	struct in_addr intsrc = inet_makeaddr(INADDR_B, src);	struct in_addr tunsrc = inet_makeaddr(INADDR_A, src);	struct in_addr tundst = inet_makeaddr(INADDR_A, dst);	int route_sock = -1, ret = -1;	uint32_t route_seq; 	if (switch_ns(nsfd))		return -1; 	if (netlink_sock(&route_sock, &route_seq, NETLINK_ROUTE)) {		printk("Failed to open netlink route socket in child");		return -1;	} 	if (ip4_addr_set(route_sock, route_seq++, veth, intsrc, PREFIX_LEN)) {		printk("Failed to set ipv4 addr");

Lines 1–500 of 2345.