aboutgitcodebugslistschat
path: root/tap.c
diff options
context:
space:
mode:
Diffstat (limited to 'tap.c')
-rw-r--r--tap.c136
1 files changed, 136 insertions, 0 deletions
diff --git a/tap.c b/tap.c
new file mode 100644
index 0000000..f8b8b4f
--- /dev/null
+++ b/tap.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+/* PASST - Plug A Simple Socket Transport
+ *
+ * tap.c - Functions to communicate with guest-facing tap interface
+ *
+ * Copyright (c) 2020-2021 Red Hat GmbH
+ * Author: Stefano Brivio <sbrivio@redhat.com>
+ *
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <stdint.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
+#include <linux/icmpv6.h>
+
+#include "passt.h"
+#include "util.h"
+
+/**
+ * tap_send() - Send frame and qemu socket header with indication of length
+ * @fd: tap file descriptor
+ * @len: Total L2 packet length
+ * @flags: Flags for send(), if any
+ *
+ * Return: return code from send()
+ */
+int tap_send(int fd, void *data, size_t len, int flags)
+{
+ uint32_t vnet_len = htonl(len);
+ send(fd, &vnet_len, 4, 0);
+
+ return send(fd, data, len, flags);
+}
+
+/**
+ * tap_ip_send() - Send IP packet, with L2 headers, calculating L3/L4 checksums
+ * @c: Execution context
+ * @src: IPv6 source address, IPv4-mapped for IPv4 sources
+ * @proto: L4 protocol number
+ * @in: Payload
+ * @len: L4 payload length
+ */
+void tap_ip_send(struct ctx *c, struct in6_addr *src, uint8_t proto,
+ char *in, size_t len)
+{
+ char pkt[USHRT_MAX];
+ struct ethhdr *eh;
+
+ eh = (struct ethhdr *)pkt;
+
+ /* TODO: ARP table lookup */
+ memcpy(eh->h_dest, c->mac_guest, ETH_ALEN);
+ memcpy(eh->h_source, c->mac, ETH_ALEN);
+
+ if (IN6_IS_ADDR_V4MAPPED(src)) {
+ struct iphdr *iph = (struct iphdr *)(eh + 1);
+ char *data = (char *)(iph + 1);
+
+ eh->h_proto = ntohs(ETH_P_IP);
+
+ iph->version = 4;
+ iph->ihl = 5;
+ iph->tos = 0;
+ iph->tot_len = htons(len + 20);
+ iph->id = 0;
+ iph->frag_off = 0;
+ iph->ttl = 255;
+ iph->protocol = proto;
+ iph->daddr = c->addr4;
+ memcpy(&iph->saddr, &src->s6_addr[12], 4);
+
+ iph->check = 0;
+ iph->check = csum_ip4(iph, iph->ihl * 4);
+
+ memcpy(data, in, len);
+
+ if (iph->protocol == IPPROTO_TCP) {
+ csum_tcp4(iph);
+ } else if (iph->protocol == IPPROTO_UDP) {
+ struct udphdr *uh = (struct udphdr *)(iph + 1);
+
+ uh->check = 0;
+ }
+
+ tap_send(c->fd_unix, pkt, len + sizeof(*iph) + sizeof(*eh), 0);
+ } else {
+ struct ipv6hdr *ip6h = (struct ipv6hdr *)(eh + 1);
+ char *data = (char *)(ip6h + 1);
+
+ eh->h_proto = ntohs(ETH_P_IPV6);
+
+ memset(ip6h->flow_lbl, 0, 3);
+ ip6h->payload_len = htons(len);
+ ip6h->priority = 0;
+
+ ip6h->saddr = *src;
+ ip6h->daddr = c->addr6_guest;
+
+ memcpy(data, in, len);
+
+ ip6h->hop_limit = proto;
+ ip6h->version = 0;
+ ip6h->nexthdr = 0;
+ if (proto == IPPROTO_TCP) {
+ struct tcphdr *th = (struct tcphdr *)(ip6h + 1);
+
+ th->check = 0;
+ th->check = csum_ip4(ip6h, len + sizeof(*ip6h));
+ } else if (proto == IPPROTO_UDP) {
+ struct udphdr *uh = (struct udphdr *)(ip6h + 1);
+
+ uh->check = 0;
+ uh->check = csum_ip4(ip6h, len + sizeof(*ip6h));
+ } else if (proto == IPPROTO_ICMPV6) {
+ struct icmp6hdr *ih = (struct icmp6hdr *)(ip6h + 1);
+
+ ih->icmp6_cksum = 0;
+ ih->icmp6_cksum = csum_ip4(ip6h, len + sizeof(*ip6h));
+ }
+ ip6h->version = 6;
+ ip6h->nexthdr = proto;
+ ip6h->hop_limit = 255;
+
+ tap_send(c->fd_unix, pkt, len + sizeof(*ip6h) + sizeof(*eh), 0);
+ }
+}