122 lines
4.2 KiB
C
122 lines
4.2 KiB
C
/*
|
|
* Copyright 2023 Regents of the University of Michigan
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <netpacket/packet.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <linux/netlink.h>
|
|
|
|
#include "../lib/includes.h"
|
|
#include "../lib/logger.h"
|
|
#include "./send.h"
|
|
#include "./send-linux.h"
|
|
|
|
int send_run_init(sock_t s)
|
|
{
|
|
// Get the actual socket
|
|
int sock = s.sock;
|
|
// get source interface index
|
|
struct ifreq if_idx;
|
|
memset(&if_idx, 0, sizeof(struct ifreq));
|
|
if (strlen(zconf.iface) >= IFNAMSIZ) {
|
|
log_error("send", "device interface name (%s) too long\n",
|
|
zconf.iface);
|
|
return EXIT_FAILURE;
|
|
}
|
|
strncpy(if_idx.ifr_name, zconf.iface, IFNAMSIZ - 1);
|
|
if (ioctl(sock, SIOCGIFINDEX, &if_idx) < 0) {
|
|
log_error("send", "%s", "SIOCGIFINDEX");
|
|
return EXIT_FAILURE;
|
|
}
|
|
int ifindex = if_idx.ifr_ifindex;
|
|
|
|
// destination address for the socket
|
|
memset((void *)&sockaddr, 0, sizeof(struct sockaddr_ll));
|
|
sockaddr.sll_ifindex = ifindex;
|
|
sockaddr.sll_halen = ETH_ALEN;
|
|
if (zconf.send_ip_pkts) {
|
|
sockaddr.sll_protocol = htons(ETHERTYPE_IP);
|
|
}
|
|
memcpy(sockaddr.sll_addr, zconf.gw_mac, ETH_ALEN);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int send_batch(sock_t sock, batch_t* batch, int retries) {
|
|
if (batch->len == 0) {
|
|
// nothing to send
|
|
return EXIT_SUCCESS;
|
|
}
|
|
struct mmsghdr msgvec [batch->capacity]; // Array of multiple msg header structures
|
|
struct msghdr msgs[batch->capacity];
|
|
struct iovec iovs[batch->capacity];
|
|
|
|
for (int i = 0; i < batch->len; ++i) {
|
|
struct iovec *iov = &iovs[i];
|
|
iov->iov_base = ((void *)batch->packets) + (i * MAX_PACKET_SIZE);
|
|
iov->iov_len = batch->lens[i];
|
|
struct msghdr *msg = &msgs[i];
|
|
memset(msg, 0, sizeof(struct msghdr));
|
|
// based on https://github.com/torvalds/linux/blob/master/net/socket.c#L2180
|
|
msg->msg_name = (struct sockaddr *)&sockaddr;
|
|
msg->msg_namelen = sizeof(struct sockaddr_ll);
|
|
msg->msg_iov = iov;
|
|
msg->msg_iovlen = 1;
|
|
msgvec[i].msg_hdr = *msg;
|
|
msgvec[i].msg_len = batch->lens[i];
|
|
}
|
|
// set up per-retry variables, so we can only re-submit what didn't send successfully
|
|
struct mmsghdr* current_msg_vec = msgvec;
|
|
int total_packets_sent = 0;
|
|
int num_of_packets_in_batch = batch->len;
|
|
for (int i = 0; i < retries; i++) {
|
|
// according to manpages
|
|
// On success, sendmmsg() returns the number of messages sent from msgvec; if this is less than vlen, the
|
|
// caller can retry with a further sendmmsg() call to send the remaining messages.
|
|
// On error, -1 is returned, and errno is set to indicate the error.
|
|
int rv = sendmmsg(sock.sock, current_msg_vec, num_of_packets_in_batch, 0);
|
|
if (rv < 0) {
|
|
// retry if sending all packets failed
|
|
log_error("batch send", "error in sendmmsg: %s", strerror(errno));
|
|
continue;
|
|
}
|
|
// if rv is positive, it gives the number of packets successfully sent
|
|
total_packets_sent += rv;
|
|
if (rv == num_of_packets_in_batch){
|
|
// all packets in batch were sent successfully
|
|
break;
|
|
}
|
|
// batch send was only partially successful, we'll retry if we have retries available
|
|
log_warn("batch send", "only successfully sent %d packets out of a batch of %d packets", total_packets_sent, batch->len);
|
|
// per the manpages for sendmmsg, packets are sent sequentially and the call returns upon a
|
|
// failure, returning the number of packets successfully sent
|
|
// remove successfully sent packets from batch for retry
|
|
current_msg_vec = &msgvec[total_packets_sent];
|
|
num_of_packets_in_batch = batch->len - total_packets_sent;
|
|
}
|
|
return total_packets_sent;
|
|
}
|