Created
October 28, 2019 13:05
-
-
Save YutaroHayakawa/35e3cf9bbf9dc40e44dc9b3df92d8e67 to your computer and use it in GitHub Desktop.
Send gso frame using virtio-net header from tap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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