I don't understand why my following code doesn't work properly.
When I sniff ethernet the bytes that I receive are shifted by 2 bytes
I send use sudo sendip -p ipv4 -p udp 127.0.0.1 -d r8 to send packet to my computer
With the following code:
#include <pcap/pcap.h>
#define SIZE_ETHERNET 14
#define ETHER_ADDR_LEN 6
struct sniff_ethernet {
u_char ether_dhost[ETHER_ADDR_LEN];
u_char ether_shost[ETHER_ADDR_LEN];
u_short ether_type;
};
void packet_call_back(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) {
struct sniff_ethernet *ethernet = (struct sniff_ethernet*)(bytes);
printf("test = %#x\n", ethernet->ether_type); // print is 0x0
}
int main(void) {
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t *handle = pcap_create("any", errbuf);
pcap_set_timeout(handle, -1);
pcap_loop(handle, 0, packet_call_back, NULL);
pcap_close(handle);
}
The ether_type that I receive is incorrect here 0x0000 instead of 0x0800
but if instead of: struct sniff_ethernet *ethernet = (struct sniff_ethernet*)(bytes);
I put struct sniff_ethernet *ethernet = (struct sniff_ethernet*)(bytes + 2);
here everything work fine and I can analyse my packet easily.
I compile with gcc test.c -l pcap
And I work on an Ubuntu VM with parallels
Thank's for reading
When I sniff ethernet the bytes that I receive are shifted by 2 bytes
You're not sniffing Ethernet, you're sniffing the "any" device. That will capture on all interfaces, whether they're Ethernet interfaces or not; even if they are all Ethernet interfaces, that doesn't make a difference.
In order to make this work, a link-layer header type other than Ethernet is used on the "any" device. On Linux, that's a special header; here's what that header looks like.
ALL programs that use libpcap to capture network traffic or to read a capture file must, after opening the capture device (pcap_activate(), pcap_open_live(), pcap_open()) or the capture file (pcap_open_offline()), call pcap_datalink() to determine the link-layer header type from the device or the file.
That function returns a value that can be found in the link-layer header types list; it will be one of the values with a name that begins with DLT_. Do not check for the numerical values given there; check for the DLT_ values.
Related
in r8169 driver from realtek it does
rx_buf = page_address(tp->Rx_databuff[entry]);
dma_sync_single_for_cpu(d, addr, pkt_size, DMA_FROM_DEVICE);
prefetch(rx_buf);
skb_copy_to_linear_data(skb, rx_buf, pkt_size);<----//Do I get packet at this????
skb->tail += pkt_size;
skb->len = pkt_size;
dma_sync_single_for_device(d, addr, pkt_size, DMA_FROM_DEVICE);
//csum...
skb->protocol = eth_type_trans(skb, dev);
napi_gro_receive(&tp->napi, skb);
this is inside rtl_rx function called from poll of driver. I like to know in above code how can I extract the entire packet from skb at which line afterwards.
I assume at this line
skb_copy_to_linear_data(skb, rx_buf, pkt_size);
I should have a packet, but like to know the correct way I can create a kmalloc obect like
void *packet= kmalloc(....sizeof(struct ethhdr)+sizeof(struct iphdr)+sizeof(tcphdr))
and read ethernet ip and tcp headers from void *packet
How to achieve it
Or should I simple do skb_netword_header, skb_tcp_header, etc... to extract the headers and payload from skb after it get populated in above lines,
or can I simply cast as
rx_buf = page_address(tp->Rx_databuff[entry]);
struct ether_header ethhdr_of_packet=(struct eher_header *) rx_buf;
Should it work?
The highlighted line (the one with skb_copy_to_linear_data()) indeed copies entire packet data from the buffer in the driver-internal Rx ring (rx_buf) to the data buffer of the skb.
static inline void skb_copy_to_linear_data(struct sk_buff *skb,
const void *from,
const unsigned int len)
{
memcpy(skb->data, from, len);
}
Casting the rx_buf pointer to Ethernet header should be OK, too. However, the purpose of accessing the packet header like this is rather vague in the question of yours. Are you trying to just print ("dump") the packet or do you intend to copy the packet data to a completely different buffer to be consumed elsewhere?
According to Wikipedia, a traceroute program
Traceroute, by default, sends a sequence of User Datagram Protocol
(UDP) packets addressed to a destination host[...] The time-to-live
(TTL) value, also known as hop limit, is used in determining the
intermediate routers being traversed towards the destination. Routers
decrement packets' TTL value by 1 when routing and discard packets
whose TTL value has reached zero, returning the ICMP error message
ICMP Time Exceeded.[..]
I started writing a program (using an example UDP program as a guide) to adhere to this specification,
#include <sys/socket.h>
#include <assert.h>
#include <netinet/udp.h> //Provides declarations for udp header
#include <netinet/ip.h> //Provides declarations for ip header
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#define DATAGRAM_LEN sizeof(struct iphdr) + sizeof(struct iphdr)
unsigned short csum(unsigned short *ptr,int nbytes) {
register long sum;
unsigned short oddbyte;
register short answer;
sum=0;
while(nbytes>1) {
sum+=*ptr++;
nbytes-=2;
}
if(nbytes==1) {
oddbyte=0;
*((u_char*)&oddbyte)=*(u_char*)ptr;
sum+=oddbyte;
}
sum = (sum>>16)+(sum & 0xffff);
sum = sum + (sum>>16);
answer=(short)~sum;
return(answer);
}
char *new_packet(int ttl, struct sockaddr_in sin) {
static int id = 0;
char *datagram = malloc(DATAGRAM_LEN);
struct iphdr *iph = (struct iphdr*) datagram;
struct udphdr *udph = (struct udphdr*)(datagram + sizeof (struct iphdr));
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = DATAGRAM_LEN;
iph->id = htonl(++id); //Id of this packet
iph->frag_off = 0;
iph->ttl = ttl;
iph->protocol = IPPROTO_UDP;
iph->saddr = inet_addr("127.0.0.1");//Spoof the source ip address
iph->daddr = sin.sin_addr.s_addr;
iph->check = csum((unsigned short*)datagram, iph->tot_len);
udph->source = htons(6666);
udph->dest = htons(8622);
udph->len = htons(8); //udp header size
udph->check = csum((unsigned short*)datagram, DATAGRAM_LEN);
return datagram;
}
int main(int argc, char **argv) {
int s, ttl, repeat;
struct sockaddr_in sin;
char *data;
printf("\n");
if (argc != 3) {
printf("usage: %s <host> <port>", argv[0]);
return __LINE__;
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(argv[1]);
sin.sin_port = htons(atoi(argv[2]));
if ((s = socket(AF_PACKET, SOCK_RAW, 0)) < 0) {
printf("Failed to create socket.\n");
return __LINE__;
}
ttl = 1, repeat = 0;
while (ttl < 2) {
data = new_packet(ttl);
if (write(s, data, DATAGRAM_LEN) != DATAGRAM_LEN) {
printf("Socket failed to send packet.\n");
return __LINE__;
}
read(s, data, DATAGRAM_LEN);
free(data);
if (++repeat > 2) {
repeat = 0;
ttl++;
}
}
return 0;
}
... however at this point I have a few questions.
Is read(s, data, ... reading whole packets at a time, or do I need to parse the data read from the socket; seeking markers particular to IP packets?
What is the best way to uniquely mark my packets as they return to my box as expired?
Should I set up a second socket with the IPPROTO_ICMP flag, or is it easier to write a filter; accepting everything?
Do any other common mistakes exist; or are any common obstacles foreseeable?
Here are some of my suggestions (based on assumption it's a Linux machine).
read packets
You might want to read whole 1500 byte packets (entire Ethernet frame). Don't worry - smaller frames would still be read completely with read returning the length of data read.
Best way to add marker is to have some UDP payload (a simple unsigned int) should be good enough. Increase it on every packet sent. (I just did a tcpdump on traceroute - the ICMP error - does return an entire IP frame back - so you can look at the returned IP frame, parse the UDP payload and so on. Note your DATAGRAM_LEN would change accordingly. ) Of course you can use ID - but be careful that ID is mainly used by fragmentation. You should be okay with that - 'cos you'd not be approaching fragmentation limit on any intermediate routers with these packet sizes. Generally, not a good idea to 'steal' protocol fields that are meant for something else for our custom purpose.
A cleaner way could be to actually use IPPROTO_ICMP on raw sockets (if manuals are installed on your machine man 7 raw and man 7 icmp). You would not want to receive copy of all packets on your device and ignore those that are not ICMP.
If you are using type SOCKET_RAW on AF_PACKET, you will have to manually attach a link layer header or you can do SOCKET_DGRAM and check. Also man 7 packet for lot of subtleties.
Hope that helps or are you looking at some actual code?
A common pitfall is that programming at this level needs very careful use of the proper include files. For instance, your program as-is won't compile on NetBSD, which is typically quite strict in following relevant standards.
Even when I add some includes, there is no struct iphdr but there is a struct udpiphdr instead.
So for now the rest of my answer is not based on trying your program in practice.
read(2) can be used to read single packets at a time. For packet-oriented protocols, such as UDP, you'll never get more data from it than a single packet.
However you can also use recvfrom(2), recv(2) or recvmsg(2) to receive the packets.
If fildes refers to a socket, read() shall be equivalent to recv()
with no flags set.
To identify the packets, I believe using the id field is typically done, as you have already. I am not sure what you mean with "mark my packets as they return to my box as expired", since your packets don't return to you. What you may get back are ICMP Time Exceeded messages. These usually arrive within a few seconds, if they arrive at all. Sometimes they are not sent, sometimes they may be blocked by misconfigured routers between you and their sender.
Note that this assumes that the IP ID you set up in your packet is respected by the network stack you're using. It is possible that it doesn't, and replaces your chosen ID with a different one. Van Jacobson, the original author of the traceroute command as found in NetBSD therefore use a different method:
* The udp port usage may appear bizarre (well, ok, it is bizarre).
* The problem is that an icmp message only contains 8 bytes of
* data from the original datagram. 8 bytes is the size of a udp
* header so, if we want to associate replies with the original
* datagram, the necessary information must be encoded into the
* udp header (the ip id could be used but there's no way to
* interlock with the kernel's assignment of ip id's and, anyway,
* it would have taken a lot more kernel hacking to allow this
* code to set the ip id). So, to allow two or more users to
* use traceroute simultaneously, we use this task's pid as the
* source port (the high bit is set to move the port number out
* of the "likely" range). To keep track of which probe is being
* replied to (so times and/or hop counts don't get confused by a
* reply that was delayed in transit), we increment the destination
* port number before each probe.
Using a IPPROTO_ICMP socket for receiving the replies is more likely to be efficient than trying to receive all packets. It would also require fewer privileges to do so. Of course sending raw packets normally already requires root, but it could make a difference if a more fine-grained permission system is in use.
I have a client on PC. I have a server on PC. The client and server are connected via a router with firmware based on Linux OS.
The client sends a packet to the server and receive a response. The router must intercept the packets and modify it. Something like sniffing but it's not a sniffing because i need to modify the packets.
I must to write a program for this.
I tried to open a raw socket on the router, but reсvfrom on raw socket does not intercept the packet and just copy it. The packet is going on.
Could you suggest me any way to solve this problem?
P.S. Sorry for my bad English. :)
I'd use a mix of iptables and libnetfilter_queue (assuming your kernel is relatively recent)
Add to the iptables a rules that forward all the udp packets to the NFQUEUE 0 in order to get packets from kernel to user space.
iptables -A INPUT -p udp -m udp --dport xxxxx -j NFQUEUE --queue-num 0
Build a process who listen to the NFQUEUE number 0, modify payload and give the full packet back to the kernel space using libnetfilter_queue capabilities. Follow this link to know how to do it.
In a nutshell you have to open the queue 0 (nfq_create_queue), set the mode in order to get the content of the packet (nfq_set_mode), then loop in an infinite recv to get ever udp packet filtered by iptables
fd = nfq_fd(h);
while ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) {
printf("pkt received\n");
nfq_handle_packet(h, buf, rv);
}
Everytime you call nfq_handle_packet is called, the callback defined during the nfq_create_queue phase is called. In that callback you have to modify the payload, update the size and recalculate the checksum, then set as "valid" with nfq_set_verdict
I wrote the module for the kernel and some applications. Module uses netfilter and discards packets that I need to netfilter_queue. The application processes a queue and I decide what to do with each package.
uint hook_main(uint hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *) )
{
struct iphdr *ip;
struct udphdr *udp;
if (skb->protocol == htons(ETH_P_IP)){
ip = (struct iphdr *)(skb->data);
if (ip->version == 4 && ip->protocol == IPPROTO_UDP){
udp = (struct udphdr *)(skb->data + sizeof(struct iphdr));
if(ntohs(udp->dest) == SOME_PORT){
return NF_QUEUE;
}
}
}
return NF_ACCEPT;
}
int init_module ()
{
printk("[udp-catch] start udp-catch\n");
catch_hook.hook = hook_main;
catch_hook.owner = THIS_MODULE;
catch_hook.pf = PF_INET;
catch_hook.hooknum = NF_INET_FORWARD;
catch_hook.priority = NF_IP_PRI_FIRST;
nf_register_hook(&catch_hook);
return 0;
}
And a redesigned sample from netfilter.org is the application.
Routers will automatically send out whatever they receive on their other ports.
e.g. For a 4 port router, what comes in on port 1 will be sent out on ports 2,3 & 4.
To do what you require, you need another PC with 2 network cards. Connect your client PC to one network card, and the server PC to the other.
Then your program will need to recvfrom on one network card, modify the packet and sendto on the other network card.
My Ubuntu virtual machine's IP address is 192.168.1.110. Everything else looks fine. I don't know what is wrong with the code. Maybe I'm using a wrong package header structure?
Below is my code and output. Again my host IP should be 192.168.1.110 and port for now is definitely wrong.
sudo ./sniffall 0
84.72.137.105:38055 192.168.1.105:56652
192.168.1.105:56652 174.141.213.124:28073
84.72.137.105:38055 192.168.1.105:56652
192.168.1.105:56652 174.141.213.124:28073
84.72.137.105:38055 192.168.1.105:56652
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#include <netinet/ether.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
void getPacket(u_char *args, const struct pcap_pkthdr *pkthdr, const u_char *packet){
struct ip *ip;
struct tcphdr *tcp;
ip = (struct ip*)(packet+sizeof(struct ether_header));
tcp = (struct tcphdr*)(packet+sizeof(struct ether_header)+sizeof(struct ip));
char* src = inet_ntoa(ip->ip_src);
printf("%s:%d ",src,tcp->source);
char* dst = inet_ntoa(ip->ip_dst);
printf(" %s:%d\n", dst, tcp->dest);
}
int main(int argc, char *argv[]){
char errbuf[PCAP_ERRBUF_SIZE], *device;
device = argv[1];
pcap_t *handle;
handle = pcap_open_live(device, BUFSIZ, 1, 1000, errbuf);
if(!handle){
device = pcap_lookupdev(errbuf);
handle = pcap_open_live(device, BUFSIZ, 1, 1000, errbuf);
if(!handle){
printf("Couldn't open device %s: %s\n", device, errbuf);
}
}
pcap_loop(handle, 5, getPacket, NULL);
return 0;
}
Pcap is going to show some other traffic other than your system's if you're in promiscuous mode. Why you're seeing specific packets that aren't being sent or received from your system is going to be dependent a bit on your network configuration. Some ethernet switches will occasionally leak packets destined to other systems if they're unsure where they should go, etc.
You also need to need to convert between byte orders. In most common cases now, "network byte order" is not the same as your machine's byte order. To print out the port number, you need to do something like:
printf("%s:%d ",src,ntohs(tcp->source));
Also, you may want to try struct iphdr instead of struct ip. I've seen instances before where there were multiple definitions of a struct named ip in headers, but iphdr was always right for me.
Remember that you can always run tcpdump in another window to see what packets are actually coming in, it's possible that you're receiving more traffic than you are expecting.
First, after calling pcap_open_live(), call pcap_datalink() on handle and, if it doesn't return DLT_EN10MB, either exit or rewrite your program so that it can handle the value it returns. See the tcpdump.org link-layer header types page for a description of the supported values from pcap_datalink().
Second, do NOT assume that the packet is an IPv4 packet unless you have either installed a filter of "ip" or have checked the packet type (e.g., the type field in an Ethernet header) to make sure the packet is an IPv4 packet.
Third, do NOT assume that the header of an IPv4 packet is exactly sizeof(struct ip) bytes long. I assume sizeof(struct ip) will be 20, which is the minimum length of an IPv4 header, but the header may include options - check the "header length" field of the IPv4 header (which is in units of 4-byte words, so a value of 5 means "20 bytes") and use that as the length of the header (make sure it's at least 5 - if it's less than 5, the packet is not valid - and then multiply by 4 to get the length of the header).
Fourth, do NOT assume that the packet is a TCP packet unless you have either installed a filter of "ip and tcp" or just "tcp" (with the latter, you'll still have to check yourself to see whether it's an IPv4 packet) or have checked the "protocol" field of the IPv4 header to make sure it has a value of 6 (for TCP).
In this snippet of code i capture the packet and i am trying to display the source and destination address by using inet_ntoa , even before that i am printing the packet src and dst address in hexa format. The problem here is both do not match, the o/p of inet_ntoa is wrong as shown in o/p
the src ip address should be 172.28.6.87 but inet_ntoa shows 86.212.172.28
the src ip address should be 172.28.6.110 but inet_ntoa shows 6.87.172.28
char *ptr = NULL;
ptr_fltr = (struct packet_filter*)(packet);
memcpy(out_data,packet,50);
printf("\n");
for(i= 28;i<36;i++)
printf("%#x\t",out_data[i]);
printf("*******************************************************************\n");
printf("---------------------Received Packet Info--------------------\n");
ptr = inet_ntoa(ptr_fltr->ip.ip_src);
printf("Source Ip Addr :%s\n",ptr);
here
struct packet_filter
{
struct mac_filter mac;
struct ip_filter ip;
union {
struct udp_filter proto;
}protocol;
}__attribute__((packed));
struct ip_filter
{
u_char ip_vhl;
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src; /* source and dest address */
struct in_addr ip_dst; /* source and dest address */
}__attribute__((packed));
output
0xac 0x1c 0x6 0x57 0xac 0x1c 0x6 0x6e
************************************************************
--------------------Received Packet Info--------------------
Source Ip Addr :86.212.172.28
Destination Ip Addr :6.87.172.28
Clearly your struct is off by two bytes by the time you get to the IP addresses. I've checked against the IPv4 protocol and that bit looks OK. So I suspect the struct mac is wrong. I presume struct mac is meant to be an ethernet frame. If so, it's already a bit suspicious because an Ethernet frame is not of a fixed length.
Also, (assuming you are getting these from the Berkeley Packet Filter) make sure you calculate the start of the packet correctly from the bpf header (you can't rely on sizeof(struct bpf_header)).
Your IP packet starts at offset 16, and if you have copied struct mac from ethernet header it is 14 bytes long. Looks like there is some unexpected data in packet.