#include <assert.h>
#include <errno.h>
-#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
/** 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,
}
_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;
}
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;
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) {
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)
{
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;
// 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;