From: Libor Peltan Date: Mon, 23 Dec 2019 15:50:34 +0000 (+0100) Subject: xdp: support for ipv6 X-Git-Tag: embedded_lmdb~35^2~79 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9a3f7fdbed7db5df5781d07a47a925dde8fee773;p=thirdparty%2Fknot-dns.git xdp: support for ipv6 --- diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c index b8ac182d1d..9465fdec68 100644 --- a/src/knot/server/udp-handler.c +++ b/src/knot/server/udp-handler.c @@ -406,7 +406,7 @@ static int xdp_recvmmsg_handle(udp_context_t *ctx, void *d, void *xdp_sock) struct iovec *rx = &rq->msgs_rx[i].payload; struct iovec *tx = &rq->msgs_tx[i].payload; - *tx = knot_xsk_alloc_frame(xdp_sock); + *tx = knot_xsk_alloc_frame(xdp_sock, rq->msgs_rx[i].ip_to.ss_family == AF_INET6); if (tx->iov_base == NULL) { return KNOT_ERROR; } diff --git a/src/libknot/xdp/af_xdp.c b/src/libknot/xdp/af_xdp.c index f48eae2478..46e30042d5 100644 --- a/src/libknot/xdp/af_xdp.c +++ b/src/libknot/xdp/af_xdp.c @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -65,14 +64,16 @@ /** The memory layout of each umem frame. */ struct umem_frame { - union { uint8_t bytes[FRAME_SIZE]; struct { + union { uint8_t bytes[FRAME_SIZE]; union { struct udpv4 udpv4; + struct udpv6 udpv6; }; }; }; -static const size_t FRAME_PAYLOAD_OFFSET = offsetof(struct udpv4, data) + offsetof(struct umem_frame, udpv4); +static const size_t FRAME_PAYLOAD_OFFSET4 = offsetof(struct udpv4, data) + offsetof(struct umem_frame, udpv4); +static const size_t FRAME_PAYLOAD_OFFSET6 = offsetof(struct udpv6, data) + offsetof(struct umem_frame, udpv6); static const struct xsk_umem_config global_umem_config = { .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, @@ -128,14 +129,15 @@ static struct umem_frame *xsk_alloc_umem_frame(struct xsk_umem_info *umem) } _public_ -struct iovec knot_xsk_alloc_frame(struct knot_xsk_socket *socket) +struct iovec knot_xsk_alloc_frame(struct knot_xsk_socket *socket, bool ipv6) { struct iovec res = { 0 }; + size_t ofs = ipv6 ? FRAME_PAYLOAD_OFFSET6 : FRAME_PAYLOAD_OFFSET4; struct umem_frame *uframe = xsk_alloc_umem_frame(socket->umem); if (uframe != NULL) { - res.iov_len = MIN(UINT16_MAX, FRAME_SIZE - FRAME_PAYLOAD_OFFSET - 4/*eth CRC*/); - res.iov_base = uframe->udpv4.data; + res.iov_len = MIN(UINT16_MAX, FRAME_SIZE - ofs - 4/*eth CRC*/); + res.iov_base = ipv6 ? uframe->udpv6.data : uframe->udpv4.data; } return res; } @@ -271,6 +273,26 @@ static __be16 pkt_ipv4_checksum_2(const struct iphdr *h) return ~BS16(from32to16(sum32)); } +static void udp_checksum1(size_t *result, const void *_data, size_t _data_len) +{ + assert(!(_data_len & 1)); + const uint16_t *data = _data; + size_t len = _data_len / 2; + while (len-- > 0) { + *result += *data++; + } +} + +static void udp_checksum_finish(size_t *result) +{ + while (*result > 0xffff) { + *result = (*result & 0xffff) + (*result >> 16); + } + if (*result != 0xffff) { + *result = ~*result; + } +} + static int pkt_send(struct knot_xsk_socket *xsk, uint64_t addr, uint32_t len) { uint32_t tx_idx; @@ -304,8 +326,7 @@ static uint8_t *msg_uframe_p(struct knot_xsk_socket *socket, const knot_xsk_msg_ return uframe_p; } -_public_ -int knot_xsk_sendmsg(struct knot_xsk_socket *socket, const knot_xsk_msg_t *msg) +int xsk_sendmsg_ipv4(struct knot_xsk_socket *socket, const knot_xsk_msg_t *msg) { uint8_t *uframe_p = msg_uframe_p(socket, msg); if (uframe_p == NULL) { @@ -347,11 +368,76 @@ int knot_xsk_sendmsg(struct knot_xsk_socket *socket, const knot_xsk_msg_t *msg) memcpy(h->eth.h_source, msg->eth_from, sizeof(msg->eth_from)); h->eth.h_proto = BS16(ETH_P_IP); - uint32_t eth_len = FRAME_PAYLOAD_OFFSET + msg->payload.iov_len + 4/*CRC*/; + uint32_t eth_len = FRAME_PAYLOAD_OFFSET4 + msg->payload.iov_len + 4/*CRC*/; + // TODO compute eth CRC (also for IPv6) + + return pkt_send(socket, h->bytes - socket->umem->frames->bytes, eth_len); +} + +int xsk_sendmsg_ipv6(struct knot_xsk_socket *socket, const knot_xsk_msg_t *msg) +{ + uint8_t *uframe_p = msg_uframe_p(socket, msg); + if (uframe_p == NULL) { + return KNOT_EINVAL; + } + + struct umem_frame *uframe = (struct umem_frame *)uframe_p; + struct udpv6 *h = &uframe->udpv6; + + // sockaddr* contents is already in network byte order + const struct sockaddr_in6 *src_v6 = (const struct sockaddr_in6 *)&msg->ip_from; + const struct sockaddr_in6 *dst_v6 = (const struct sockaddr_in6 *)&msg->ip_to; + + const uint16_t udp_len = sizeof(h->udp) + msg->payload.iov_len; + h->udp.len = BS16(udp_len); + h->udp.source = src_v6->sin6_port; + h->udp.dest = dst_v6->sin6_port; + h->udp.check = 0; + + h->ipv6.version = 6; + h->ipv6.payload_len = BS16(udp_len); + memset(h->ipv6.flow_lbl, 0, sizeof(h->ipv6.flow_lbl)); + h->ipv6.hop_limit = IPDEFTTL; + h->ipv6.nexthdr = 17; // UDP + h->ipv6.priority = 0; + + memcpy(&h->ipv6.saddr, &src_v6->sin6_addr, sizeof(src_v6->sin6_addr)); + memcpy(&h->ipv6.daddr, &dst_v6->sin6_addr, sizeof(dst_v6->sin6_addr)); + + // UDP checksum is mandatory for IPv6, in contrast to IPv4 + size_t chk = 0; + udp_checksum1(&chk, &h->ipv6.saddr, sizeof(h->ipv6.saddr)); + udp_checksum1(&chk, &h->ipv6.daddr, sizeof(h->ipv6.daddr)); + udp_checksum1(&chk, &h->udp.len, sizeof(h->udp.len)); + __be16 version = BS16(h->ipv6.nexthdr); + udp_checksum1(&chk, &version, sizeof(version)); + udp_checksum1(&chk, &h->udp, sizeof(h->udp)); + udp_checksum1(&chk, msg->payload.iov_base, msg->payload.iov_len); + udp_checksum_finish(&chk); + h->udp.check = chk; // TODO check for mistakes ? + + memcpy(h->eth.h_dest, msg->eth_to, sizeof(msg->eth_to)); + memcpy(h->eth.h_source, msg->eth_from, sizeof(msg->eth_from)); + h->eth.h_proto = BS16(ETH_P_IPV6); + + uint32_t eth_len = FRAME_PAYLOAD_OFFSET6 + msg->payload.iov_len + 4/*CRC*/; return pkt_send(socket, h->bytes - socket->umem->frames->bytes, eth_len); } +_public_ +int knot_xsk_sendmsg(struct knot_xsk_socket *socket, const knot_xsk_msg_t *msg) +{ + switch (msg->ip_from.ss_family) { + case AF_INET: + return xsk_sendmsg_ipv4(socket, msg); + case AF_INET6: + return xsk_sendmsg_ipv6(socket, msg); + default: + return KNOT_EINVAL; + } +} + _public_ int knot_xsk_sendmmsg(struct knot_xsk_socket *socket, const knot_xsk_msg_t msgs[], uint32_t count) { @@ -431,9 +517,12 @@ static int rx_desc(struct knot_xsk_socket *xsi, const struct xdp_desc *desc, udp = (struct udphdr *)(uframe_p + sizeof(struct ethhdr) + ipv4->ihl * 4); } else if (eth->h_proto == BS16(ETH_P_IPV6)) { - (void)ipv6; - ret = KNOT_ENOTSUP; // FIXME later - goto free_frame; + ipv6 = (struct ipv6hdr *)(uframe_p + sizeof(struct ethhdr)); + if (ipv6->version != 6) { + ret = KNOT_EFEWDATA; + goto free_frame; + } + udp = (struct udphdr *)(uframe_p + sizeof(struct ethhdr) + sizeof(struct ipv6hdr)); // TODO ??? } else { ret = KNOT_ENOTSUP; goto free_frame; @@ -450,13 +539,27 @@ static int rx_desc(struct knot_xsk_socket *xsi, const struct xdp_desc *desc, // process the packet; ownership is passed on, but beware of holding frames // LATER: filter the address-port combinations that we listen on? - assert(ipv4); - struct sockaddr_in *src_v4 = (struct sockaddr_in *)&msg->ip_from; - struct sockaddr_in *dst_v4 = (struct sockaddr_in *)&msg->ip_to; - memcpy(&src_v4->sin_addr, &ipv4->saddr, sizeof(src_v4->sin_addr)); - memcpy(&dst_v4->sin_addr, &ipv4->daddr, sizeof(dst_v4->sin_addr)); - src_v4->sin_port = udp->source; - dst_v4->sin_port = udp->dest; + if (ipv4) { + struct sockaddr_in *src_v4 = (struct sockaddr_in *)&msg->ip_from; + struct sockaddr_in *dst_v4 = (struct sockaddr_in *)&msg->ip_to; + memcpy(&src_v4->sin_addr, &ipv4->saddr, sizeof(src_v4->sin_addr)); + memcpy(&dst_v4->sin_addr, &ipv4->daddr, sizeof(dst_v4->sin_addr)); + src_v4->sin_port = udp->source; + dst_v4->sin_port = udp->dest; + src_v4->sin_family = AF_INET; + dst_v4->sin_family = AF_INET; + } else { + assert(ipv6); + struct sockaddr_in6 *src_v6 = (struct sockaddr_in6 *)&msg->ip_from; + struct sockaddr_in6 *dst_v6 = (struct sockaddr_in6 *)&msg->ip_to; + memcpy(&src_v6->sin6_addr, &ipv6->saddr, sizeof(src_v6->sin6_addr)); + memcpy(&dst_v6->sin6_addr, &ipv6->daddr, sizeof(dst_v6->sin6_addr)); + src_v6->sin6_port = udp->source; + dst_v6->sin6_port = udp->dest; + src_v6->sin6_family = AF_INET6; + dst_v6->sin6_family = AF_INET6; + // TODO shall we anyhow handle flow info ? + } return KNOT_EOK; diff --git a/src/libknot/xdp/af_xdp.h b/src/libknot/xdp/af_xdp.h index 5455147c8e..e2a9be3dc6 100644 --- a/src/libknot/xdp/af_xdp.h +++ b/src/libknot/xdp/af_xdp.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include @@ -35,7 +36,7 @@ int knot_xsk_init(struct knot_xsk_socket **socket, const char *ifname, int if_qu void knot_xsk_deinit(struct knot_xsk_socket *socket); -struct iovec knot_xsk_alloc_frame(struct knot_xsk_socket *socket); +struct iovec knot_xsk_alloc_frame(struct knot_xsk_socket *socket, bool ipv6); int knot_xsk_sendmsg(struct knot_xsk_socket *socket, const knot_xsk_msg_t *msg); // msg->payload MUST have been allocated by knot_xsk_alloc_frame() diff --git a/src/libknot/xdp/bpf-kernel.c b/src/libknot/xdp/bpf-kernel.c index 31007a43b2..7894ab1290 100644 --- a/src/libknot/xdp/bpf-kernel.c +++ b/src/libknot/xdp/bpf-kernel.c @@ -31,7 +31,7 @@ int xdp_redirect_udp_func(struct xdp_md *ctx) { struct ethhdr *eth; struct iphdr *iphdr; - //struct ipv6hdr *ipv6hdr; + struct ipv6hdr *ipv6hdr; struct udphdr *udphdr; void *data_end = (void *)(long)ctx->data_end; @@ -42,11 +42,9 @@ int xdp_redirect_udp_func(struct xdp_md *ctx) case ETH_P_IP: ip_type = parse_iphdr(&nh, data_end, &iphdr); break; - /* case ETH_P_IPV6: ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr); break; - */ default: return XDP_PASS; } diff --git a/src/libknot/xdp/bpf-user.h b/src/libknot/xdp/bpf-user.h index e7f30b8a79..ccce985f5e 100644 --- a/src/libknot/xdp/bpf-user.h +++ b/src/libknot/xdp/bpf-user.h @@ -25,8 +25,6 @@ #include #include -//#include // LATER: split kresd-specific stuff - struct udpv4 { union { uint8_t bytes[1]; struct { @@ -38,6 +36,17 @@ struct udpv4 { } __attribute__((packed)); }; }; +struct udpv6 { + union { uint8_t bytes[1]; struct { + + struct ethhdr eth; // no VLAN support; CRC at the "end" of .data! + struct ipv6hdr ipv6; + struct udphdr udp; + uint8_t data[]; + + } __attribute__((packed)); }; +}; + /** Data around one network interface. */ struct kxsk_iface { const char *ifname;