i have made a packet sniffer using libpcap on C++.
I am using pcap_loop and calling a loopback function , which at the moment i havent put much thought of.
Here is my code.
int PacketSniff(int *count)
{
int ifnum;
int NumOfDevs=0;
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 ip;
bpf_u_int32 netmask;
struct in_addr ip_addr , netmask_addr;
pcap_if_t *devs , *d;
pcap_t *handler;
char packet_filter[] = "ip";
struct bpf_program fcode;
/* Find all interface devices */
pcap_findalldevs(&devs, errbuf);
for(d=devs; d; d=d->next)
{
printf("%d. %s", ++NumOfDevs, d->name);
if (d->description)
{
printf(" (%s)\n", d->description);
}
else
{
printf(" (No description available)\n");
}
}
if(NumOfDevs==0)
{
printf("\nNo interfaces found!\n");
return (-1);
}
/* Prompt User to select interface */
printf("Enter the interface number (1-%d):\n",NumOfDevs);
scanf("%d",&ifnum);
if(ifnum < 1 || ifnum > NumOfDevs)
{
printf("\nInterface number out of range.\n");
/* Free the device list */
pcap_freealldevs(devs);
return (-1);
}
/* Jump to the selected adapter/interface */
for(d=devs; ifnum>1 ;d=d->next, ifnum--);
/* Open the selected adapter/interface */
handler = pcap_open_live(d->name, 65535, 0, 2000, errbuf);
if ((handler = pcap_open_live(d->name, 65535, 0, 2000, errbuf)) == NULL)
{
fprintf(stderr, "Couldn't open device %s: %s\n", d->name, errbuf);
return(-1);
}
if (pcap_datalink(handler) != DLT_EN10MB )
{
fprintf(stderr,"\nThis program works only on Ethernet networks.\n");
pcap_freealldevs(devs);
return -1;
}
/* This means that we set the datalink layer header size at 14 */
int linkhdrlen = 14;
if (pcap_lookupnet(d->name, &ip, &netmask, errbuf) <0 )
{
fprintf(stderr, "Can't get netmask for device %s\n", d->name);
netmask = 0;
ip = 0;
}
/* Compile the filter */
if (pcap_compile(handler, &fcode, packet_filter, 1, netmask) <0 )
{
fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax. Error: %s\n", errbuf);
pcap_freealldevs(devs);
return -1;
}
/* Set the filter */
if (pcap_setfilter(handler, &fcode)<0)
{
fprintf(stderr,"\nError setting the filter. Error: %s\n", errbuf);
pcap_freealldevs(devs);
return -1;
}
printf("\nListening for packets on interface <%s>...\n", d->name);
/* At this point, we don't need any more the device list. Free it */
pcap_freealldevs(devs);
pcap_loop(handler, 0, my_callback, NULL);}
And my_callback is like this:
void my_callback(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_ptr){
struct tm ltime;
char timestr[16];
struct ip_header *iphdr;
struct tcp_header *tcphdr;
time_t local_tv_sec;
/* Convert the timestamp to readable format */
local_tv_sec = header->ts.tv_sec;
localtime_r(&local_tv_sec , <ime);
strftime( timestr, sizeof timestr, "%H:%M:%S", <ime);
/* Print timestamp and length of the packet */
printf("Time >> %s.%.6d \nPacket Length:%d \n\n", timestr, header->ts.tv_usec, header->len);
/* Retireve the position of the ip header http://www.tcpdump.org/pcap.html */
iphdr = (ip_header *) (pkt_ptr +14);
// Advance to the transport layer header then parse and display
// the fields based on the type of hearder: tcp, udp or icmp.
pkt_ptr += 4*iphdr->ver_ihl ;
tcphdr = (tcp_header *)(pkt_ptr + 14);
/* print ip addresses and tcp ports */
printf("%d.%d.%d.%d : %d ---> %d.%d.%d.%d : %d\n\n",
iphdr->saddr.byte1,
iphdr->saddr.byte2,
iphdr->saddr.byte3,
iphdr->saddr.byte4,
tcphdr->src_port,
iphdr->daddr.byte1,
iphdr->daddr.byte2,
iphdr->daddr.byte3,
iphdr->daddr.byte4,
tcphdr->dst_port);}
Now i can sniff packets and print various things .
But my goal is to Extract Stats from the packets (like numOfpackets , numOfTCPpackets , numOfIncomingPAcket , numOfOutgoingPackets , Packet Size Variance , Time Interval Variance ) while they are being sniffed .
But i want this to be done in 1000ms Time-Windows.
For example: Every 1000ms i want an output file of..
numOfTCPPackets = ....
numof = ....
.
.
.
My questions are :
How can i incorporate those Time-Windows?
How to extract the needed stats without interfering too muchwith the sniffing speed.?
Thank you a lot!
Use pcap_next() instead of pcap_loop() to get the packet and set the timeout with pcap_set_timeout() to a small value such as 10 milliseconds to prevent pcap_next() blocking forever so that your code to write the statistics to the file gets a chance to run. You need to call pcap_next() in a loop and have code like the following after the pcap_next() call:
if (cur_time64() - last_time64 >= stat_time64)
{
last_time64 += stat_time64;
print_statistics_to_file();
}
...where cur_time64() returns the current time as a 64-bit integer in microseconds since the epoch (you can use gettimeofday() to implement cur_time64() if you use a Unix-like operating system). stat_time64 would be 1 second, i.e. 1000*1000, in your example.
Then, proceed to process the packet. Check the return value of pcap_next() to see if it returned a packet: if no, continue the loop; if yes, process the packet.
To do the checks without interfering too much with the sniffing speed, your only option is to write the code as efficiently as possible. Handle only those header fields you absolutely need to handle, i.e. you can avoid checking the checksums of IP and TCP headers.
Related
I am learning to write pcap code in c. Below i have written a simple c code to automatically detect a device for snifiing, getting ip and subnet mask, getting link layer headers and filtering traffic and then printing packet size.
Code complies successfully but gets stuck at
Network device found: wlo1
when run. Removing the filter part does print the packet size. And removing the priting packet part; the program complies and runs successfully.
I think i am lacking understanding of filtering part.
I compile using(on linux): gcc program_name -lpcap
Output of the code is:
Network device found: wlo1
wlo1 is wlan device
#include <stdio.h>
#include <pcap.h>
int main(int argc, char *argv[]){
char *dev; //device automatically detected for sniffing
char errbuf[PCAP_ERRBUF_SIZE]; //error string
pcap_t *handle; //session hnadle
struct bpf_program fp; //The compiled filter expression
char filter_exp[] = "port 23"; //The filter expression
bpf_u_int32 mask; //The netmask of our sniffing device
bpf_u_int32 net; //The IP of our sniffing device
struct pcap_pkthdr header;
const unsigned char *packet;
//device detection block
dev = pcap_lookupdev(errbuf);
if (dev == NULL){
printf("Error finding device: %s\n", errbuf);
return 1;
}
printf("Network device found: %s\n", dev);
//opening device for sniffing
handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if(handle == NULL){
fprintf(stderr,"Couldn't open device %s : %s\n",dev,errbuf);
return 1;
}
// //check for link-layer header of the device
if(pcap_datalink(handle) != DLT_EN10MB){ //for ethernet data link layer
if(pcap_datalink(handle) != DLT_IEEE802_11){ //for wlan data link layer
fprintf(stderr, "Device %s doesn't provide WLAN headers - not supported\n", dev);
return 1;
}
else{
fprintf(stderr, "Device %s doesn't provide Ethernet headers - not supported\n", dev);
return 1;
}
}
//block to get device ip and subnet mask
if(pcap_lookupnet(dev, &net, &mask, errbuf) == -1){
fprintf(stderr, "Can't get netmask for device %s\n", dev);
net = 0;
mask = 0;
}
//block for filtering traffic we want to sniff
if(pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
return 1;
}
if(pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
return 1;
}
/* Grab a packet */
packet = pcap_next(handle, &header);
/* Print its length */
printf("Jacked a packet with length of [%d]\n", header.len);
/* And close the session */
pcap_close(handle);
return 0;
}
If wlo1 is capturing in monitor mode on a "protected" network (a network with traffic encrypted at the link layer, using WEP or WPA/WPA2/WPA3), then any filter that works above the link layer - such as a TCP/UDP-layer filter, which "port 80" is - will not work, because the packets, as delivered to the filtering code, will have the 802.11 payload encrypted, so filters won't work on them.
Therefore, no packets will pass the filter.
I'm writing a program to get certain pieces of information from the headers in a pcap. I'm not sure if I did this right. It works with all of my professor's tests, however, there are hidden tests that I need to be aware of. It's the TCP flags I'm not sure about. It works in index 47, but don't know why, should be 46. (Ethernet Header(14) + IPv4 header(20) + 13th byte in TCP header (13) -1 (to account for arrays starting at 0) = 46). Is it a fluke that it works on spot 47?
Here's my code:
#include <pcap/pcap.h>
#include <stdlib.h>
#include <netinet/ether.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
/*
* Most of this file is the background functionality to open a capture file or to
* open an inteface for a live capture. You can ignore all this unless you are
* interested in an example of how pcap works.
*
* To use the file, simply insert your code in the "Put your code here" section and
* create a Makefile for compilation.
*/
/* Maximum time that the OS will buffer packets before giving them to your program. */
#define MAX_BUFFER_TIME_MS (300)
/* Maximum time the program will wait for a packet during live capture.
* Measured in MAX_BUFFER_TIME_MS units. Program closes when it expires. */
#define MAX_IDLE_TIME 100 /* 100*MAX_BUFFER_TIME_MS idle time at most */
/* Function that creates the structures necessary to perform a packet capture and
* determines capture source depending on arguments. Function will terminate the
* program on error, so return value always valid. */
pcap_t* setup_capture(int argc, char *argv[], char *use_file);
/* Cleanup the state of the capture. */
void cleanup_capture(pcap_t *handle);
/* Check for abnormal conditions during capture.
* 1 returned if a packet is ready, 0 if a packet is not available.
* Terminates program if an unrecoverable error occurs. */
char valid_capture(int return_value, pcap_t *pcap_handle, char use_file);
int main(int argc, char *argv[]) {
pcap_t *pcap_handle = NULL; /* Handle for PCAP library */
struct pcap_pkthdr *packet_hdr = NULL; /* Packet header from PCAP */
const u_char *packet_data = NULL; /* Packet data from PCAP */
int ret = 0; /* Return value from library calls */
char use_file = 0; /* Flag to use file or live capture */
/* Setup the capture and get the valid handle. */
pcap_handle = setup_capture(argc, argv, &use_file);
/* Loop through all the packets in the trace file.
* ret will equal -2 when the trace file ends.
* ret will never equal -2 for a live capture. */
ret = pcap_next_ex(pcap_handle, &packet_hdr, &packet_data);
struct ether_header
{
u_int8_t ether_dhost[6]; /* destination eth addr */
u_int8_t ether_shost[6]; /* source ether addr */
u_int16_t ether_type; /* packet type ID field */
};
struct ether_header *eptr;
char src[INET_ADDRSTRLEN];
char dst[INET_ADDRSTRLEN];
char src6[INET6_ADDRSTRLEN];
char dst6[INET6_ADDRSTRLEN];
while( ret != -2 ) {
if( valid_capture(ret, pcap_handle, use_file) ){
eptr = (struct ether_header *) packet_data;
fprintf(stdout,"%s -> ",ether_ntoa((const struct ether_addr *)&eptr->ether_shost));
fprintf(stdout,"%s \n",ether_ntoa((const struct ether_addr *)&eptr->ether_dhost));
if(packet_data[12] == 0x08 && packet_data[13] == 0x00)
{
printf(" [IPv4] ");
fprintf(stdout,"%s -> ", inet_ntop(AF_INET,(const void *)packet_data+26,src,INET_ADDRSTRLEN));
fprintf(stdout,"%s\n", inet_ntop(AF_INET,(const void *)packet_data+30,dst,INET_ADDRSTRLEN));
if(packet_data[23] == 0x06)
{
printf(" [TCP] %d -> ",packet_data[34]*256+packet_data[35]);
printf("%d ",packet_data[36]*256+packet_data[37]);
// printf("%02X ",packet_data[47]); //print out value of flag;
if(packet_data[47] & (1!=0))
printf("FIN \n");
else if((packet_data[47] == 0x02 || packet_data[47] == 0x12) & (2!=0))
printf("SYN \n");
else{
printf("\n");
}
}
else if(packet_data[23] == 0x11)
{
printf(" [UDP] %d -> ",packet_data[34]*256+packet_data[35]);
printf("%d \n",packet_data[36]*256+packet_data[37]);
}
else{
printf(" [%d] \n",packet_data[23]);
}
}
else if(packet_data[12] == 0x86 && packet_data[13] == 0xdd)
{
printf(" [IPv6] ");
printf("%s -> ", inet_ntop(AF_INET6, (const void *)packet_data+22, src6, INET6_ADDRSTRLEN));
printf("%s \n", inet_ntop(AF_INET6, (const void *)packet_data+38, dst6, INET6_ADDRSTRLEN));
if(packet_data[20] == 0x06)
{
printf(" [TCP] %d -> ",packet_data[54]*256+packet_data[55]);
printf("%d ",packet_data[56]*256+packet_data[57]);
// printf("%02X ",packet_data[67]); //print out value of flag
if(packet_data[67] & (1!=0))
printf("FIN \n");
else if((packet_data[67] == 0x02 || packet_data[67] == 0x12) & (2!=0))
printf("SYN \n");
else{
printf("\n");
}
}
else if(packet_data[20] == 0x11)
{
printf(" [UDP] %d -> ",packet_data[54]*256+packet_data[55]);
printf("%d \n",packet_data[56]*256+packet_data[57]);
}
else{
printf(" [%d] \n",packet_data[20]);
}
} else {
fprintf(stdout," [%d] \n",ntohs(eptr->ether_type));
}
}
/* Get the next packet */
ret = pcap_next_ex(pcap_handle, &packet_hdr, &packet_data);
}
cleanup_capture(pcap_handle);
return 0;
}
pcap_t* setup_capture(int argc, char *argv[], char *use_file) {
char *trace_file = NULL; /* Trace file to process */
pcap_t *pcap_handle = NULL; /* Handle for PCAP library to return */
char pcap_buff[PCAP_ERRBUF_SIZE]; /* Error buffer used by pcap functions */
char *dev_name = NULL; /* Device name for live capture */
/* Check command line arguments */
if( argc > 2 ) {
fprintf(stderr, "Usage: %s [trace_file]\n", argv[0]);
exit(-1);
}
else if( argc > 1 ){
*use_file = 1;
trace_file = argv[1];
}
else {
*use_file = 0;
}
/* Open the trace file, if appropriate */
if( *use_file ){
pcap_handle = pcap_open_offline(trace_file, pcap_buff);
if( pcap_handle == NULL ){
fprintf(stderr, "Error opening trace file \"%s\": %s\n", trace_file, pcap_buff);
exit(-1);
}
}
/* Lookup and open the default device if trace file not used */
else{
dev_name = pcap_lookupdev(pcap_buff);
if( dev_name == NULL ){
fprintf(stderr, "Error finding default capture device: %s\n", pcap_buff);
exit(-1);
}
/* Use buffer length as indication of warning, per pcap_open_live(3). */
pcap_buff[0] = 0;
pcap_handle = pcap_open_live(dev_name, BUFSIZ, 1, MAX_BUFFER_TIME_MS, pcap_buff);
if( pcap_handle == NULL ){
fprintf(stderr, "Error opening capture device %s: %s\n", dev_name, pcap_buff);
exit(-1);
}
if( pcap_buff[0] != 0 ) {
printf("Warning: %s\n", pcap_buff);
}
printf("Capturing on interface '%s'\n", dev_name);
}
return pcap_handle;
}
void cleanup_capture(pcap_t *handle) {
/* Close the trace file or device */
pcap_close(handle);
}
char valid_capture(int return_value, pcap_t *pcap_handle, char use_file) {
static int idle_count = 0; /* Count of idle periods with no packets */
char ret = 0; /* Return value, invalid by default */
/* A general error occurred */
if( return_value == -1 ) {
pcap_perror(pcap_handle, "Error processing packet:");
cleanup_capture(pcap_handle);
exit(-1);
}
/* Timeout occured for a live packet capture */
else if( (return_value == 0) && (use_file == 0) ){
if( ++idle_count >= MAX_IDLE_TIME ){
printf("Timeout waiting for additional packets on interface\n");
cleanup_capture(pcap_handle);
exit(0);
}
}
/* Unexpected/unknown return value */
else if( return_value != 1 ) {
fprintf(stderr, "Unexpected return value (%i) from pcap_next_ex()\n", return_value);
cleanup_capture(pcap_handle);
exit(-1);
}
/* Normal operation, packet arrived */
else{
idle_count = 0;
ret = 1;
}
return ret;
}
Here's a few sample print outs: (the left is the professors results, the right is mine, I have extra printout to see what's in that spot in the array). Thanks
0:0:86:5:80:da -> 0:60:97:7:69:ea 0:0:86:5:80:da -> 0:60:97:7:69:ea
[IPv6] 3ffe:507:0:1:200:86ff:fe05:80da -> 3ffe:501:410:0:2c0:dfff:fe47:33e [IPv6] 3ffe:507:0:1:200:86ff:fe05:80da -> 3ffe:501:410:0:2c0:dfff:fe47:33e
[TCP] 1022 -> 22 SYN | [TCP] 1022 -> 22 02 SYN
0:60:97:7:69:ea -> 0:0:86:5:80:da 0:60:97:7:69:ea -> 0:0:86:5:80:da
[IPv6] 3ffe:501:410:0:2c0:dfff:fe47:33e -> 3ffe:507:0:1:200:86ff:fe05:80da [IPv6] 3ffe:501:410:0:2c0:dfff:fe47:33e -> 3ffe:507:0:1:200:86ff:fe05:80da
[TCP] 22 -> 1022 SYN | [TCP] 22 -> 1022 12 SYN
Here's how you can locate the TCP flags:
If we assume that we are talking about Ethernet, the Ethernet frame header will be 14 bytes: a 6 byte destination followed by a 6 byte source and then a 2 byte ether type (for 802.3/SNAP/Ethernet II, which is most likely)
If the Ethertype at offset 12/13 from the start of the frame contains 0x0800, you are looking at TCP/IP.
if(frame[12]==0x08 && frame[13]==0x00) { /* IP packet inside */ }
Assuming that you have an IP Ethertype, the next byte will contain two nibble sized fields: The IP version number (likely 0x40 for you) and then the IP header length (likely 0x05). Putting those nibbles together, you would have 0x45 sitting in that field. It is very important to check that field. You could mask off the upper nibble like so:
ihl = frame[14]&0x0f;
to grab the IP header length field. This number will tell you where to find the next protocol layer's header. Typically you will have a 5 here (20 byte header), but if there are IP options, this number will be larger. Let's take this number and calculate from here:
embedded_protocol_header = frame[ihl * 4];
Next, you should verify that you actually have a TCP packet. This can be verified by examining byte offset 9 in the IP header:
ip_header_start = frame[14];
embedded_protocol = ip_header_start[9];
if(embedded_protocol == 6) { tcp_header = embedded_protocol_header; }
Now that we know it is TCP, we can grab the TCP flags. These will be at offset 13 in the TCP header:
tcp_flags = tcp_header[13];
To examine the SYN/ACK bits, you can mask everything else off:
synack = tcp_flags & 0x3f;
You can now check to see if it's a SYN ACK:
if(synack == 0x12) { /* SYN and ACK were set */
You may wonder about the 0x3f mask above. The reason for it is that the two high order bits in the TCP flags are used for ECN if the system supports ECN. If it is supported, ECN negotiation occurs during the 3 way handshake in these bits and the two low order bits in the TOS byte of the IP header (differentiated services byte). Rather than dealing with all of the possible cases, the simplest thing is to turn those bits off completely and check to see if you still have SYN and ACK.
I am trying to parse a pcap file including different type of Network Packets (some are tagged as VLAN and some aren't) using #include .
here is my code so far:
pcap_t *pcap;
const unsigned char *packet;
char errbuf[PCAP_ERRBUF_SIZE];
struct pcap_pkthdr header;
pcap = pcap_open_offline(argv[0], errbuf);
if (pcap == NULL)
{
fprintf(stderr, "error reading pcap file: %s\n", errbuf);
exit(1);
}
while ((packet = pcap_next(pcap, &header)) != NULL)
{
struct ip_header *ip;
unsigned int IP_header_length;
packet += sizeof(struct ether_header);
capture_len -= sizeof(struct ether_header);
ip = (struct ip_header*) packet;
IP_header_length = ip->vhl * 4; /* ip_hl is in 4-byte words */
char *sinfo = strdup(inet_ntoa(ip->src));
char *dinfo = strdup(inet_ntoa(ip->dst));
printf ("%s<-__->%s\n", sinfo ,dinfo);
free (sinfo);
free (dinfo);
}
There must be somewhere in the code to check the VLAN and jump over them correctly.How should I distinguish VLAN packets from non-VLANS?
(If you are testing this on a 'live' environment, it's important to remember that routers can remove 802.1q tags before forwarding to a non-trunking line.)
If you have a particular platform & protocol in mind, the fastest way to do this will always be to 'manually' check a frame:
htonl( ((uint32_t)(ETH_P_8021Q) << 16U)
| ((uint32_t)customer_tci & 0xFFFFU) ) T
However, libpcap provides for a portable & clean packet filters in the form of functions for compiling a BPF filters and applying those to a stream of packets (although it is important to note that there are different sets of functions for on-the-wire vs. offline filtering)
In this fashion, We can use pcap_offline_filter to apply the compiled BPF filter directive to a PCAP file. I've used the filter expression vlan here, you may want something else like vlan or ip. If you need something more complex, you can consult the documentation)
...
pcap_t *pcap;
char errbuf[PCAP_ERRBUF_SIZE];
const unsigned char *packet;
struct pcap_pkthdr header;
struct bpf_program fp; // Our filter expression
pcap = pcap_open_offline(argv[0], errbuf);
if (pcap == NULL) {
fprintf(stderr, "error reading pcap file: %s\n", errbuf);
exit(1);
}
// Compile a basic filter expression, you can exam
if (pcap_compile(pcap, &fp, "vlan", 0, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
return 2;
}
while ((packet = pcap_next(pcap, &header) != NULL)
&& pcap_offline_filter(&fp, header, packet)) {
struct ip_header *ip;
unsigned int IP_header_length;
packet += sizeof(struct ether_header);
capture_len -= sizeof(struct ether_header);
ip = (struct ip_header*) packet;
IP_header_length = ip->vhl * 4; /* ip_hl is in 4-byte words */
char *sinfo = strdup(inet_ntoa(ip->src));
char *dinfo = strdup(inet_ntoa(ip->dst));
printf ("%s<-__->%s\n", sinfo ,dinfo);
free (sinfo);
free (dinfo);
}
...
I work under multi platform hw timestamp application. I am little bit confused in linux timestamp behaviour. I got error 'No message of desired type' from recvmsg and try handle it like error. My debug code below. As I see expected behavior:
Sent time stamps the outgoing packet is looped back to
the socket's error queue with the send time stamp attached.
Clone of sent packet can be received with recvmsg with flags |= MSG_ERRQUEUE.
recvmsg return outgoing packet with sock_extended_err (ee_errno==ENOMSG). ENOMSG is 'No message of desired type'.
So it look like linux should keep clone of outgoing packet in error queue for feature time calculation.
Should I skip ENOMSG in my error handler code?
if (errno == EAGAIN || errno == EINTR || errno == ENOMSG)
break;
Why it reported via error message? Probably it is not clear why ENOMSG expected or not?
I got: error description = 'No message of desired type' from recvmsg.
recvmsg(sock, &msg, recvmsg_flags|MSG_ERRQUEUE);
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
{
...
switch (cmsg->cmsg_level) {
case SOL_IP:
...
pkt.cmsg = (const struct cmsghdr*)cmsg;
pkt.msg = (const struct msghdr*)msg;
print_sol_ip(&pkt);
break;
}
}
/* Network level L3.
* Note that there is no TCP error queue;
* MSG_ERRQUEUE flag can not be used for socket type SOCK_STREAM.
* Thus, any errors can only be obtained as the value returned by the socket,
* or through option SO_ERROR.
*/
static void print_sol_ip(struct cmh_packet *pkt)
{
const int type = pkt->cmsg->cmsg_type;
const struct cmsghdr *cmsg = pkt->cmsg;
if (pkt->cmsg->cmsg_level != SOL_IP) {
printf("Wrong handler.\n");
return;
}
printf("SOL::IP::");
switch (type) {
case IP_RECVERR:
printf("RECVERR::\n");
struct sock_extended_err *err;
struct sockaddr *sk_addr;
struct sockaddr_in *sk_addrin;
socklen_t sk_addrlen;
err = (struct sock_extended_err *)CMSG_DATA(pkt->cmsg);
if ((sk_addr = malloc(sizeof(struct sockaddr))) == NULL) return;
/*
* The original destination address of the datagram that caused the error is supplied via msg_name
* For local errors, no address is passed (this can be checked with the cmsg_len member of the cmsghdr).
*/
printf("pointer to the data: %p\n", pkt->cmsg->__cmsg_data);
printf("data byte count, including header: %zd\n", pkt->cmsg->cmsg_len); /* CMSG_LEN */
printf("originating protocol: %d\n", pkt->cmsg->cmsg_level); /* SOL_SOCKET */
printf("protocol-specific type: %d\n", pkt->cmsg->cmsg_type); /* SCM_RIGHTS */
printf("%s = %d \n", "error number", err->ee_errno);
printf("%s = %d \n", "error origin", err->ee_origin); /* origin: SO_EE_ORIGIN_ICMP..LOCAL..NONE */
printf("%s = %d \n", "error type", err->ee_type); /* type: ICMP_NET_UNREACH..ICMP_HOST_UNREACH */
printf("%s = %d \n", "error code", err->ee_code);
printf("%s = %d \n", "error pad", err->ee_pad);
printf("%s = %d \n", "error info", err->ee_info);
printf("%s = %d \n", "error data", err->ee_data);
printf("error description = '%s'\n", strerror(err->ee_errno));
sk_addr = (struct sockaddr*)pkt->msg->msg_name;
sk_addrlen = pkt->msg->msg_namelen;
sk_addrin = (struct sockaddr_in*)sk_addr;
printf("%s:%d addrlen: %d\n", inet_ntoa(sk_addrin->sin_addr), ntohs(sk_addrin->sin_port), sk_addrlen);
print_af(sk_addr->sa_family);
break;
case IP_PKTINFO:
printf("PKTINFO::\n");
struct in_pktinfo *pki;
pki = (struct in_pktinfo *)CMSG_DATA(pkt->cmsg);
printf("Source interface index %u local address %s destination address %s",
pki->ipi_ifindex,
inet_ntoa(pki->ipi_spec_dst),
inet_ntoa(pki->ipi_addr));
break;
case IP_RECVOPTS: /* Routing header and other options are already installed on the local host. */
printf("IP_RECVOPTS::\n");
break;
case IP_RETOPTS:
printf("IP_RETOPTS::\n");
break;
case IP_TOS: /* the field is used to create network packet priorities.
There are several default values flag TOS: IPTOS_LOWDELAY,
in order to minimize delays for the traffic, IPTOS_THROUGHPUT,
to improve throughput, IPTOS_RELIABILITY, to increase
reliability, IPTOS_MINCOST, should be used for "optional data"
that can be sent at the minimum speed. */
printf("IP_TOS::\n");
break;
case IP_TTL: /* ip_default_ttl */
printf("IP_TTL::\n");
int ttl;
int *pttl;
pttl = (int *) CMSG_DATA(pkt->cmsg);
ttl = *pttl;
printf("ttl value = %d\n", ttl);
break;
case IP_HDRINCL: /* Enabling this flag means that the user has already added the IP header to the top of their data. */
printf("IP_HDRINCL::\n");
break;
case IP_MTU:
printf("IP_MTU::\n");
//TODO: getsockopt(sock, level, IP_MTU, IP_MTU_value_get, size);
break;
case IP_ROUTER_ALERT: /* It transmits this socket all packets that are sent with the option
* of IP Router Alert. This option is used only in the
* type of sockets RAW.
* */
printf("IP_ROUTER_ALERT::\n");
break;
case IP_MULTICAST_TTL:
printf("IP_MULTICAST_TTL::\n");
break;
default:
printf("TYPE %d\n", cmsg->cmsg_type);
break;
}
}
It was clear that I got ENOMSG from loopback timestamped skb. Like described in linux/Documentation/networking/timestamping.txt. And finally I found that ip_recv_error() extract skb from sk->sk_error_queue (errqueue) and reset error sk->sk_err = 0. But it also check if exist additional skb's in sk_error_queue. My errqueue had several skb in sk_error_queue that is why ip_recv_error in the end check errqueue found that it had skb's.
skb2 = skb_peek(&sk->sk_error_queue);
it had skb2 and sk->sk_err was reset back to ENOMSG.
sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno;
skb2 previously came from
void skb_tstamp_tx(struct sk_buff *orig_skb,
struct skb_shared_hwtstamps *hwtstamps)
serr = SKB_EXT_ERR(skb);
serr->ee.ee_errno = ENOMSG;
No matter from which queue you will read if errqueue contain skb (skb cb control buffer should contain error in ee_errno) recvmsg will report error. Because udp_recvmsg call __skb_recv_datagram
and it check if struct sk containe error.
int error = sock_error(sk);
If so it will report -1 with error that was found in skb. So it is critical for udp to read all messages from sk_error_queue. Because during last read sk->sk_err will reset (or getsockopt(SO_ERROR)). Or just make skip and it will read next time with some delay.
I am trying to work with pcap but I have some troubles reading a sequence number.
I use the code below to listen for an incomming packet, but when I try to read the sequence number the program halts without an error. Just stops the execution silently.
This code largely comes from the pcap tutorial from the tcpdump site.
bpf_u_int32 net=0, mask=0;
pcap_t *descr = NULL;
struct bpf_program filter;
struct ip *iphdr = NULL;
struct tcphdr *tcphdr = NULL;
struct pcap_pkthdr pkthdr;
const unsigned char *packet = NULL;
char pcap_errbuf[PCAP_ERRBUF_SIZE];
char filter_exp[] = "tcp port 111 dst host 10.0.0.10";
char * dev;
// Define the device
dev = pcap_lookupdev(pcap_errbuf);
if (dev == NULL) {
printf( "Couldn't find default device: %s\n", pcap_errbuf); fflush(stdout);
exit(1);
}
// Find the properties for the device
if( pcap_lookupnet(dev, &net, &mask, pcap_errbuf) == -1 ){
printf("Couldn't get netmask for device %s: %s\n", dev, pcap_errbuf); fflush(stdout);
net = 0;
mask = 0;
}
// Open the session in non-promiscuous mode
descr = pcap_open_live(dev, BUFSIZ, 0, 1000, pcap_errbuf);
if (descr == NULL) {
printf("Couldn't open device %s: %s\n", dev, pcap_errbuf); fflush(stdout);
exit(1);
}
// Compile and apply the filter
if( pcap_compile(descr, &filter, filter_exp, 0, net) == -1) {
printf("Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(descr)); fflush(stdout);
exit(1);
}
if (pcap_setfilter(descr, &filter) == -1) {
printf( "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(descr)); fflush(stdout);
exit(1);
}
packet = pcap_next(descr, &pkthdr );
iphdr = (struct ip *)(packet+14);
tcphdr = (struct tcphdr *)(packet+14+20);
printf("test1\n"); fflush( stdout );
printf("SEQ: %d\n", ntohl(tcphdr->th_seq) ); fflush( stdout );
printf("test2\n");
pcap_close(descr);
The "test1" is printed, but the "test2" and the "SEQ: %d" isn't. Its hard to debug if there's no error at all.
Anyone seen this before?
Thanks
Nikolai Fetissov is correct - you must check whether pcap_next() returns NULL or not. It might return NULL if, for example, the timeout expires and no packets have arrived. In that case, you should keep looping until it returns a non-null value.
However, it could also return NULL if there's an error, and that error might mean that you won't get any more packets. A better routine to use is pcap_next_ex(), which returns returns 1 if the packet was read without problems, 0 if packets are being read from a live capture and the timeout expired, -1 if an error occurred while reading the packet, and -2 if packets are being read from a savefile and there are no more packets to read from the savefile.
In your case, you're doing a live capture, so you should use pcap_next_ex(), and loop until it returns either 1, in which case you print the packet information, or -1, in which case you report an error and exit:
int status;
while ((status = pcap_next_ex(descr, &pkthdr, &packet)) == 0)
;
if (status == -1) {
fprintf(stderr, "pcap_next_ex failed: %s\n", pcap_geterr(descr));
exit(1);
}
iphdr = (struct ip *)(packet+14);
tcphdr = (struct tcphdr *)(packet+14+20);
printf("test1\n"); fflush( stdout );
printf("SEQ: %d\n", ntohl(tcphdr->th_seq) ); fflush( stdout );
printf("test2\n");
pcap_close(descr);
Note also that there's no guarantee that the IPv4 header is 20 bytes long - it could be longer, so you need to extract the header length from the first byte of the IPv4 header (the header length/version field), multiply it by 4 (as it's in units of 4-byte words), and use that when calculating the address of the TCP header, rather than using a hard-coded 20.
In addition, you should also make sure that the link-layer header type of the device, as returned by pcap_datalink(descr), is DLT_EN10MB, to make sure the packets have Ethernet headers rather than some other type of header.
In addition, I just copied your printf code, as I was concentrating on the capture problem. Somebody else added an ntohl() call, which is necessary when printing the sequence number - multi-byte numerical fields in IP and TCP headers are in "network byte order", i.e. big-endian, but you might be running on a little-endian machine, so the sequence number has to be converted to the host byte order before printing it.