/* * QEMU System Emulator * * Copyright (c) 2003-2008 Fabrice Bellard * Copyright (c) 2012-2014 Cisco Systems * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "qemu/osdep.h" #include <linux/ip.h> #include <netdb.h> #include "net/net.h" #include "clients.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/sockets.h" #include "qemu/iov.h" #include "qemu/main-loop.h" /* The buffer size needs to be investigated for optimum numbers and * optimum means of paging in on different systems. This size is * chosen to be sufficient to accommodate one packet with some headers */ #define BUFFER_ALIGN sysconf(_SC_PAGESIZE) #define BUFFER_SIZE 2048 #define IOVSIZE 2 #define MAX_L2TPV3_MSGCNT 64 #define MAX_L2TPV3_IOVCNT (MAX_L2TPV3_MSGCNT * IOVSIZE) /* Header set to 0x30000 signifies a data packet */ #define L2TPV3_DATA_PACKET 0x30000 /* IANA-assigned IP protocol ID for L2TPv3 */ #ifndef IPPROTO_L2TP #define IPPROTO_L2TP 0x73 #endif typedef struct NetL2TPV3State { NetClientState nc; int fd; /* * these are used for xmit - that happens packet a time * and for first sign of life packet (easier to parse that once) */ uint8_t *header_buf; struct iovec *vec; /* * these are used for receive - try to "eat" up to 32 packets at a time */ struct mmsghdr *msgvec; /* * peer address */ struct sockaddr_storage *dgram_dst; uint32_t dst_size; /* * L2TPv3 parameters */ uint64_t rx_cookie; uint64_t tx_cookie; uint32_t rx_session; uint32_t tx_session; uint32_t header_size; uint32_t counter; /* * DOS avoidance in error handling */ bool header_mismatch; /* * Ring buffer handling */ int queue_head; int queue_tail; int queue_depth; /* * Precomputed offsets */ uint32_t offset; uint32_t cookie_offset; uint32_t counter_offset; uint32_t session_offset; /* Poll Control */ bool read_poll; bool write_poll; /* Flags */ bool ipv6; bool udp; bool has_counter; bool pin_counter; bool cookie; bool cookie_is_64; } NetL2TPV3State; static void net_l2tpv3_send(void *opaque); static void l2tpv3_writable(void *opaque); static void l2tpv3_update_fd_handler(NetL2TPV3State *s) { qemu_set_fd_handler(s->fd, s->read_poll ? net_l2tpv3_send : NULL, s->write_poll ? l2tpv3_writable : NULL, s); } static void l2tpv3_read_poll(NetL2TPV3State *s, bool enable) { if (s->read_poll != enable) { s->read_poll = enable; l2tpv3_update_fd_handler(s); } } static void l2tpv3_write_poll(NetL2TPV3State *s, bool enable) { if (s->write_poll != enable) { s->write_poll = enable; l2tpv3_update_fd_handler(s); } } static void l2tpv3_writable(void *opaque) { NetL2TPV3State *s = opaque; l2tpv3_write_poll(s, false); qemu_flush_queued_packets(&s->nc); } static void l2tpv3_send_completed(NetClientState *nc, ssize_t len) { NetL2TPV3State *s = DO_UPCAST(NetL2TPV3State, nc, nc); l2tpv3_read_poll(s, true); } static void l2tpv3_poll(NetClientState *nc, bool enable) { NetL2TPV3State *s = DO_UPCAST(NetL2TPV3State, nc, nc); l2tpv3_write_poll(s, enable); l2tpv3_read_poll(s, enable); } static void l2tpv3_form_header(NetL2TPV3State *s) { uint32_t *counter; if (s->udp) { stl_be_p((uint32_t *) s->header_buf, L2TPV3_DATA_PACKET); } stl_be_p( (uint32_t *) (s->header_buf + s->session_offset), s->tx_session ); if (s->cookie) { if (s->cookie_is_64) { stq_be_p( (uint64_t *)(s->header_buf + s->cookie_offset), s->tx_cookie ); } else { stl_be_p( (uint32_t *) (s->header_buf + s->cookie_offset), s->tx_cookie ); } } if (s->has_counter) { counter = (uint32_t *)(s->header_buf + s->counter_offset); if (s->pin_counter) { *counter = 0; } else { stl_be_p(counter, ++s->counter); } } } static ssize_t net_l2tpv3_receive_dgram_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) { NetL2TPV3State *s = DO_UPCAST(NetL2TPV3State, nc, nc); struct msghdr message; int ret; if (iovcnt > MAX_L2TPV3_IOVCNT - 1) { error_report( "iovec too long %d > %d, change l2tpv3.h", iovcnt, MAX_L2TPV3_IOVCNT ); return -1; } l2tpv3_form_header(s); memcpy(s->vec + 1, iov, iovcnt * sizeof(struct iovec)); s->vec->iov_base = s->header_buf; s->vec->iov_len = s->offset; message.msg_name = s->dgram_dst; message.msg_namelen = s->dst_size; message.msg_iov = s->vec; message.msg_iovlen = iovcnt + 1; message.msg_control = NULL; message.msg_controllen = 0; message.msg_flags = 0; do { ret = sendmsg(s->fd, &message, 0); } while ((ret == -1) && (errno == EINTR)); if (ret > 0) { ret -= s->offset; } else if (ret == 0) { /* belt and braces - should not occur on DGRAM * we should get an error and never a 0 send */ ret = iov_size(iov, iovcnt); } else { /* signal upper layer that socket buffer is full */ ret = -errno; if (ret == -EAGAIN || ret == -ENOBUFS) { l2tpv3_write_poll(s, true); ret = 0; } } return ret; } static ssize_t net_l2tpv3_receive_dgram(NetClientState *nc, const uint8_t *buf, size_t size) { NetL2TPV3State *s = DO_UPCAST(NetL2TPV3State, nc, nc); struct iovec *vec; struct msghdr message; ssize_t ret = 0; l2tpv3_form_header(s); vec = s->vec; vec->iov_base = s->header_buf; vec->iov_len = s->offset; vec++; vec->iov_base = (void *) buf; vec->iov_len = size; message.msg_name = s->dgram_dst; message.msg_namelen = s->dst_size; message.msg_iov = s->vec; message.msg_iovlen = 2; message.msg_control = NULL; message.msg_controllen = 0; message.msg_flags = 0; do { ret = sendmsg(s->fd, &message, 0); } while ((ret == -1) && (errno == EINTR)); if (ret > 0) { ret -= s->offset; } else if (ret == 0) { /* belt and braces - should not occur on DGRAM * we should get an error and never a 0 send */ ret = size; } else { ret = -errno; if (ret == -EAGAIN || ret == -ENOBUFS) { /* signal upper layer that socket buffer is full */ l2tpv3_write_poll(s, true); ret = 0; } } return ret; } static int l2tpv3_verify_header(NetL2TPV3State *s, uint8_t *buf) { uint32_t *session; uint64_t cookie; if ((!s->udp) && (!s->ipv6)) { buf += sizeof(struct iphdr) /* fix for ipv4 raw */; } /* we do not do a strict check for "data" packets as per * the RFC spec because the pure IP spec does not have * that anyway. */ if (s->cookie) { if (s->cookie_is_64) { cookie = ldq_be_p(buf + s->cookie_offset); } else { cookie = ldl_be_p(buf + s->cookie_offset) & 0xffffffffULL; } if (cookie != s->rx_cookie) { if (!s->header_mismatch) { error_report("unknown cookie id"); } return -1; } } session = (uint32_t *) (buf + s->session_offset); if (ldl_be_p(session) != s->rx_session) { if (!s->header_mismatch) { error_report("session mismatch"); } return -1; } return 0; } static void net_l2tpv3_process_queue(NetL2TPV3State *s) { int size = 0; struct iovec *vec; bool bad_read; int data_size; struct mmsghdr *msgvec; /* go into ring mode only if there is a "pending" tail */ if (s->queue_depth > 0) { do { msgvec = s->msgvec + s->queue_tail; if (msgvec->msg_len > 0) { data_size = msgvec->msg_len - s->header_size; vec = msgvec->msg_hdr.msg_iov; if ((data_size > 0) && (l2tpv3_verify_header(s, vec->iov_base) == 0)) { vec++; /* Use the legacy delivery for now, we will * switch to using our own ring as a queueing mechanism * at a later date */ size = qemu_send_packet_async( &s->nc, vec->iov_base, data_size, l2tpv3_send_completed ); if (size == 0) { l2tpv3_read_poll(s, false); } bad_read = false; } else { bad_read = true; if (!s->header_mismatch) { /* report error only once */ error_report("l2tpv3 header verification failed"); s->header_mismatch = true; } } } else { bad_read = true; } s->queue_tail = (s->queue_tail + 1) % MAX_L2TPV3_MSGCNT; s->queue_depth--; } while ( (s->queue_depth > 0) && qemu_can_send_packet(&s->nc) && ((size > 0) || bad_read) ); } } static void net_l2tpv3_send(void *opaque) { NetL2TPV3State *s = opaque; int target_count, count; struct mmsghdr *msgvec; /* go into ring mode only if there is a "pending" tail */ if (s->queue_depth) { /* The ring buffer we use has variable intake * count of how much we can read varies - adjust accordingly */ target_count = MAX_L2TPV3_MSGCNT - s->queue_depth; /* Ensure we do not overrun the ring when we have * a lot of enqueued packets */ if (s->queue_head + target_count > MAX_L2TPV3_MSGCNT) { target_count = MAX_L2TPV3_MSGCNT - s->queue_head; } } else { /* we do not have any pending packets - we can use * the whole message vector linearly instead of using * it as a ring */ s->queue_head = 0; s->queue_tail = 0; target_count = MAX_L2TPV3_MSGCNT; } msgvec = s->msgvec + s->queue_head; if (target_count > 0) { do { count = recvmmsg( s->fd, msgvec, target_count, MSG_DONTWAIT, NULL); } while ((count == -1) && (errno == EINTR)); if (count < 0) { /* Recv error - we still need to flush packets here, * (re)set queue head to current position */ count = 0; } s->queue_head = (s->queue_head + count) % MAX_L2TPV3_MSGCNT; s->queue_depth += count; } net_l2tpv3_process_queue(s); } static void destroy_vector(struct mmsghdr *msgvec, int count, int iovcount) { int i, j; struct iovec *iov; struct mmsghdr *cleanup = msgvec; if (cleanup) { for (i = 0; i < count; i++) { if (cleanup->msg_hdr.msg_iov) { iov = cleanup->msg_hdr.msg_iov; for (j = 0; j < iovcount; j++) { g_free(iov->iov_base); iov++; } g_free(cleanup->msg_hdr.msg_iov); } cleanup++; } g_free(msgvec); } } static struct mmsghdr *build_l2tpv3_vector(NetL2TPV3State *s, int count) { int i; struct iovec *iov; struct mmsghdr *msgvec, *result; msgvec = g_new(struct mmsghdr, count); result = msgvec; for (i = 0; i < count ; i++) { msgvec->msg_hdr.msg_name = NULL; msgvec->msg_hdr.msg_namelen = 0; iov = g_new(struct iovec, IOVSIZE); msgvec->msg_hdr.msg_iov = iov; iov->iov_base = g_malloc(s->header_size); iov->iov_len = s->header_size; iov++ ; iov->iov_base = qemu_memalign(BUFFER_ALIGN, BUFFER_SIZE); iov->iov_len = BUFFER_SIZE; msgvec->msg_hdr.msg_iovlen = 2; msgvec->msg_hdr.msg_control = NULL; msgvec->msg_hdr.msg_controllen = 0; msgvec->msg_hdr.msg_flags = 0; msgvec++; } return result; } static void net_l2tpv3_cleanup(NetClientState *nc) { NetL2TPV3State *s = DO_UPCAST(NetL2TPV3State, nc, nc); qemu_purge_queued_packets(nc); l2tpv3_read_poll(s, false); l2tpv3_write_poll(s, false); if (s->fd >= 0) { close(s->fd); } destroy_vector(s->msgvec, MAX_L2TPV3_MSGCNT, IOVSIZE); g_free(s->vec); g_free(s->header_buf); g_free(s->dgram_dst); } static NetClientInfo net_l2tpv3_info = { .type = NET_CLIENT_DRIVER_L2TPV3, .size = sizeof(NetL2TPV3State), .receive = net_l2tpv3_receive_dgram, .receive_iov = net_l2tpv3_receive_dgram_iov, .poll = l2tpv3_poll, .cleanup = net_l2tpv3_cleanup, }; int net_init_l2tpv3(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { const NetdevL2TPv3Options *l2tpv3; NetL2TPV3State *s; NetClientState *nc; int fd = -1, gairet; struct addrinfo hints; struct addrinfo *result = NULL; char *srcport, *dstport; nc = qemu_new_net_client(&net_l2tpv3_info, peer, "l2tpv3", name); s = DO_UPCAST(NetL2TPV3State, nc, nc); s->queue_head = 0; s->queue_tail = 0; s->header_mismatch = false; assert(netdev->type == NET_CLIENT_DRIVER_L2TPV3); l2tpv3 = &netdev->u.l2tpv3; if (l2tpv3->has_ipv6 && l2tpv3->ipv6) { s->ipv6 = l2tpv3->ipv6; } else { s->ipv6 = false; } if ((l2tpv3->has_offset) && (l2tpv3->offset > 256)) { error_setg(errp, "offset must be less than 256 bytes"); goto outerr; } if (l2tpv3->has_rxcookie || l2tpv3->has_txcookie) { if (l2tpv3->has_rxcookie && l2tpv3->has_txcookie) { s->cookie = true; } else { error_setg(errp, "require both 'rxcookie' and 'txcookie' or neither"); goto outerr; } } else { s->cookie = false; } if (l2tpv3->has_cookie64 || l2tpv3->cookie64) { s->cookie_is_64 = true; } else { s->cookie_is_64 = false; } if (l2tpv3->has_udp && l2tpv3->udp) { s->udp = true; if (!(l2tpv3->has_srcport && l2tpv3->has_dstport)) { error_setg(errp, "need both src and dst port for udp"); goto outerr; } else { srcport = l2tpv3->srcport; dstport = l2tpv3->dstport; } } else { s->udp = false; srcport = NULL; dstport = NULL; } s->offset = 4; s->session_offset = 0; s->cookie_offset = 4; s->counter_offset = 4; s->tx_session = l2tpv3->txsession; if (l2tpv3->has_rxsession) { s->rx_session = l2tpv3->rxsession; } else { s->rx_session = s->tx_session; } if (s->cookie) { s->rx_cookie = l2tpv3->rxcookie; s->tx_cookie = l2tpv3->txcookie; if (s->cookie_is_64 == true) { /* 64 bit cookie */ s->offset += 8; s->counter_offset += 8; } else { /* 32 bit cookie */ s->offset += 4; s->counter_offset += 4; } } memset(&hints, 0, sizeof(hints)); if (s->ipv6) { hints.ai_family = AF_INET6; } else { hints.ai_family = AF_INET; } if (s->udp) { hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = 0; s->offset += 4; s->counter_offset += 4; s->session_offset += 4; s->cookie_offset += 4; } else { hints.ai_socktype = SOCK_RAW; hints.ai_protocol = IPPROTO_L2TP; } gairet = getaddrinfo(l2tpv3->src, srcport, &hints, &result); if ((gairet != 0) || (result == NULL)) { error_setg(errp, "could not resolve src, errno = %s", gai_strerror(gairet)); goto outerr; } fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (fd == -1) { fd = -errno; error_setg(errp, "socket creation failed, errno = %d", -fd); goto outerr; } if (bind(fd, (struct sockaddr *) result->ai_addr, result->ai_addrlen)) { error_setg(errp, "could not bind socket err=%i", errno); goto outerr; } freeaddrinfo(result); memset(&hints, 0, sizeof(hints)); if (s->ipv6) { hints.ai_family = AF_INET6; } else { hints.ai_family = AF_INET; } if (s->udp) { hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = 0; } else { hints.ai_socktype = SOCK_RAW; hints.ai_protocol = IPPROTO_L2TP; } result = NULL; gairet = getaddrinfo(l2tpv3->dst, dstport, &hints, &result); if ((gairet != 0) || (result == NULL)) { error_setg(errp, "could not resolve dst, error = %s", gai_strerror(gairet)); goto outerr; } s->dgram_dst = g_new0(struct sockaddr_storage, 1); memcpy(s->dgram_dst, result->ai_addr, result->ai_addrlen); s->dst_size = result->ai_addrlen; freeaddrinfo(result); if (l2tpv3->has_counter && l2tpv3->counter) { s->has_counter = true; s->offset += 4; } else { s->has_counter = false; } if (l2tpv3->has_pincounter && l2tpv3->pincounter) { s->has_counter = true; /* pin counter implies that there is counter */ s->pin_counter = true; } else { s->pin_counter = false; } if (l2tpv3->has_offset) { /* extra offset */ s->offset += l2tpv3->offset; } if ((s->ipv6) || (s->udp)) { s->header_size = s->offset; } else { s->header_size = s->offset + sizeof(struct iphdr); } s->msgvec = build_l2tpv3_vector(s, MAX_L2TPV3_MSGCNT); s->vec = g_new(struct iovec, MAX_L2TPV3_IOVCNT); s->header_buf = g_malloc(s->header_size); qemu_set_nonblock(fd); s->fd = fd; s->counter = 0; l2tpv3_read_poll(s, true); snprintf(s->nc.info_str, sizeof(s->nc.info_str), "l2tpv3: connected"); return 0; outerr: qemu_del_net_client(nc); if (fd >= 0) { close(fd); } if (result) { freeaddrinfo(result); } return -1; }