zmap-mark-ii/src/send-bsd.h

116 lines
3.7 KiB
C

/*
* ZMap Copyright 2013 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
*/
#ifndef ZMAP_SEND_BSD_H
#define ZMAP_SEND_BSD_H
#include <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include "send.h"
#include "../lib/includes.h"
#include <netinet/in.h>
#include <net/bpf.h>
#ifdef ZMAP_SEND_LINUX_H
#error "Don't include both send-bsd.h and send-linux.h"
#endif
int send_run_init(UNUSED sock_t sock)
{
// Don't need to do anything on BSD-like variants
return EXIT_SUCCESS;
}
int send_packet(sock_t sock, void *buf, int len, UNUSED uint32_t retry_ct)
{
if (zconf.send_ip_pkts) {
struct ip *iph = (struct ip *)buf;
#if defined(__APPLE__) || (defined(__FreeBSD__) && __FreeBSD_version < 1100030)
// Early BSD's raw IP sockets required IP headers to have len
// and off fields in host byte order, as they were being byte
// swapped on the way out. Getting byte order wrong would
// result in EINVAL from sendto(2) below.
// Most modern BSD systems have fixed this and removed the byte
// swapping on raw IP sockets, while some, notably macOS, still
// require header fields in host byte order.
// See ip(4) for details on byte order requirements of raw IP
// sockets.
// Swap byte order on the first send attempt for a given packet
// (retry_ct == 0), and rely on the caller to pass us the same
// buffer again for retries (retry_ct > 0), with the buffer
// still containing the header fields in corrected byte order.
if (retry_ct == 0) {
iph->ip_len = ntohs(iph->ip_len);
iph->ip_off = ntohs(iph->ip_off);
iph->ip_sum = 0;
}
#endif
struct sockaddr_in sai;
bzero(&sai, sizeof(sai));
sai.sin_family = AF_INET;
sai.sin_addr.s_addr = iph->ip_dst.s_addr;
return sendto(sock.sock, buf, len, 0, (struct sockaddr *)&sai, sizeof(sai));
} else {
return write(sock.sock, buf, len);
}
}
// macOS doesn't have sendmmsg as of Sonoma. Since we want a uniform interface, we'll emulate the send_batch used in Linux.
// FreeBSD does have sendmmsg, but it is a libc wrapper around the sendmsg syscall, without the perf benefits of sendmmsg.
// The behavior in sendmmsg is to send as many packets as possible until one fails, and then return the number of sent packets.
// Following the same pattern for consistency
// Returns - number of packets sent
// Returns -1 and sets errno if no packets could be sent successfully
int send_batch(sock_t sock, batch_t* batch, int retries) {
if (batch->len == 0) {
// nothing to send
return EXIT_SUCCESS;
}
int packets_sent = 0;
int rc = 0;
for (int packet_num = 0; packet_num < batch->len; packet_num++) {
for (int retry_ct = 0; retry_ct < retries; retry_ct++) {
rc = send_packet(sock, ((uint8_t *)batch->packets) + (packet_num * MAX_PACKET_SIZE), batch->lens[packet_num], retry_ct);
if (rc >= 0) {
packets_sent++;
break;
}
}
if (rc < 0) {
// packet couldn't be sent in retries number of attempts
struct in_addr addr;
addr.s_addr = batch->ips[packet_num];
char addr_str_buf[INET_ADDRSTRLEN];
const char *addr_str =
inet_ntop(
AF_INET, &addr,
addr_str_buf,
INET_ADDRSTRLEN);
if (addr_str != NULL) {
log_debug( "send-bsd", "send_packet failed for %s. %s", addr_str,
strerror( errno));
}
}
}
if (packets_sent == 0) {
// simulating the return behaviour of the Linux send_mmsg sys call on error. Returns -1 and leaves
// errno as set by send_packet
return -1;
}
return packets_sent;
}
#endif /* ZMAP_SEND_BSD_H */