I need to set up a raw socket for incoming packets only on a specific interface (has both eth0 and eth1, eth1 only). In other words, I need only incoming packets on one specific interface.
Comparing to other sniffers
In ifconfig there is an RX Packets field for each interface. While this remains constant, this sniffer will still register as receiving packets. Perhaps RX Packets is limited to certain protocols? I have also compared this to a python sniffer - the same problem exists. The python sniffer will not return as many packets as this c sniffer does. I cannot compare this to wireshark as I am unable to install it on the system, it is embedded.
Bindings
I thought perhaps I binded this incorrectly, but that seems to be working. Having two of these running, one on eth0 and the other on eth1 gives different results/
Suspected issue
It seems to me the problem is in the recvfrom command not filtering to only incoming packets, but instead reading from the buffer, whether that be incoming or outgoing. Perhaps there is a way to look at the address to see if the packet is incoming or outgoing, as in python, or perhaps recvfrom is already doing this.
Note
Near the end the program prints packet sizes sniffed and times that size packet has been received. Here is the trimmed down code. Thanks in advance.
#include<errno.h> //error codes
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<time.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<string.h>
#include<netinet/in.h>
#include<stdio.h>
#include<stdlib.h>
#include<net/if.h>
const int TIME_INTERVAL = 2;
const int BUF_LENGTH = 65534;
int main()
{
int sock_errno(void), data_size=0, raw_sock;
long recv_count = 0, last_count = 0, rate = 0;
time_t start;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
char buffer[BUF_LENGTH];
int hist[BUF_LENGTH];
int i;
for (i = 0; i < BUF_LENGTH; i++)
hist[i] = 0;
int table[BUF_LENGTH];
int index = 0;
//Create a raw socket that shall sniff
raw_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
//bind to interface
clilen = sizeof(struct sockaddr_in);
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth1");
setsockopt(raw_sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr));
start = time(NULL);
while (1)
{
data_size = recvfrom(raw_sock, buffer, BUF_LENGTH, 0, (struct sockaddr*)&cliaddr, &clilen);
recv_count = recv_count + data_size;
hist[data_size] = hist[data_size] + 1;
if (time(NULL) - start > TIME_INTERVAL) // display data every time interval
{
start = time(NULL);
rate = (float)(recv_count - last_count) / TIME_INTERVAL;
printf("(I) Bytes received: %d\n", recv_count);
for (i=0; i<BUF_LENGTH; i++) {
if (hist[i] > 0) //only print received packet sizes
{
printf("%d - ", i); //print packet size
printf("%d\n", hist[i]); //print received counter
}
}
printf("\n\n");
}
}
close(raw_sock);
return 0;
}
see the answer to Raw Socket promiscuous mode not sniffing what I write
looking at http://man7.org/linux/man-pages/man7/packet.7.html change the type of cliaddr to struct sockaddr_ll you can then look at cliaddr.sll_pkttype to determine incoming or outgoing
struct sockaddr_ll {
unsigned short sll_family; /* Always AF_PACKET */
unsigned short sll_protocol; /* Physical layer protocol */
int sll_ifindex; /* Interface number */
unsigned short sll_hatype; /* ARP hardware type */
unsigned char sll_pkttype; /* Packet type */
unsigned char sll_halen; /* Length of address */
unsigned char sll_addr[8]; /* Physical layer address */
};
sll_pkttype contains the packet type.
Valid types are PACKET_HOST for a packet addressed to the local host,
PACKET_BROADCAST for a physical layer broadcast packet,
PACKET_MULTICAST for a packet sent to a physical layer multicast
address, PACKET_OTHERHOST for a packet to some other host that has
been caught by a device driver in promiscuous mode, and
PACKET_OUTGOING for a packet originated from the local host that is
looped back to a packet socket.
Related
Below is the code I inherited from my company (written by I don't know who) and this code is currently working for my specific scenario which is:
A piece of equipment (signal generator) sends me UDP data, and I need to receive the data, analyze it and sometimes send the equipment a command (based on the analysis). Here is what it looks like:
/*********************************************
** Communication Struct.
**********************************************/
typedef struct CtxCom
{
int socket_client; //socket
char* cmd; //command
char* recepbuff; //recepbuff
struct sockaddr_in addr_client; //contains IP and PORT
}CtxCom;
extern struct CtxCom init_Ctx_com ( char* IP_client, const int PORT_client, struct timeval timeout )
{
CtxCom ClientCom; //define struct
ClientCom.socket_client = socket(AF_INET, SOCK_DGRAM, 0); //Create the socket
if(ClientCom.socket_client < 0) //Check the creation of the socket
{
perror("[Init_Com] socket()");
exit(errno);
}
struct sockaddr_in addr_me = { 0 }; //create the server struct
addr_me.sin_addr.s_addr = htonl(INADDR_ANY); //any incoming IP
addr_me.sin_port = htons(PORT_client);
addr_me.sin_family = AF_INET; //address family
if(bind(ClientCom.socket_client,(struct sockaddr *) &addr_me, sizeof(addr_me)) < 0) //bind the socket
{
perror("[Init_Com] bind()");
exit(errno);
}
//conf equipment side
ClientCom.addr_client.sin_addr.s_addr = inet_addr(IP_client);
ClientCom.addr_client.sin_port = htons(PORT_client);
ClientCom.addr_client.sin_family = AF_INET;
//timeout
//setsockopt(ClientCom.socket_client , SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout , sizeof (struct timeval));
//fcntl(ClientCom.socket_client, F_SETFL, O_NONBLOCK); //set socket to non block
//Printf info
printf("[Init CtxCom]");
printf(" Socket connected and Client[%s:%d] configured *************** \n",
inet_ntoa(ClientCom.addr_client.sin_addr),
ntohs(ClientCom.addr_client.sin_port) );
return ClientCom;
}
/*Write char* cmd of size cmdSize in the socket specified*/
extern void write_client(struct CtxCom CtxCom, char* cmd, int cmdSize)
{
//adding 0x0a 0x0d to the end of a CMD
cmdSize+=2;
cmd[cmdSize-2]='\r';
cmd[cmdSize-1]='\n';
//send CMD
if(sendto(CtxCom.socket_client, cmd, cmdSize, 0, (struct sockaddr *)&(CtxCom.addr_client), sizeof(CtxCom.addr_client)) < 0)
{
perror("[Write_client] send()");
exit(errno);
}
else
{
//printf( "\n***************SEND OK [%s:%d]******************\n"
// ,inet_ntoa(CtxCom.addr_client.sin_addr), ntohs(CtxCom.addr_client.sin_port) );
}
}
* Give in output the char* strings outStringLLRX with a size of sizeOutStringLLRX*/
extern void read_client(
/*input*/ struct CtxCom CtxCom, struct timeval timeout,
/*output*/ char** outStringLLRX, int* sizeOutStringLLRX)
{
//timeout forced
//timeout.tv_usec=TIMEOUT_LISTEN_GIII;
//Define variables
fd_set readfs;
int loop=1;
int i=0, k=0, z=0, z_prev=0;
int res;
char buf[25500];
int sizeBuf;
//Init variables
memset(buf, '\0' ,sizeof(buf));
for(i=0;i<NB_CHANNELS_LLRX;i++)
{
sizeOutStringLLRX[i]=0;
outStringLLRX[i][0]='\0';
}
//Make sure buffer is empty
memset(buf, '\0' ,sizeof(buf)); //empty recep buffer
FD_ZERO(&readfs); //zero testing
FD_SET(CtxCom.socket_client, &readfs); // set testing
//block until input becomes available
res=select(CtxCom.socket_client+1, &readfs, NULL, NULL, &timeout);
switch (res)
{
case 0: //timeout
printf("TIMEOUT error [Read Client] - No data received \n");
break;
case -1: //error
printf("Error [Read Client] \n");
break;
default : //streams event
if( FD_ISSET(CtxCom.socket_client, &readfs) )
{
sizeBuf=recvfrom (CtxCom.socket_client, buf , 25500, 0, NULL, NULL); //already now which IP, no need to update
if ( sizeBuf<0 ) //if <0 => no data => error
{
printf("[Read_Client] Read failed : SizeBuf<0 \n");
}
else
{
printf("[Read_Client] Got a buffer of size %d (round %d) \n", sizeBuf, k);
(sizeOutStringLLRX[0])+=sizeBuf;
for( z=0; z<sizeBuf; z++) {outStringLLRX[0][z_prev]=buf[z]; z_prev++;}
}
}
break;
}//switch
//printf("[Read_Client] final size =%d\n", z_prev);
/*printf("***************RECV OK [%s:%d]******************\n",
inet_ntoa(CtxCom.addr_client.sin_addr),ntohs(CtxCom.addr_client.sin_port) );*/
}
I have read socket lesson and bind() man, but I'm still wondering: If I have another equipment that sends data on the same subnet, but in broadcast (on the x.255). Can this pollute the socket? and sometimes on the same socket when I'm receiving data from my equipment I received the broadcast instead (or in addition)?
In my case, bind is actually here just to "give a name" to the socket and not to accept any incoming IP address like it's written in the comment?
(Btw, if anything written here is really bad, let me know, I'll be glad to make this code better)
You shouldn't have to worry about receiving broadcast packets on the socket. Assuming Linux, man 7 ip tells us "Datagrams to broadcast addresses can be sent or received only when the SO_BROADCAST socket flag is set" (that flag can be set with setsockopt and is documented in man 7 socket).
bind() is used to select the port to listen to, but also which network interface. The interface is specified by its local address, and INADDR_ANY in this case means to listen on all network interfaces (see man 7 ip). The socket will receive data from any (valid) IP address on the selected interface(s).
I am using sendto() function in C. I have set the destination address and dest port. While sending UDP frames I can see the frames in Wireshark and the number of packet Wireshark shows are exactly as I have defined in my program.
The problem is even though the destination address is not reachable the frames are being sent and I can see it in Wireshark.
Should not the sendto() function generates a error if the destination IP is not existing?
if (sendto(sockfd, &buffer[i], UDP_FRAME, 0,
(const struct sockaddr*)&server, sizeof(server)) < 0)
{
fprintf(stderr, "Error in sendto()\n");
//return EXIT_FAILURE;
}
Dest. IP: 234.168.0.1
Dest port: 80 or 9 (discard protocol)
#define PORT (80)
#define FRAMES (20000)
#define UDP_FRAME (1442)
#define SERVERADDRESS "234.168.0.1"
#define BUFFER_SIZE (FRAMES * UDP_FRAME)
char buffer[BUFFER_SIZE];
int main(int argc, char **argv)
{
struct timespec start, end, loop_start, loop_end;
int sockfd, count_frame = 0, frames_total, i = UDP_FRAME, n=1;
struct sockaddr_in server;
printf("Build Data...\n");
build(buffer, sizeof(buffer));
printf("Configure socket...\n");
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
fprintf(stderr, "Error opening socket");
return EXIT_FAILURE;
}
/*----------------------------------------------------*/
/*--- Initialize address protocol ---*/
/*----------------------------------------------------*/
bzero((char*)&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(SERVERADDRESS);
server.sin_port = htons(PORT);
/*---------------------------------------------------*/
/*--- S E N D I N G D A T A --*/
/*---------------------------------------------------*/
printf("\nSend UDP data...\n\n");
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
clock_gettime(CLOCK_MONOTONIC_RAW, &loop_start);
frames_total = 0;
for (int i = 0; i < BUFFER_SIZE; i += UDP_FRAME) {
//while(1) {
if (sendto(sockfd, &buffer[i], UDP_FRAME, 0,
(const struct sockaddr*)&server, sizeof(server)) < 0)
{
fprintf(stderr, "Error in sendto()\n");
//return EXIT_FAILURE;
}
count_frame += 1;
clock_gettime(CLOCK_MONOTONIC_RAW, &loop_end);
if ((loop_end.tv_nsec - loop_start.tv_nsec) > 5000000) {
printf("\nCount [%d] ... ", n);
printf("Fames sent: %d\n", count_frame);
frames_total += count_frame;
n+=1;
count_frame = 0;
clock_gettime(CLOCK_MONOTONIC_RAW, &loop_start);
}
}
printf("Total successful counted frames: %d \n", frames_total);
return EXIT_SUCCESS;
}
UDP is an unreliable protocol. A call to sendto is successful once the packet leaves the interface. After that, whether it gets to its destination or not is up to the network.
Even if the network supports ICMP messages stating that the host or port is not reachable, it won't matter in your particular case because you're sending to a multicast address. If you have at least one multicast-capable interface, the system will pick one to send the packet over. It could be received by multiple (or no) hosts. So it doesn't make sense to say that the destination is not reachable.
sendto() will give you an error if the host doesn't know a route to the host (which is almost never the case, since your host will have a default gateway). Otherwise, you might (or might not) receive an ICMP destination unreachable message if your packet did not reach the targeted application, but this is unreliable and won't be communicated by the call to sendto().
What you can do is to query the socket with
struct sock_extended_err err;
socklen_t errlen = sizeof(err);
getsockopt(fd, SOL_IP, IP_RECVERR, &err, &errlen);
for received errors, which will give you detailed information about received errors on the socket (i.e. ICMP port unreachable, ICMP host unreachable, etc. pp). This can help, but as I said, it is not realiable, since ICMP messages are often strictly rate limited, filtered on the way or not sent at all, if your packet is blocked by a packet filter (firewall).
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.
Hi i am trying to parse a Ip/Udp packet's header details actually to get the timestamp,port adresses etc. I know i can use library to do this. So after googling a lot i found out a code to parse through a row packet in the following method
void dump_UDP_packet(const unsigned char *packet, struct timeval ts,
unsigned int capture_len)
{
struct ip *ip;
struct UDP_hdr *udp;
unsigned int IP_header_length;
/* For simplicity, we assume Ethernet encapsulation. */
if (capture_len < sizeof(struct ether_header))
{
/* We didn't even capture a full Ethernet header, so we
* can't analyze this any further.
*/
too_short(ts, "Ethernet header");
return;
}
/* Skip over the Ethernet header. */
packet += sizeof(struct ether_header);
capture_len -= sizeof(struct ether_header);
if (capture_len < sizeof(struct ip))
{ /* Didn't capture a full IP header */
too_short(ts, "IP header");
return;
}
ip = (struct ip*) packet;
IP_header_length = ip->ip_hl * 4; /* ip_hl is in 4-byte words */
if (capture_len < IP_header_length)
{ /* didn't capture the full IP header including options */
too_short(ts, "IP header with options");
return;
}
if (ip->ip_p != IPPROTO_UDP)
{
problem_pkt(ts, "non-UDP packet");
return;
}
/* Skip over the IP header to get to the UDP header. */
packet += IP_header_length;
capture_len -= IP_header_length;
if (capture_len < sizeof(struct UDP_hdr))
{
too_short(ts, "UDP header");
return;
}
udp = (struct UDP_hdr*) packet;
printf("%s UDP src_port=%d dst_port=%d length=%d\n",
timestamp_string(ts),
ntohs(udp->uh_sport),
ntohs(udp->uh_dport),
ntohs(udp->uh_ulen));
}
the thing is that i dont really know what are the parameters that i should use to invoke this function, ie, packet? timeval? etc am recieving my packets using socket api by listening to the port and using recv() function
for (;;)
{
len = sizeof(cliaddr);
n = recvfrom(sockfd,mesg,1000,0,(struct sockaddr *)&cliaddr,&len);
//sendto(sockfd,mesg,n,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));
printf("-------------------------------------------------------\n");
printf("%s\n from:%s port number:%d",mesg,inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
printf("-------------------------------------------------------\n");
}
Now here can i use the the mesg[] to pass to the above function to get the packet details or else is there any other way to receive the packet from a specific port. What value shall i use for the timeVal. Any help would be useful for me. Thanks in advance
Here the most relevant is how do you open your socket. Do you create the socket with SOCK_RAW flag? If yes, then recvfrom will receive RAW packets which you can directly send to your function. I am not sure about Windows, but on Linux the code creating a raw socket is like::
sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
The timeval argument is not directly relevant to the packet. It is supposed to be the time when you have got the packet. You will get it by calling gettimeofday after recvfrom.
Perhaps you should consider using libpcap (Packet CAPture library), the guts of tcpdump.
Take the following code example
https://gist.github.com/3825444
/*
Testing arbitrary raw ip packets
works only if datagram is filled with 0
filling with anything else will not send any packets, or atleast wireshark does not detect anything
this is strange
*/
#include<stdio.h>
#include<string.h> //memset
#include<sys/socket.h>
#include<stdlib.h> //for exit(0);
#include<errno.h> //For errno - the error number
#include<netinet/tcp.h> //Provides declarations for tcp header
#include<netinet/ip.h> //Provides declarations for ip header
int main (void)
{
//Create a raw socket
int s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);
if(s < 0)
{
perror("socket");
}
//Datagram to represent the packet
char datagram[4096] , source_ip[32];
struct sockaddr_in sin;
strcpy(source_ip , "192.168.1.2");
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
sin.sin_addr.s_addr = inet_addr ("1.2.3.4");
memset (datagram, 2 , 4096); /* zero out the buffer */
//IP_HDRINCL to tell the kernel that headers are included in the packet
int one = 1;
const int *val = &one;
if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
{
printf ("Error setting IP_HDRINCL. Error number : %d . Error message : %s \n" , errno , strerror(errno));
exit(0);
}
//Uncommend the loop if you want to flood :)
while (1)
{
//Send the packet
if (sendto (s, /* our socket */
datagram, /* the buffer containing headers and data */
512, /* 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() */
{
perror("sendto");
}
//Data send successfully
else
{
printf ("Packet Send \n");
}
}
return 0;
}
The above program does not generate any packets, or atleast wireshark will not detect any.
However if the datagram is filled with 0 by doing
memset (datagram, 0 , 4096); /* zero out the buffer */
then plenty of packets are generate and are detected by wireshark.
Why such a difference ?
You're putting garbage into the header. It's more remarkable that setting zeros succeeds than that setting 2's fails.