Ignore Path MTU on Linux when sending UDP packets - c

I am implementing DPLPMTUD and I want to suppress the Linux kernel from returning -1 with errno = EMSGSIZE when I send UDP packet longer than the local interface's MTU. I want to avoid the pain of dealing with error handling when several datagrams are sent out (especially when using sendmmsg(2)), each perhaps belonging to a different connection. I'd rather have the kernel drop the packet and let the application DPLPMTUD logic figure out the MTU.
ip(7) has this to say:
It is possible to implement RFC 4821 MTU probing with SOCK_DGRAM
or SOCK_RAW sockets by setting a value of IP_PMTUDISC_PROBE
(available since Linux 2.6.22). This is also particularly use‐
ful for diagnostic tools such as tracepath(8) that wish to de‐
liberately send probe packets larger than the observed Path MTU.
Yet setting this option does not produce the desired effect. Here is the code to illustrate the problem:
/* emsgsize.c: test whether IP_PMTUDISC_PROBE suppresses EMSGSIZE
*
* Usage: emsgsize packet_size
*/
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#define CHECK(w_, s_) do { if ((s_) < 0) { perror(w_); return 1; }} while (0)
/* Payload */
static unsigned char data[64 * 1024];
int
main (int argc, char **argv)
{
int fd, on, s, size;
struct sockaddr_in si;
ssize_t sent;
if (argc != 2)
{
fprintf(stderr, "usage: emsgsize size\n");
return 1;
}
size = atoi(argv[1]);
memset(&si, 0, sizeof(si));
si.sin_family = AF_INET;
fd = socket(si.sin_family, SOCK_DGRAM, 0);
CHECK("socket", fd);
s = bind(fd, (struct sockaddr *) &si, sizeof(si));
CHECK("bind", s);
/* This is supposed to suppress sendmsg(2) returning -1 with
* errno = EMSGSIZE, see ip(7):
*
" It is possible to implement RFC 4821 MTU probing with SOCK_DGRAM
" or SOCK_RAW sockets by setting a value of IP_PMTUDISC_PROBE
" (available since Linux 2.6.22). This is also particularly use-
" ful for diagnostic tools such as tracepath(8) that wish to de-
" liberately send probe packets larger than the observed Path MTU.
*/
on = IP_PMTUDISC_PROBE;
s = setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &on, sizeof(on));
CHECK("setsockopt", s);
memset(&si, 0, sizeof(si));
si.sin_family = AF_INET;
si.sin_port = htons(12345); /* Destination does not matter */
s = inet_pton(AF_INET, "127.0.0.1", &si.sin_addr);
CHECK("inet_pton", s);
sent = sendto(fd, data, (size_t) size, 0, (struct sockaddr *) &si,
sizeof(si));
CHECK("sendto", sent);
return 0;
}
When I send packets larger than the MTU, sendto() above returns -1 and errno is set to EMSGSIZE -- exactly what I want to avoid.
Is there a way to do what I want?

Related

UDP socket not reading packets

So I have a device connected to my network card and it sends data to port 11678 and address 192.168.121.1 using IPv4 and UDP. I have checked that the device does actually send to that port and address using IPv4 and UDP by calling tcpdump. However my C socket does not receive any packets. Below I have a minimum non-working example that just runs an infinite loop until one packet is received. It does not receive any packets even though tcpdump does, so I assume something is wrong with my code.
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define DEST_ADDR "192.168.121.1"
#define DEST_PORT 11678
#define PACKET_MAXSIZE 1024
int main(int argc, char **argv) {
struct sockaddr_in dest_addr;
bzero(&dest_addr, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
/* create socket */
int fd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); // use SOCK_NONBLOCK?
if (fd < 0) {
perror("Could not create socket.");
}
/* bind port and address to socket */
dest_addr.sin_port = htons(DEST_PORT);
inet_aton(DEST_ADDR, &dest_addr.sin_addr);
int rc = bind(fd, (struct sockaddr*) &dest_addr, sizeof(dest_addr));
if (rc != 0) {
perror("Could not bind socket to local address");
}
/* read packets */
void* buf;
posix_memalign(&buf, 4096, 1024);
while (true) {
ssize_t read_size = read(fd, buf, PACKET_MAXSIZE);
printf("%d\n", read_size);
if (read_size > 0) {
break;
}
}
return 0;
}
The read just returns -1 and sets errno to 11 (EAGAIN) in every iteration. Any help is appreciated. Thanks in advance.
If you're on a system that uses iptables, check that you aren't dropping packets. tcpdump will show packets that are incoming before they get to iptables.
Another thing is that you should be using epoll or select to read from the socket in a more controlled way. EAGAIN isn't neccessarily wrong: it just means there's no data. But you're whizzing round that while loop without waiting, so I'd expect lots of EAGAIN's until something actually arrives at the port.

How do you echo binary data to a listening C program via netcat?

I'm trying to set up a simple communication scheme with a program I wrote using UDP. The program will set up a UDP socket in a nonblocking fashion, then use select() to read non blocking from the socket. Here are the contents of the program:
#include <stdio.h> /* Standard input/output definitions */
#include <stdlib.h> /* UNIX standard function definitions */
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <fcntl.h> /* File control definitions */
#include <signal.h>
#include <time.h>
#include <stdint.h> /* Standard integer types */
#include <string.h> /* String function definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
int setupUdpSocketN(int *fd, uint16_t port_no)
{
struct sockaddr_in my_addr;
// Create UDP socket:
if ( (*fd = socket(AF_INET,SOCK_DGRAM,0)) < 0 )
return -1;
fcntl(*fd, F_SETFL, O_NONBLOCK);
// Bind socket:
memset((char *)&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
my_addr.sin_port = htons(port_no);
if ( bind(*fd, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1 ) {
close(*fd);
return -2;
}
return EXIT_SUCCESS;
}
int main(void) {
int fd, maxfd, nready;
size_t recvlen;
uint8_t buf[256];
fd_set rset;
setupUdpSocketN(&fd, 60000);
maxfd = fd + 1;
while(1) {
FD_ZERO(&rset);
FD_SET(fd, &rset);
if( (nready = select(maxfd, &rset, NULL, NULL, NULL)) < 0 ) {
printf("Error in select: %s\n", strerror(errno));
}
if( FD_ISSET(fd, &rset) ) {
recvlen = recv(fd, buf, 256, 0);
//recvlen = recvfrom(fd, buf, 256, 0, NULL, NULL);
for(int i = 0; i < recvlen; i++) {
printf("%02x ", buf[i]);
}
printf("\n");
}
}
return 0;
}
I then try and send this program binary data using echo, xxd, and netcat:
echo -ne "\xfe\x64\x32\x1a\xb0" | xxd -rp | nc -u localhost 60000
However, this program when run just blocks endlessly. I'm very new to socket programming and unsure how to proceed.
I think the main problem is not with your code but with how you call netcat. When I run your server and use nc -u localhost 60000, I can reproduce your problem: The server is blocking indefinitely without receiving data. However when I force netcat to use IPv4, I can send data to the server successfully:
nc -4 -u localhost 60000
Alternatively, changing your code to use IPv6 and using netcat without the -4 flag also works:
struct sockaddr_in6 my_addr;
...
if ( (*fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0 )
return -1;
...
my_addr.sin6_family = AF_INET6;
my_addr.sin6_addr = in6addr_any;
my_addr.sin6_port = htons(port_no);
To show you how I debugged the problem: I first used netcat to start a UDP server, and send data to the server with a second netcat instance. This worked as expected. I then used strace to inspect the system calls used by the netcat server. The strace output shows netcat using IPv6:
...
socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
bind(3, {sa_family=AF_INET6, sin6_port=htons(60000), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = 0
...
Besides that, there are two miscellaneous problems:
The recv function returns the number of bytes received or -1 in case of an error. For this reason, the type of the return value is ssize_t, instead of size_t. Since your code declares size_t recvlen, when recv returns -1 an integer underflow will occur and recv will be the maximum possible value of type size_t (e.g. ULLONG_MAX = 18446744073709551615 on my platform). This would then result in a buffer overrun, as buf is much smaller.
Your echo/xxd call produces only zeros for me. I'm not sure if this intended. The -r flag for xxd enables "reverse" mode, where xxd will attempt to parse a hexdump (e.g. as produced by xxd without the -r flag) and then reproduces the raw byte sequence that was the input to the original hexdump. But in your case, the echo outputs raw binary data, not an encoded hexdump, hence this data cannot be parsed by xxd in reverse mode.

UNIX socket error : socket operation on non-socket

I am trying to make a client-server communication using UNIX sockets and using the STREAM protocol.
My server is running fine, but my client is not working. Whenever I try to send or receive data, I get an error : "socket operation on non-socket". I really don't see where it comes from, because my server is very similar and I don't have any problem. My server is on my local machine (127.0.0.1) and port 5000. It is open and listening (I checked with the netstat command).
The code is there :
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#define CHECK(ret, mes) if ((ret) == -1) {perror(mes); exit(-1);}
#define STRING_LENGTH 250
int createSocketINETClient(short mode, char *addr, short port)
{
int s;
struct sockaddr_in moi, server;
int moi_len, server_len;
moi.sin_family = AF_INET;
moi.sin_port = htons(port);
moi.sin_addr.s_addr = inet_addr(addr);
memset(moi.sin_zero, 0, 8);
s = socket(AF_INET, mode, 0);
CHECK(s, "socket");
moi_len = sizeof(moi);
CHECK(bind(s, (struct sockaddr*) &moi, moi_len), "bind");
return s;
}
void infoSocket (int s)
{
struct sockaddr_in sock_addr;
socklen_t len = sizeof(sock_addr);
getsockname(s, (struct sockaddr*) &sock_addr, &len);
printf("Onfo of socket %d\n", s);
printf("\t IP : %s\n", inet_ntoa(sock_addr.sin_addr));
printf("\t port : %d\n\n", ntohs(sock_addr.sin_port));
}
int main ()
{
int bytes;
int sock = createSocketINETClient(SOCK_STREAM, "0.0.0.0", 0);
struct sockaddr_in serveurSock;
int client = 0, clientSockLen = 0;
char message[] = "I am a message that is supposed to WORK !!!!\n";
char fromServer[STRING_LENGTH] = "";
infoSocket(sock);
serveurSock.sin_family = AF_INET;
serveurSock.sin_port = htons(5000);
serveurSock.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(serveurSock.sin_zero, 0, 8);
CHECK(connect(sock, (struct sockaddr*) &serveurSock, sizeof(serveurSock)), "connect");
usleep(1000000);
CHECK((bytes = send(client, message, sizeof(message), 0)), "send");
printf("Message sent to server : %d bytes, \"%s\"\n", bytes, message);
CHECK((bytes = recv(client, fromServer, sizeof(fromServer), 0)), "recv");
printf("Message received from server : %d bytes, \"%s\"\n", bytes, fromServer);
close(client);
printf("Client released !\n\n");
return 0;
}
What did I do wrong ?
EDIT : The error comes from this line :
CHECK((bytes = send(client, message, sizeof(message), 0)), "send");
you are using the "client" variable as a socket parameter to sendto() when in fact you should use the "sock" variable.
should you strace the program, you could see the following:
connect(3, {sa_family=AF_INET, sin_port=htons(5000), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
nanosleep({tv_sec=1, tv_nsec=0}, NULL) = 0
sendto(0, "I am a message that is supposed "..., 46, 0, NULL, 0) = -1 ENOTSOCK (Socket operation on non-socket)
note the first sendto() parameter which is 0 (by default this is the stdin file descriptor) when in fact it should be 3 (see connect(...) line)
as a side note, you don't need to bind() the client socket unless you have an explicit reason to do so (using some other route than the default one, bypassing a firewall rule somewhere, etc). the os will assign by default the ip of the network interface the default route goes through and a random free port.
In the posted code you initialize int client = 0 and then I don't see you change it. So when you call send(0, ...) you obviously get that error.
As already mentioned by #EJP in comment to your question it can be a typo because it looks like you really was intended to use sock (as you connected it: connect(sock, ...) instead of client in call to send.

UDP C Server sendto() error: Can't assign requested address

I'm writing a basic Client/Server program in C, using UDP. The idea of the program is that the client sends a message to the server, the server receives it, then echoes it back to the client (the goal being to measure RTT for UDP). Unfortunately, on the server side, when the program attempts to call sendto() to echo the message, I receive the error "Can't assign requested address".
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>
#define SERVER_PORT 7000
#define MAX_PENDING 10
#define MAX_LINE 1024000
int main()
{
struct sockaddr_in sin, sout;
socklen_t soutLen;
char buf[MAX_LINE];
int len;
int msgLen;
int s;
char *msg;
if( (s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0){
perror("could not establish UDP socket");
exit(1);
}
/* build address data structure */
bzero((char *)& sin, sizeof( sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(SERVER_PORT);
if( (bind(s, (struct sockaddr *)&sin, sizeof(sin))) < 0){
perror("udpServer: bind");
exit( 1);
}
while(1){
if((msgLen = recvfrom(s, buf, sizeof(buf), 0,(struct sockaddr *)&sout, &soutLen))<0){
perror("udpServer: recvfrom()");
exit( 1);
}
if( (sendto(s, buf, msgLen, 0, (struct sockaddr *)&sout, sizeof(sout)))<0 ){
perror("udpServer: sendto()");
exit( 1);
}
free(msg);
}
}
Thanks in advance: I'm pretty new to C, so any advice is much appreciated!
The problem is that your sout you pass to sendto is not correct, because you are not correctly setting it's size when passing it to recvfrom:
man recvfrom:
ssize_t
recvfrom(int socket, void *restrict buffer, size_t length,
int flags, struct sockaddr *restrict address,
socklen_t *restrict address_len);
If address is not a null pointer and the socket is not connection-oriented, the source address of the message is filled in. The address_len argument is a
value-result argument, initialized to the size of the buffer associated with address, and modified on return to indicate the actual size of the address
stored there.
When you pass &sout to recvfrom, you also have to tell recvfrom the size of the structure you're passing in so it knows how much data it can write there -- soutLen is both an in parameter and an out parameter. Since you are not initializing soutLen, it probably has some value smaller than the actual size of the structure, which means that what you end up with in sout is not valid.
So you need to initialize soutLen:
struct sockaddr_in sin, sout;
socklen_t soutLen = sizeof(sout);
You should then pass this value as the size to sendto instead of sizeouf(sout) (this may not be required but it's good practice):
if( (sendto(s, buf, msgLen, 0, (struct sockaddr *)&sout, soutLen))<0 ){
Also just as a note, you are freeing msg which you never allocated. This is unrelated but might cause problems later.
Hope this helps.

Sending UDP socket in linux - C language

I know that this is obviously elementary question and I know that there are many tutorials and ready-to-go examples but I must missing something. I am trying to send for example text (char *) via UDP socket to other machine in local network. So far I tried some tutorials like http://gafferongames.com/networking-for-game-programmers/sending-and-receiving-packets/ and so on but I always get error in bind() function with errno "Cannot assign requested address".
I just have some data in char array and I want to push them via network to another host. Could someone please point me to the right direction? Do I need socket server or client? Do I need to bind the socket to some interface?
This is my playground:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>
int handle;
int init_socket()
{
handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (handle <= 0)
{
printf("failed to create socket\n");
return 1;
}
printf("sockets successfully initialized\n");
return 0;
}
int main ()
{
unsigned short port = 30000;
char * data = "hovno";
init_socket();
struct sockaddr_in address;
memset((char *) &address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("192.168.11.129"); // this is address of host which I want to send the socket
address.sin_port = htons(port);
printf("handle: %d\n", handle); // prints number greater than 0 so I assume handle is initialized properly
if (bind(handle, (const struct sockaddr*) &address, sizeof(struct sockaddr_in)) < 0)
{
printf("failed to bind socket (%s)\n", strerror(errno)); // Cannot assign requested address
return 1;
}
int nonBlocking = 1;
if (fcntl(handle, F_SETFL, O_NONBLOCK, nonBlocking) == -1)
{
printf("failed to set non-blocking\n");
return 2;
}
int sent_bytes = sendto(handle, data, strlen(data), 0, (const struct sockaddr*) &address, sizeof(struct sockaddr_in));
if (sent_bytes != strlen(data))
{
printf("failed to send packet\n");
return 3;
}
return 0;
}
bind is called for the local address (one you intend to recv packets to). The IP address must be a local IP address of the machine, or (most frequently) INADDR_ANY.
Normally you don't have to use bind on the client side at all. The system will pick a suitable free port for you automatically.
To specify the remote address for a UDP socket, use sendto, not send.
If you search Google for udp client c code, one of the first results is this one. You can see that the networking part is basically just two calls, socket and sendto.

Resources