aboutgitcodebugslistschat
path: root/ndp.c
diff options
context:
space:
mode:
authorStefano Brivio <sbrivio@redhat.com>2020-07-21 10:48:24 +0200
committerStefano Brivio <sbrivio@redhat.com>2021-02-16 07:58:05 +0100
commitd02e059ddcc00fba763c995818a5884ed8e97984 (patch)
treeccfe9c33632e6741536ca2d8180f8eec131c56dd /ndp.c
parent6709ade2bde563f31e8d28a27c473fe626216e5d (diff)
downloadpasst-d02e059ddcc00fba763c995818a5884ed8e97984.tar
passt-d02e059ddcc00fba763c995818a5884ed8e97984.tar.gz
passt-d02e059ddcc00fba763c995818a5884ed8e97984.tar.bz2
passt-d02e059ddcc00fba763c995818a5884ed8e97984.tar.lz
passt-d02e059ddcc00fba763c995818a5884ed8e97984.tar.xz
passt-d02e059ddcc00fba763c995818a5884ed8e97984.tar.zst
passt-d02e059ddcc00fba763c995818a5884ed8e97984.zip
passt: Add IPv6 and NDP support, further fixes for IPv4 CT
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Diffstat (limited to 'ndp.c')
-rw-r--r--ndp.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/ndp.c b/ndp.c
new file mode 100644
index 0000000..a15ecc3
--- /dev/null
+++ b/ndp.c
@@ -0,0 +1,133 @@
+/* PASST - Plug A Simple Socket Transport
+ *
+ * ndp.c - NDP support for PASST
+ *
+ * Author: Stefano Brivio <sbrivio@redhat.com>
+ * License: GPLv2
+ *
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
+#include <linux/udp.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <arpa/inet.h>
+
+#include "passt.h"
+#include "util.h"
+
+#define RS 133
+#define RA 134
+#define NS 135
+#define NA 136
+
+/**
+ * ndp() - Check for NDP solicitations, reply as needed
+ * @c: Execution context
+ * @len: Total L2 packet length
+ * @eh: Packet buffer, Ethernet header
+ *
+ * Return: 0 if not handled here, 1 if handled, -1 on failure
+ */
+int ndp(struct ctx *c, unsigned len, struct ethhdr *eh)
+{
+ struct ethhdr *ehr;
+ struct ipv6hdr *ip6h = (struct ipv6hdr *)(eh + 1), *ip6hr;
+ struct icmp6hdr *ih, *ihr;
+ char buf[BUFSIZ] = { 0 };
+ uint8_t proto, *p;
+
+ ih = (struct icmp6hdr *)ipv6_l4hdr(ip6h, &proto);
+ if (!ih)
+ return -1;
+
+ if (proto != IPPROTO_ICMPV6 ||
+ ih->icmp6_type < RS || ih->icmp6_type > NA)
+ return 0;
+
+ ehr = (struct ethhdr *)buf;
+ ip6hr = (struct ipv6hdr *)(ehr + 1);
+ ihr = (struct icmp6hdr *)(ip6hr + 1);
+
+ if (ih->icmp6_type == NS) {
+ fprintf(stderr, "NDP: received NS, sending NA\n");
+ ihr->icmp6_type = NA;
+ ihr->icmp6_code = 0;
+ ihr->icmp6_router = 1;
+ ihr->icmp6_solicited = 1;
+ ihr->icmp6_override = 1;
+
+ p = (unsigned char *)(ihr + 1);
+ memcpy(p, &c->gw6, sizeof(c->gw6)); /* target address */
+ p += 16;
+ *p++ = 2; /* target ll */
+ *p++ = 1; /* length */
+ memcpy(p, c->mac, ETH_ALEN);
+ p += 6;
+ } else if (ih->icmp6_type == RS) {
+ fprintf(stderr, "NDP: received RS, sending RA\n");
+ ihr->icmp6_type = RA;
+ ihr->icmp6_code = 0;
+ ihr->icmp6_rt_lifetime = htons(3600);
+
+ p = (unsigned char *)(ihr + 1);
+ p += 8; /* reachable, retrans time */
+ *p++ = 3; /* prefix */
+ *p++ = 4; /* length */
+ *p++ = 64; /* prefix length */
+ *p++ = 0xc0; /* flags: L, A */
+ *(uint32_t *)p = htonl(3600); /* lifetime */
+ p += 4;
+ *(uint32_t *)p = htonl(3600); /* preferred lifetime */
+ p += 8;
+ memcpy(p, &c->addr6, 8); /* prefix */
+ p += 16;
+
+ *p++ = 25; /* RDNS */
+ *p++ = 3; /* length */
+ p += 2;
+ *(uint32_t *)p = htonl(60); /* lifetime */
+ p += 4;
+ memcpy(p, &c->dns6, 16); /* address */
+ p += 16;
+
+ *p++ = 1; /* source ll */
+ *p++ = 1; /* length */
+ memcpy(p, c->mac, ETH_ALEN);
+ p += 6;
+ } else {
+ return 1;
+ }
+
+ len = (uintptr_t)p - (uintptr_t)ihr - sizeof(*ihr);
+
+ ip6hr->daddr = ip6h->saddr;
+ ip6hr->saddr = c->gw6;
+ ip6hr->payload_len = htons(sizeof(*ihr) + len);
+ ip6hr->hop_limit = IPPROTO_ICMPV6;
+ ihr->icmp6_cksum = 0;
+ ihr->icmp6_cksum = csum_ip4(ip6hr, sizeof(*ip6hr) +
+ sizeof(*ihr) + len);
+
+ ip6hr->version = 6;
+ ip6hr->nexthdr = IPPROTO_ICMPV6;
+ ip6hr->hop_limit = 255;
+
+ len += sizeof(*ehr) + sizeof(*ip6hr) + sizeof(*ihr);
+ memcpy(ehr->h_dest, eh->h_source, ETH_ALEN);
+ memcpy(ehr->h_source, c->mac, ETH_ALEN);
+ ehr->h_proto = htons(ETH_P_IPV6);
+
+ if (send(c->fd_unix, ehr, len, 0) < 0)
+ perror("NDP: send");
+
+ return 1;
+}