My intent is to write a function which takes as parameter a buffer holding an entire ethernet frame and sends it to a raw socket (so needed only for transmission).
Here the obvious steps:
sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
// ...
write(sockfd, buffer, buffer_len);
// ...
close(sockfd);
But the write function fails with an EXNIO error code: "No such device or address". I grab the packet content from a wireshark session, so it should be well formatted.
There are several examples on internet about sending a raw eth packet, but I haven't found anything using write() instead of sendto(), which requires the sockaddr_ll struct to be filled.
Has anyone experienced the same issue? Is using sendto() the only way to accomplish the task?
Thanks.
Note: the program runs as root.
Here is a part of code which worked for me. In my understanding of things, write will not work as it is supposed to send a stream of chars. It can write to a file or to a TCP connection or similar. I think that raw packets are very different.
int sock;
sock = socket(PF_INET, SOCK_RAW, 0);
if (sock < 0) {
printf("Can't get socket\n");
exit(1);
}
/* Insist that we have header included */
int one = 1;
if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, &one, sizeof (one)) < 0) {
printf ("Cannot set IP_HDRINCL!\n");
exit(1);
}
...
struct sockaddr_in sockin;
sockin.sin_family = AF_INET;
sockin.sin_port = dest_port;
sockin.sin_addr.s_addr = dest_ip;
...
sendto (sock, buffer, bufferlen, 0, (struct sockaddr *) &sockin, sizeof (sockin));
...
close(sock);
Related
I have trouble reading RTP packets from a multicast socket which is opened using
the following function:
int
open_multicast_socket
(const char *group_address,
uint16_t port)
{
assert(group_address != NULL);
int
s;
if (-1 != (s = socket(
AF_INET, SOCK_DGRAM, 0
)))
{
int
reuse = 1;
if (-1 != setsockopt(
s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse
))
{
struct sockaddr_in
sock_addr;
bzero(&sock_addr, sizeof sock_addr);
if (1 == inet_pton(
AF_INET, group_address, &sock_addr.sin_addr
))
{
sock_addr.sin_family = AF_INET;
sock_addr.sin_port = htons(port);
if (0 == bind(
s, (struct sockaddr*)&sock_addr, sizeof sock_addr
))
{
struct ip_mreq
mreq = {
.imr_multiaddr.s_addr = inet_addr(group_address),
.imr_interface.s_addr = htonl(INADDR_ANY)
};
if (0 == setsockopt(
s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof mreq
))
{
//fcntl(s, F_SETFL, O_NONBLOCK);
return s;
} // setsockopt
else
{
perror("setsockopt");
close(s);
}
} // bind
else
{
perror("bind");
close(s);
}
} // inet_pton
else
{
perror("inet_pton");
close(s);
}
} // setsockopt
else
{
perror("setsockopt");
close(s);
}
} // socket
else
{
perror("socket");
}
return -1;
}
If I read RTP header plus payload in one read operation, I get the entire
packet. However, if I attempt to receive the RTP header first, then - a custom
header in the payload - the 2nd read always gets a next RTP header instead,
discarding all attached data. Because payload length may vary, the only way to
receive a whole packet, it seems, is to guess its max possible size.
I tried to get a number of available bytes before reading:
ioctl(sock, FIONREAD, &nbytes);
but it always returns 0.
Polling on the socket always fails, as if no data is available at all.
When non-blocking is enabled (i.e. fcntl(sock, F_SETFL, O_NONBLOCK);) - read
always fails (-1), so does recv(sock, buf, buf_len, MSG_DONTWAIT).
So is there a way to properly parse RTP packets via consequensive non-blocking
read calls?
Non-blocking is essential, because it should be possible to check whether a connection was lost and re-open the socket if necessary.
Unlike TCP which is a stream based protocol, UDP is a packet based protocol. This means that whenever you read from a UDP socket (multicast or not) you'll get exactly one UDP datagram. If your buffer isn't big enough to hold the entire datagram, the remaining data is essentially lost.
Make sure your buffer is big enough to hold a complete datagram. If your network supports jumbo frames end-to-end that means your buffer should be 9000 bytes, otherwise it should be 1500 bytes.
One should read the complete buffer from the socket and then parse them.
One can create a buffer of MTU size, read from the socket to this temp buffer and then parser the complete buffer and then take action.
One can use select() or poll() to check if the data is present in the socket. Read it when it is available.
I wrote the code in order to handle receiving UDP packets. The packets are all same length(120 bytes), and about 1,000 packets are coming in every second. Simply, my code is like this.
int sock = -1;
int flag = 0;
int nRead = 0;
#define LOCAL_BUFF_SIZE (8192)
char buff[LOCAL_BUFF_SIZE];
struct sockaddr_in sockAddr;
memset((void *)&sockAddr, 0x00, sizeof(struct sockaddr_in));
if((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
{
/* Print error and terminate */
}
/* Make it non-blocking */
flag = fcntl( sock, F_GETFL, 0 );
fcntl( sock, F_SETFL, flag | O_NONBLOCK );
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(portNum);
sockAddr.sin_addr.s_addr = INADDR_ANY;
if(bind(sock, (struct sockaddr *)&sockAddr, sizeof (sockAddr)) < 0)
{
/* Print error and terminate */
}
while(...)
{
nRead = recv(sock, buff, LOCAL_BUFF_SIZE, 0);
if(nBytes > 0)
{
/* Process the data */
}
else
{
/* If it's error, handle error */
}
}
When I wrote this code, I expect that recv() function returns every bytes in the UDP socket buffer at that moment, but, it seems that it only returns one packet(120 byte) every time even though there are more bytes in the buffer. So now I encountered with packet loss. I know that there are many other ways to solve this problem, but, for now reading all existent bytes in the UDP buffer at once is the easiest way for me. So, is there any way to read all bytes in the UDP buffer at once?
Thanks in advance
UDP is a message oriented protocol, therefore, you are getting single message in one recv operation. You can possible use recvmmsg() system call to receive multiple messages in a single call.
I am trying to make a simple UDP multicast example where a message is sent from one program and received from the other but right now the output is only:
Connected
Message Sent
and
bind
setup multicast
Can someone please tell me what I am missing so that I can receive the message successfully? Thank you!! Here are the codes in full:
int main(int argc, char *argv[])
{
int udp_socket_info;
struct sockaddr_in udp_server;
char* message="test";
//create socket
udp_socket_info = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_socket_info == -1) {
puts("Could not create socket");
}
//assign local values
udp_server.sin_addr.s_addr = inet_addr("225.0.0.37"); //multicast address
udp_server.sin_family = AF_INET;
udp_server.sin_port = htons( 1100 );
//checks connection
if (connect(udp_socket_info, (struct sockaddr *)&udp_server, sizeof(udp_server)) < 0) {
perror("Connection error");
}
puts("Connected");
//sends message
if( sendto(udp_socket_info , message , strlen(message) , 0, (struct sockaddr *)&udp_server, sizeof(udp_server)) < 0) {
perror("Send failed");
}
puts("Message Sent");
}
and the second program is
int main(int argc, char *argv[])
{
//initialize udp socket and structures
int udp_socket_info;
struct sockaddr_in udp_server;
struct sockaddr addr;
struct ip_mreq mreq;
socklen_t fromlen;
fromlen = sizeof addr;
char incoming_message[100];
//create udp socket
udp_socket_info = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_socket_info == -1) {
puts("Could not create socket");
}
// set up
memset((char*)&udp_server,0,sizeof(udp_server));
udp_server.sin_family=AF_INET;
udp_server.sin_port = htons( 1100 );
udp_server.sin_addr.s_addr = inet_addr("192.168.0.100"); //local address
// bind
if (bind(udp_socket_info,(struct sockaddr *)&udp_server, sizeof(udp_server)) < 0) {
perror("bind error");
exit (1);
}
puts("bind");
// use setsockopt() to join multicast group
mreq.imr_multiaddr.s_addr=inet_addr("225.0.0.37"); //multicast address
mreq.imr_interface.s_addr= htonl(INADDR_ANY); //can use local address here too
if (setsockopt(udp_socket_info, IPPROTO_IP,IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
perror("setsockopt");
exit (1);
}
puts("setup multicast");
//Receive an incoming message
if( recvfrom(udp_socket_info, incoming_message , sizeof(incoming_message), 0, &addr, &fromlen) < 0) {
puts("Received failed");
exit (1);
}
puts("Message received");
puts(incoming_message);
}
You should bind the receiving socket to INADDR_ANY, not a local interface address. Otherwise you run the risk that the sender is out there via a different route and can't reach your socket. On some platforms you can bind it to the multicast address itself.
NB when you get an error it isn't sufficient to print a message of your own devising. The message must contain the errno, or the result of strerror(). For example, call perror().
Your receiver should not bind to a local address. It should instead bind to either INADDR_ANY or the multicast address you intend on joining. Binding to a local address breaks multicast on Linux systems.
Note that if you bind to a multicast address, this means you'll only receive packets for that multicast address. If you want to receive from multiple multicast addresses or if you also want to receive unicast packets then you need to bind to INADDR_ANY.
When joining a multicast group, using INADDR_ANY causes you to join the specified group on the default network interface. It's generally a good idea to explicitly specify an interface.
As EJP mentioned, you should always use perror to print error messages from any system or library call to ensure that a meaningful error message is printed.
Wireshark is an important tool for programs such as this. It helps you ensure that packets are going out and coming in the network interfaces you expect.
Also, if the sender and receive are on different network segments, you'll need to set the TTL via the IP_MULTICAST_TTL socket option. You also need to make sure that any routers between them are configured to pass multicast traffic.
I have really strange problem. I made server and client communicating using unicast sockets.
Server is sending text file in blocks of 512 bytes and client is receiving and writing blocks to a txt file. But I noticed that without sleep(1) function on both sides server can not send the whole file. For example the file is made of 2939 blocks and in wireshark I can see just 1827, 2005, 1657 but never the whole file , all 2939 blocks ? Why ? Packets are not lost because server and client are connected via ethernet cable to the router and are on the same local network.
Can you give me directions and advices how to solve this problem? Here is the code :
#includes...
#define BUF_SIZE 512
#define PORT 1234
#define IP "192.168.0.103" // address of a client
static int val = 1;
int sent=0;
int main() {
struct sockaddr_in client,server;
char tmp[BUF_SIZE];
int n,s,fd;
ssize_t numRead;
int rv,optval=1;
if((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))<0){
printf("Opening datagram socket error",strerror(errno));
return 1;
}else{
printf("Opening datagram socket....OK.\n");
}
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1){
printf("Setsockopt error: %s", strerror(errno));
return 1;
}
memset((char *) &server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(PORT);
memset((char *) &client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_addr.s_addr = inet_addr(IP);
client.sin_port = htons(PORT);
if(bind(s, (struct sockaddr*)&server, sizeof(server))){
printf("Binding datagram socket error %s",strerror(errno));
close(s);
return 1;
}
if((fd = open("udp_text.txt", O_RDONLY , 0777))== -1){
printf("Error while opening txt file %s!\n",strerror(errno));
return 1;
}
while (1) {
if((numRead = read(fd,tmp,512)) == 512){
tmp[numRead]='\0';
rv = sendto(s,tmp,sizeof(tmp),0, (struct sockaddr *) &client, sizeof(struct sockaddr_in));
memset(tmp,0,BUF_SIZE);
}else{
rv = sendto(s,tmp,sizeof(tmp),0, (struct sockaddr *) &client, sizeof(struct sockaddr_in));
printf("EOF !\n");
return 1;
}
}
close(s);
return 0;
}
Thx
Since not all the packets show up in wireshark, I would guess that the sender is running out of network packet buffer space in the operating system. UDP can not only suffer packet loss from transmission failures but also if any routing component runs out of capacity and is forced to drop the packet because it is too busy. This includes the local internet protocol stack.
As a start, check the error code from sendto(). If the local operating system is to blame then it will likely have the courtesy of reporting an error.
Update: No error from sendto(), well then, no easy fix.
One final note of caution/advice. Even direct ethernet connections between hosts does not guarantee packets will always get through. If you depend on the file data getting transferred reliably then you'll need to add some kind of acknowledgement response from the receiver to confirm successful reception of the data. And associated logic in the sender to retransmit data as required.
That's a fair bit of work and why you might rather switch to TCP sockets which do all that for you.
If you are going to send files over UDP, at least use an existing protocol that is designed for that purpose, like Trivial FTP (see RFC 1350). It offers buffer size control and safety from dropped packets.
I'm trying to send a UDP packet to a router with a time to live of 1, to then receive an ICMP time exceeded reply. So far I'm able to send the packet, but when my program gets to the recv part of the execution, it just hangs. I have an error check for recvfrom, but it doesn't even get to that. My computer is receiving the request. I know this because I run Wireshark when I run the program and I filter for ICMP requests. Every time I run the program, I receive the reply. What am I doing wrong with recvfrom?
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#define UNSPEC_PROTO 0
int main(int argc, const char *argv[])
{
if (argc != 2) {
printf("usage: routetracer <ip address or hostname>\n");
return -1;
}
struct addrinfo hints; //params for ret val of getaddrinfo
struct addrinfo* ret; //return value of getaddrinfo
struct sockaddr* reply_addr;
char ipv4[INET_ADDRSTRLEN];
char* msg = "THE PORT IS OVER 9000!!!!";
int status = 0;
int ttl = 0;
int src_sock = 0;
int recv_sock = 0;
socklen_t reply_addr_len = sizeof(struct sockaddr);
const char* dest_port = "9001";
int icmp_msg_len = 100;
char icmp_msg[icmp_msg_len];
//define what we want from getaddrinfo
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; //IPv4
hints.ai_socktype = SOCK_DGRAM; //UDP packets
//call getaddrinfo to fill ret, w/ error chk
if ((status = getaddrinfo(argv[1], dest_port, &hints, &ret)) != 0) {
printf("getaddrinfo: %s\n", gai_strerror(status));
return -1;
}
//extract IPv4 address from ret
struct sockaddr_in* ip = (struct sockaddr_in *)ret->ai_addr;
//convert address from pure numbers to something easier to read
inet_ntop(ret->ai_family, &(ip->sin_addr), ipv4, INET_ADDRSTRLEN);
//kindly inform the user of which hostname they are connecting to
printf("Route for: %s\n", ipv4);
//create a socket for our machine
if ((src_sock = socket(ret->ai_family, ret->ai_socktype,
ret->ai_protocol)) < 0) {
fprintf(stderr, "Error creating host socket: %s\n", strerror(errno));
return -1;
}
//create a socket to recv icmp packet from hops
if ((recv_sock = socket(AF_INET, SOCK_DGRAM, UNSPEC_PROTO)) < 0){
fprintf(stderr, "Error creating recv socket: %s\n", strerror(errno));
}
/*
* We go from hop to hop by incrementing the time to live in the IP header
* for each hop we visit until we reach the destination IP address (which we
* already have). Time to live decrements for every hop, so once it reaches
* zero we report the IP address of the node we are connected to.
*/
//while(test_ip != dest_ip)
//time_to_live++
//send_to(dest_addr)
//receive icmp error message
//get src addr of error msg from ip header of icmp
//test_ip = src addr
/*
while (last_hop == 0) {
ttl++;
setsockopt(sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
sendto(sock, msg, strlen(msg), 0, (struct sockaddr *)ip, sizeof(ip));
}
*/
ttl = 1;
if (!(setsockopt(src_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)))) {
printf("TTL set successfully\n");
} else {
printf("Error setting TTL: %s\n", strerror(errno));
}
if ((sendto(src_sock, msg, strlen(msg), 0, ret->ai_addr,
ret->ai_addrlen)) > 0) {
printf("msg sent successfully\n");
} else {
fprintf(stderr, "Error sending msg: %s\n", strerror(errno));
}
if ((recvfrom(recv_sock, icmp_msg, icmp_msg_len, 0, reply_addr,
&reply_addr_len)) != -1) {
/* PROCESS THE INFORMATION */
printf("Packet received\n");
} else {
fprintf(stderr, "Error receiving packet: %s\n", strerror(errno));
}
return 0;
}
Normally, UDP pretty much ignores ICMP errors, so if you want to see them, you need to open a raw socket to receive all ICMP packets and look for ones relevant to your socket.
On Linux, at least, an alternative is to set the IP_RECVERR socket option. If you do that, you can do a recvmsg with the MSG_ERRQUEUE flag set to get any ICMP (or other) errors associated with your socket. This has the advantage of not requiring elevated privileges or a second socket.
In some implementations of sockets, UDP socket has to be connected to receive errors.
So, you need to add connect call and then use send/recv functions.
I've confirmed this on FreeBSD. At least one source clearly states that:
http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-5.html (see 5.3 Does doing a connect() call affect the receive behaviourof the socket?)
P.S. Note, however, that you won't receive exact ICMP error message that way. You'll only get some error code, without many details (if any).
Check the options when you are opening your sockets.
See How to sniff all ICMP packets using RAW sockets.
See How to receive ICMP request in C with raw sockets.
You may also want to change the socket options to be non-blocking and use the select() function to determine if there is something to read or not.
For examples on using the select() function see the following.
Blocking recvfrom with select system call.
Unexepcted results with select and recvfrom.
First of all, your code has undefined behavior, because reply_addr is uninitialised. You should fix that first:
struct sockaddr_in reply_addr;
...then:
recvfrom(recv_sock, icmp_msg, icmp_msg_len, 0, (struct sockaddr*)&reply_addr,
&reply_addr_len);
Finally, you need to use raw sockets, not datagram sockets, to receive ICMP packets:
recv_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);