Skip to content

Instantly share code, notes, and snippets.

@YutaroHayakawa
Created October 28, 2019 13:05
Show Gist options
  • Save YutaroHayakawa/35e3cf9bbf9dc40e44dc9b3df92d8e67 to your computer and use it in GitHub Desktop.
Save YutaroHayakawa/35e3cf9bbf9dc40e44dc9b3df92d8e67 to your computer and use it in GitHub Desktop.
Send gso frame using virtio-net header from tap
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stddef.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>
#include <linux/virtio_net.h>
#include <arpa/inet.h>
#define PATH_NET_TUN "/dev/net/tun"
struct eth {
uint8_t dst[6];
uint8_t src[6];
uint16_t type;
};
struct ip {
uint16_t hl:4;
uint16_t v:4;
uint8_t tos;
uint16_t len;
uint16_t id;
uint16_t off;
uint8_t ttl;
uint8_t proto;
uint16_t csum;
uint32_t src;
uint32_t dst;
};
struct tcp {
uint16_t src;
uint16_t dst;
uint32_t seq;
uint32_t ack_seq;
uint16_t res1:4;
uint16_t doff:4;
uint16_t fin:1;
uint16_t syn:1;
uint16_t rst:1;
uint16_t psh:1;
uint16_t ack:1;
uint16_t urg:1;
uint16_t res2:2;
uint16_t window;
uint16_t csum;
uint16_t urg_ptr;
};
#define PKTLEN 5000
static void
die(char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int
tap_open(char *ifname)
{
int error, fd;
unsigned int features;
struct ifreq ifr;
if (ifname == NULL) {
fprintf(stderr, "Invalid argument");
return EINVAL;
}
fd = open(PATH_NET_TUN, O_RDWR);
if (fd < 0) {
die("Failed to open tun device");
}
memset(&ifr, 0, sizeof(ifr));
// ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR;
ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE;
strcpy(ifr.ifr_name, ifname);
error = ioctl(fd, TUNSETIFF, (void *)&ifr);
if (error == -1) {
die("Failed to configure device");
}
ioctl(fd, TUNSETOWNER, 0);
ioctl(fd, TUNSETGROUP, 0);
ioctl(fd, TUNSETPERSIST, 1);
unsigned int offload = TUN_F_CSUM | TUN_F_TSO4;
error = ioctl(fd, TUNSETOFFLOAD, offload);
if (error == -1) {
die("Failed to set offload");
}
system("ip link set tap0 up");
return fd;
}
void
tap_get_hwaddr(int fd, uint8_t *dst)
{
int error;
struct ifreq ifr;
error = ioctl(fd, SIOCGIFHWADDR, (void *)&ifr);
if (error == -1) {
die("Failed to get hwaddr");
}
memcpy(dst, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
}
void
send_one(int fd)
{
struct eth *eth;
struct ip *ip;
struct tcp *tcp;
uint8_t *buf = calloc(1, PKTLEN);
if (buf == NULL) {
die("calloc failed");
}
eth = (struct eth *)buf;
tap_get_hwaddr(fd, eth->src);
memcpy(eth->dst, eth->src, 6);
eth->type = htons(ETH_P_IP);
ip = (struct ip *)(eth + 1);
ip->hl = 5;
ip->v = 4;
ip->tos = 0;
ip->len = htons(PKTLEN - sizeof(*eth));
ip->id = 0;
ip->off = 0;
ip->ttl = 255;
ip->proto = IPPROTO_TCP;
ip->csum = 0;
ip->src = htonl(0x0a00000a);
ip->dst = htonl(0x0a00000a);
tcp = (struct tcp *)(ip + 1);
tcp->src = htons(9090);
tcp->dst = htons(12345);
tcp->seq = htonl(0x0a0a0a0a);
tcp->ack_seq = htonl(0x0b0b0b0b);
tcp->doff = 5;
tcp->psh = 1;
tcp->ack = 1;
tcp->window = 4096;
tcp->csum = 0;
ssize_t wlen = write(fd, buf, PKTLEN);
if (wlen == -1) {
die("writev failed");
}
}
void
send_gso_packet(int fd)
{
struct virtio_net_hdr *vnet;
struct eth *eth;
struct ip *ip;
struct tcp *tcp;
struct iovec iov[2];
uint8_t *buf;
vnet = calloc(1, sizeof(*vnet));
if (vnet == NULL) {
die("Failed to allocate vnet header");
}
vnet->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
vnet->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
vnet->hdr_len = sizeof(*eth) + sizeof(*ip) + sizeof(*tcp);
vnet->gso_size = 1480;
vnet->csum_start = sizeof(*eth) + sizeof(*ip);
vnet->csum_offset = sizeof(*eth) + offsetof(struct ip, csum);
iov[0].iov_base = (uint8_t *)vnet;
iov[0].iov_len = sizeof(*vnet);
buf = calloc(1, PKTLEN);
if (buf == NULL) {
die("calloc failed");
}
eth = (struct eth *)buf;
tap_get_hwaddr(fd, eth->src);
memcpy(eth->dst, eth->src, 6);
eth->type = htons(ETH_P_IP);
ip = (struct ip *)(eth + 1);
ip->hl = 5;
ip->v = 4;
ip->tos = 0;
ip->len = htons(PKTLEN - sizeof(*eth));
ip->id = 0;
ip->off = 0;
ip->ttl = 255;
ip->proto = IPPROTO_TCP;
ip->csum = 0;
ip->src = htonl(0x0a00000a);
ip->dst = htonl(0x0a00000a);
tcp = (struct tcp *)(ip + 1);
tcp->src = htons(23456);
tcp->dst = htons(12345);
tcp->seq = htonl(0x0a0a0a0a);
tcp->ack_seq = htonl(0x0b0b0b0b);
tcp->doff = 5;
tcp->psh = 1;
tcp->ack = 1;
tcp->window = 4096;
tcp->csum = 0;
iov[1].iov_base = buf;
iov[1].iov_len = PKTLEN;
ssize_t wlen = writev(fd, iov, 2);
if (wlen == -1) {
die("writev failed");
}
}
int
main(void)
{
int fd = tap_open("tap0");
// send_gso_packet(fd);
send_one(fd);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment