// 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. Load more
// 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");