Skip to content

Instantly share code, notes, and snippets.

@marten-seemann
Last active July 30, 2023 16:30
Show Gist options
  • Save marten-seemann/36a2558b48d060d063ecb3b005f47320 to your computer and use it in GitHub Desktop.
Save marten-seemann/36a2558b48d060d063ecb3b005f47320 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/udp.h>
#include <unistd.h>
#define PORT 6121
#define BUFFER_SIZE 4096
ssize_t send_with_fallback(int sockfd, const struct sockaddr_in *addr, const char *buffer, size_t len) {
struct msghdr msg = {0};
struct iovec iov = {(char *) buffer, len}; // Cast away the const
char cmsg_buffer[CMSG_SPACE(sizeof(uint16_t))];
struct cmsghdr *cmsg;
msg.msg_name = (void *)addr;
msg.msg_namelen = sizeof(*addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// GSO setup
msg.msg_control = cmsg_buffer;
msg.msg_controllen = sizeof(cmsg_buffer);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = IPPROTO_UDP;
cmsg->cmsg_type = UDP_SEGMENT;
cmsg->cmsg_len = CMSG_LEN(sizeof(uint16_t));
*(uint16_t *)CMSG_DATA(cmsg) = 6; // GSO segment size
printf("Sending with GSO...\n");
ssize_t ret = sendmsg(sockfd, &msg, 0);
if (ret == -1 && errno == EIO) {
printf("GSO failed. Attempting without GSO...\n");
msg.msg_control = NULL;
msg.msg_controllen = 0;
ret = sendmsg(sockfd, &msg, 0);
if (ret == -1) {
printf("Fallback also failed.\n");
}
} else {
printf("Message sent with GSO.\n");
}
return ret;
}
int main() {
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket");
return 1;
}
struct sockaddr_in local_address;
memset(&local_address, 0, sizeof(local_address));
local_address.sin_family = AF_INET;
local_address.sin_port = htons(PORT);
local_address.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sockfd, (struct sockaddr *)&local_address, sizeof(local_address)) == -1) {
perror("bind");
close(sockfd);
return 1;
}
// If enabled, the fallback will NOT work.
// Comment these lines to make it work.
int val = 1;
if (setsockopt(sockfd, IPPROTO_UDP, UDP_SEGMENT, &val, sizeof(val)) < 0) {
perror("setsockopt UDP_SEGMENT");
close(sockfd);
return 1;
}
// Send a message using GSO, with fallback
struct sockaddr_in remote_address;
memset(&remote_address, 0, sizeof(remote_address));
remote_address.sin_family = AF_INET;
remote_address.sin_port = htons(PORT);
inet_pton(AF_INET, "8.8.8.8", &remote_address.sin_addr);
char buffer[BUFFER_SIZE] = "Hello, World!";
if (send_with_fallback(sockfd, &remote_address, buffer, strlen(buffer)) == -1) {
perror("send_with_fallback");
}
close(sockfd);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment