I'm using BSD sockets to build an advanced traceroute program that doesn't need root privileges (like tracepath).
Using UDP and a bound socket, I call:
recvmsg(socket, header, MSG_ERRQUEUE)
I receive info about the supposed ICMP notification that a previously sent packet has triggered.
Do you know if it's possible to access the ICMP payload (which should be the previously-sent packet)?
I read from recvmsg man page:
[..] The payload of the original packet that caused the error
is passed as normal data via msg_iovec. [..]
But I can't find anything useful there, it just seems random data (I used wireshark to cross-check data).
You can use this sample code to check which ICMP Error you got and handle it (The sample contain some comments and links):
#define BUFFER_MAX_SIZE 1024
int on = 1;
/* Set the option, so we can receive errors */
setsockopt(socket, SOL_IP, IP_RECVERR,(char*)&on, sizeof(on));
/* Other code here */
/* .... */
/* Handle receving ICMP Errors */
int return_status;
char buffer[BUFFER_MAX_SIZE];
struct iovec iov; /* Data array */
struct msghdr msg; /* Message header */
struct cmsghdr *cmsg; /* Control related data */
struct sock_extended_err *sock_err; /* Struct describing the error */
struct icmphdr icmph; /* ICMP header */
struct sockaddr_in remote; /* Our socket */
for (;;)
{
iov.iov_base = &icmph;
iov.iov_len = sizeof(icmph);
msg.msg_name = (void*)&remote;
msg.msg_namelen = sizeof(remote);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = buffer;
msg.msg_controllen = sizeof(buffer);
/* Receiving errors flog is set */
return_status = recvmsg(socket, &msg, MSG_ERRQUEUE);
if (return_status < 0)
continue;
/* Control messages are always accessed via some macros
* http://www.kernel.org/doc/man-pages/online/pages/man3/cmsg.3.html
*/
for (cmsg = CMSG_FIRSTHDR(&msg);cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
/* Ip level */
if (cmsg->cmsg_level == SOL_IP)
{
/* We received an error */
if (cmsg->cmsg_type == IP_RECVERR)
{
fprintf(stderror, "We got IP_RECVERR message\n");
sock_err = (struct sock_extended_err*)CMSG_DATA(cmsg);
if (sock_err)
{
/* We are interested in ICMP errors */
if (sock_err->ee_origin == SO_EE_ORIGIN_ICMP && sock_err->ee_type == ICMP_DEST_UNREACH)
{
/* Handle ICMP destination unreachable error codes */
switch (sock_err->ee_code)
{
case ICMP_NET_UNREACH:
/* Handle this error */
fprintf(stderror, "Network Unreachable Error\n");
break;
case ICMP_HOST_UNREACH:
/* Handle this error */
fprintf(stderror, "Host Unreachable Error\n");
break;
/* Handle all other cases. Find more errors :
* http://lxr.linux.no/linux+v3.5/include/linux/icmp.h#L39
*/
}
}
}
}
}
}
}
Related
I'm receiving packets via recvmsg() and expecting 3 timestamps:
Software
Hardware (NIC) converted to software
Hardware (NIC)
I see the first and third timestamps but I get zeros for the second.
I'm basing my code on this Onload example:
https://github.com/Xilinx-CNS/onload/blob/master/src/tests/onload/hwtimestamping/rx_timestamping.c
I export the environment variable for timestamping:
export EF_RX_TIMESTAMPING=1
I preload the onload library.
I set the socket options:
int enable = SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE |
SOF_TIMESTAMPING_SYS_HARDWARE | SOF_TIMESTAMPING_SOFTWARE;
assert(setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &enable, sizeof(int)) == 0);
I create the message header and use recvmsg() to retrieve packets:
while(_running)
{
struct iovec iov;
char control[1024];
struct msghdr msg;
iov.iov_base = buffer;
iov.iov_len = 2048;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_control = control;
msg.msg_controllen = 1024;
int num bytes = recvmsg(sock, &msg, 0);
handle_time(&msg);
LOG("Got packet");
}
and I read the timestamps:
static void handle_time(struct msghdr* msg)
{
struct timespec* ts = NULL;
struct cmsghdr* cmsg;
for( cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg,cmsg) ) {
if( cmsg->cmsg_level != SOL_SOCKET )
continue;
switch( cmsg->cmsg_type ) {
case SO_TIMESTAMPNS:
ts = (struct timespec*) CMSG_DATA(cmsg);
break;
case SO_TIMESTAMPING:
ts = (struct timespec*) CMSG_DATA(cmsg);
break;
default:
/* Ignore other cmsg options */
break;
}
}
print_time(ts);
}
static void print_time(struct timespec* ts)
{
if( ts != NULL ) {
/* Hardware timestamping provides three timestamps -
* system (software)
* transformed (hw converted to sw)
* raw (hardware)
* in that order - though depending on socket option, you may have 0 in
* some of them.
*/
printf("timestamps " TIME_FMT TIME_FMT TIME_FMT "\n",
(uint64_t)ts[0].tv_sec, (uint64_t)ts[0].tv_nsec,
(uint64_t)ts[1].tv_sec, (uint64_t)ts[1].tv_nsec,
(uint64_t)ts[2].tv_sec, (uint64_t)ts[2].tv_nsec );
} else
{
printf( "no timestamp\n" );
}
}
Is there anything missing to receive the second timestamp?
It's probably the network interface driver that needs to add the timestamp from the hardware. What kind of hardware are we talking about? Can you show some ethanol output?
I'm attempting to generate a packet from scratch with Layer 2, 3, and 4 components (namely Ethernet, IP, and UDP). My socket is configured to use SOCK_RAW as the type and IPPROTO_RAW as the protocol, so that my program can be used to send different protocols at a later date. I've strictly abided by the sendto(2) man page (https://linux.die.net/man/2/sendto), however, I still seem to be having troubles transmitting a packet over local host. The error returned by my program is 22, or EINVAL, indicating that I've passed an incorrect argument.
I've already used Sockets sendto() returning EINVAL and Socket programming: sendto always fails with errno 22 (EINVAL) in attempt to resolve my problem. I've even switched the socket to use SOCK_DGRAM as the type and IPPROTO_UDP as the protocol to see if it was something to do with the socket (though I don't believe this would have prompted an incorrect argument response).
My code is as follows:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/ip.h>
#include <netinet/ether.h>
#include <netinet/udp.h>
#include <sys/socket.h>
#include <errno.h>
#define BUFFER 1024
int main(int argc, char *argv[]) {
/* Socket Creation */
int sockfd;
if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1) {
perror("Socket");
exit(EXIT_FAILURE);
}
// This will hold all of the packet's contents
char sendbuf[BUFFER];
/* Address Stuff */
// Specify Address
in_addr_t address = inet_addr("127.0.0.1");
// Specifying Address for Sendto()
struct sockaddr_in sendto_addr;
memset(&sendto_addr, 0, sizeof(sendto_addr));
sendto_addr.sin_family = AF_INET;
sendto_addr.sin_port = htons(9623); // Make sure the port isn't contested
sendto_addr.sin_addr.s_addr = address;
// Size of whole packet
size_t total_len = 0;
/* LAYER 2 */
// Ethernet Header Configuration
struct ether_header *ether = (struct ether_header *)sendbuf;
int i;
for (i = 0; i < 6; ++i) {
ether -> ether_dhost[i] = 0x00; // Temporary data fill
ether -> ether_shost[i] = 0x00; // Temporary data fill
}
ether -> ether_type = ETHERTYPE_IP;
total_len += sizeof(struct ether_header);
/* LAYER 3 */
// IP Header Configuration
struct ip *ip = (struct ip *)(sendbuf + sizeof(struct ether_header));
ip -> ip_hl = 5;
ip -> ip_v = 4;
ip -> ip_tos = 0;
ip -> ip_p = 17;
ip -> ip_ttl = 255;
ip -> ip_src.s_addr = address;
ip -> ip_dst.s_addr = address;
total_len += sizeof(struct ip);
/* LAYER 4 */
// UDP Header Configuration
struct udphdr *udp = (struct udphdr *)(sendbuf + sizeof(struct ether_header) + \
sizeof(struct ip));
udp -> source = 123; // Gibberish to fill in later
udp -> dest = 321; // Gibberish to fill in later
udp -> check = 0;
total_len += sizeof(struct udphdr);
/* Giberrish Packet Data */
sendbuf[total_len++] = 0x00;
sendbuf[total_len++] = 0x00;
sendbuf[total_len++] = 0x00;
sendbuf[total_len++] = 0x00;
/* Fill in Rest of Headers */
udp -> len = htons(total_len - sizeof(struct ether_header) - sizeof(struct ip));
ip -> ip_len = htons(total_len - sizeof(struct ether_header));
/* Send Packet */ // ERROR OCCURS HERE
printf("Sockfd is %d\n", sockfd);
printf("Total length is %d\n", total_len);
if (sendto(sockfd, sendbuf, total_len, 0, (const struct sockaddr*)&sendto_addr, \
sizeof(sendto_addr)) < 0) {
printf("Error sending packet: Error %d.\n", errno);
perror("sendto Error");
exit(EXIT_FAILURE);
}
close(sockfd);
}
The precise console message states:
Sockfd is 3
Total length is 46
Error sending packet: Error 22.
sendto Error: Invalid argument
Eureka! It was actually this line:
if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1) {
perror("Socket");
exit(EXIT_FAILURE);
}
Which should have been
if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror("Socket");
exit(EXIT_FAILURE);
}
Why this fixed the problem? Great question, would love to see someone's response on the subject :P
I want to send some data via tcp from my linux kernel module.
I have tried to use some code from http://www.avrfreaks.net/sites/default/files/tcp-server-send-recv.c , but there are too old code(it used old linux kernel api).
Also, I have tried to understand https://github.com/abysamross/simple-linux-kernel-tcp-client-server/blob/master/network_server.c , but it is too sophisticated for me:)
I want only send some small data to specified ip adress via tcp. How I can do it?
Check the client example in the same project. If you can reuse some functions, you must understand and modify the tcp_client_connect function (lines 124-198) only. In that module, the tcp_client_connect connection creates a connection when the module is loaded, and the network_client_exit closes the connection when the module is unloaded.
In the tcp_client_connect function:
(line 144) It creates a socket
struct socket *conn_socket = NULL;
ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &conn_socket);
Then, (lines 153 to 155 ) it creates a destination address
struct sockaddr_in saddr; /* a socket address */
saddr.sin_family = AF_INET; /* for internet */
saddr.sin_port = htons(PORT); /* using the port PORT */
saddr.sin_addr.s_addr = htonl(create_address(destip)); /* and address destip */
(line 157) It uses that address to open the socket (to create the connection)
int ret = -1;
ret = conn_socket->ops->connect(conn_socket, (struct sockaddr *)&saddr\
, sizeof(saddr), O_RDWR);
/* if it gets a response and it is not "in progress" */
if(ret && (ret != -EINPROGRESS))
{
/* error creating the socket*/
}
(lines 166 to 168) It sends a message using the socket.
int len = 49;
char reply[len+1];
memset(&reply, 0, len+1); /* sets 0s into all the string space */
strcat(reply, "HOLA"); /* sets the message */
tcp_client_send(conn_socket, reply, strlen(reply), MSG_DONTWAIT);
(line 170) It waits for a message (for while)
DECLARE_WAIT_QUEUE_HEAD(recv_wait);
/* wait for a response or for a timetout */
wait_event_timeout(recv_wait,\
!skb_queue_empty(&conn_socket->sk->sk_receive_queue),\
5*HZ);
(lines 180 to 190) It obtains the response.
int len = 49;
char response[len+1];
/* if something has arrived */
if(!skb_queue_empty(&conn_socket->sk->sk_receive_queue))
{
memset(&response, 0, len+1);
tcp_client_receive(conn_socket, response, MSG_DONTWAIT);
}
In the network_client_exit function,
(lines 239 to 240) it closes the connection.
/* if the socket has been created */
if(conn_socket != NULL)
{
/* relase the socket */
sock_release(conn_socket);
}
The waiting code not work, It only wakeup for timeout.
DECLARE_WAIT_QUEUE_HEAD(recv_wait);
/* wait for a response or for a timetout */
wait_event_timeout(recv_wait,\
!skb_queue_empty(&conn_socket->sk->sk_receive_queue),\
5*HZ);
I am sending TCP SYN packets (with no payload) to a webserver in the same network. I am using sniffex.c
for capturing the packets .
The problem is that after I send a SYN packet, I do not receive the SYN/ACK packet from server.
In sniffex.c:
I have used my LAN IP as the source ip.
I have set the filter as "tcp" .
I am sending to port 80
When i print the fields of the sent packet ,after i capture it using sniffex , all fields are printed correctly, hence i assume that the structure of the sent packet is such that the server can understand it.
When I connect to the webserver using browser, the SYN/ACK is received successfully.
Another related query: how do I set the filter such that I get packets relating to this conversation (b/w my pc and webserver) only
I am using UBUNTU 14.04
EDIT: The c file with which I am trying to send the packet
#define __USE_BSD /* use bsd'ish ip header */
#include <sys/socket.h> /* these headers are for a Linux system, but */
#include <netinet/in.h> /* the names on other systems are easy to guess.. */
#include <netinet/ip.h>
#define __FAVOR_BSD /* use bsd'ish tcp header */
#include <netinet/tcp.h>
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<memory.h>
#include<errno.h>
#include<sys/socket.h>
#include<sys/types.h>
#define P 80 /* lets flood the sendmail port */
unsigned short /* this function generates header checksums */
csum (unsigned short *buf, int nwords)
{
unsigned long sum;
for (sum = 0; nwords > 0; nwords--)
sum += *buf++;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return ~sum;
}
int
main (void)
{
int s = socket (AF_INET, SOCK_RAW, IPPROTO_TCP);
printf("s=%d\n",s); /* open raw socket */
char datagram[4096]; /* this buffer will contain ip header, tcp header,
and payload. we'll point an ip header structure
at its beginning, and a tcp header structure after
that to write the header values into it */
struct ip *iph = (struct ip *) datagram;
struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ip));
struct sockaddr_in sin;
/* the sockaddr_in containing the dest. address is used
in sendto() to determine the datagrams path */
sin.sin_family = AF_INET;
sin.sin_port = htons (P);/* you byte-order >1byte header values to network
byte order (not needed on big endian machines) */
sin.sin_addr.s_addr = inet_addr ("xxx.xxx.xxx.xxx");
memset (datagram, 0, 4096); /* zero out the buffer */
/* we'll now fill in the ip/tcp header values, see above for explanations */
iph->ip_hl = 5;
iph->ip_v = 4;
iph->ip_tos = 0;
iph->ip_len = sizeof (struct ip) + sizeof (struct tcphdr); /* no payload */
iph->ip_id = htonl (54321); /* the value doesn't matter here */
iph->ip_off = 0;
iph->ip_ttl = 255;
iph->ip_p = 6;
iph->ip_sum = 0; /* set it to 0 before computing the actual checksum later */
iph->ip_src.s_addr = inet_addr ("xxx.xxx.xxx.xxx");/* SYN's can be blindly spoofed */
iph->ip_dst.s_addr = sin.sin_addr.s_addr;
tcph->th_sport = htons (2000); /* arbitrary port */
tcph->th_dport = htons (P);
tcph->th_seq = random();/* in a SYN packet, the sequence is a random */
tcph->th_ack = 0;/* number, and the ack sequence is 0 in the 1st packet */
tcph->th_x2 = 5;
tcph->th_off = 5; /* first and only tcp segment */
tcph->th_flags = TH_SYN; /* initial connection request */
tcph->th_win = htonl (65535); /* maximum allowed window size */
tcph->th_sum = 0;/* if you set a checksum to zero, your kernel's IP stack
should fill in the correct checksum during transmission */
tcph->th_urp = 0;
iph->ip_sum = csum ((unsigned short *) datagram, iph->ip_len >> 1);
/* finally, it is very advisable to do a IP_HDRINCL call, to make sure
that the kernel knows the header is included in the data, and doesn't
insert its own header into the packet before our data */
/* lets do it the ugly way.. */
int one = 1;
// const int *val = &one;
if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, &one, sizeof (one)) < 0)
printf ("Warning: Cannot set HDRINCL!\terrno = %d\n",errno);
// while (1)
// {
if (sendto (s, /* our socket */
datagram, /* the buffer containing headers and data */
iph->ip_len, /* total length of our datagram */
0, /* routing flags, normally always 0 */
(struct sockaddr *) &sin, /* socket addr, just like in */
sizeof (sin)) < 0) /* a normal send() */
printf ("error\n");
else
printf ("SUCCESS\n\n\n\n");
//}
char buffer[8192];
memset (buffer, 0, 8192);
int n;
//while(n=read (s, buffer, 8192) > 0)
//{
//printf("n=%d\n",n);
//printf ("Caught tcp packet: %s\n", buffer);
//memset (buffer, 0, 8192);
//}
return 0;
}
[Summary of chat session]
In addition to the iph->ip_off issue, you may need to compute the TCP checksum yourself (your O/S may not do it for you). Useful info is here: http://www.tcpipguide.com/free/t_TCPChecksumCalculationandtheTCPPseudoHeader-2.htm and http://www.netfor2.com/tcpsum.htm
Also tcph->th_seq = htonl(23456); may be useful.
Im sending data on network via sockets like this: (broadcast)
void sendBroad(char *dstIP, char *localIP, char *localMAC)
{
int sock; /* Socket */
struct sockaddr_in broadcastAddr; /* Broadcast address */
int broadcastPermission; /* Socket opt to set permission to broadcast */
unsigned int dataLen;
char data[100]={0};
strcat(data, localIP);
strcat(data, " ");
strcat(data, localMAC);
strcat(data, " ");
/* Create socket for sending/receiving datagrams */
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
perror("socket() failed");
/* Set socket to allow broadcast */
broadcastPermission = 1;
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (void *) &broadcastPermission,
sizeof(broadcastPermission)) < 0)
perror("setsockopt() failed");
/* Construct local address structure */
memset(&broadcastAddr, 0, sizeof(broadcastAddr)); /* Zero out structure */
broadcastAddr.sin_family = AF_INET; /* Internet address family */
broadcastAddr.sin_addr.s_addr = inet_addr(dstIP); /* Broadcast IP address */
broadcastAddr.sin_port = htons(BroadcastPort); /* Broadcast port */
dataLen = strlen(data);
int j;
for (j=0; j<1; j++) /* 3krat a dost */
{
/* Broadcast localIP in datagram to clients */
if (sendto(sock, data, dataLen, 0, (struct sockaddr *)
&broadcastAddr, sizeof(broadcastAddr)) != dataLen)
perror("sendto() sent a different number of bytes than expected");
}
/* NOT REACHED */
}
but I always get some weird chars in the begining when receiving, like:
X.?192.168.....
When I try to send this data 6 times, just once I get data starting with 192..., other 5 strings starts with those weird chars. Any idea what is happening here?
Thanks
char data[100]; is not initialized. Accordingly, you are concatenating to the end of some undefined garbage, not to the end of an empty string. This is obviously undefined behaviour as it is not guaranteed that a '\0' appears anywhere within the reserved space (to say nothing of the fact that it's just plain undefined behaviour and the compiler may actually do what it wishes if/when it detects this).
char data[100] = {0}; should do the trick.