I am using l2fwd-dpdk application from which I can extract 5-tuples, and can see if DNS Packet is present or not.
Now I want to classify the DNS Packet using dpdk, for which I am failing.
Here is my code.
struct rte_udp_hdr *udp_hdr;
struct dnshdr *dns_hdr;
if (rte_be_to_cpu_16(udp_hdr->dst_port) == 53)
{
printf("DNS Packet");
char *dns_hdr = (char *)udp_hdr + sizeof(rte_udp_hdr);
}
I want to separate
Flags
Rdata
Class
TTL
and save them separately. Is there any way around, I can comfortable to use cpp wrapper as well.
DPDK as of 21.08 does not house any header or structure to typecast to DNS packet. Hence easiest way to solve the issue as mentioned by #wildplasser is to declare your custom DNS header and use it. In your code snippet, you already have struct dnshdr *dns_hdr; So the easier way is to modify your existing code to reflect
struct rte_udp_hdr *udp_hdr;
struct dnshdr *dns_hdr;
/* use DPDK mtod API to get the start of ethernet frame */
/* check for packet size, ether type, IP protocol */
/* update udp_hdr to position in the packet */
if (rte_be_to_cpu_16(udp_hdr->dst_port) == 53)
{
printf("DNS Packet");
struct dnshdr *dns_hdr = (struct dnshdr *)((char *)udp_hdr + sizeof(rte_udp_hdr));
}
Note: Possible structure definition code snippet would be
typedef struct {
uint16_t id;
uint16_t rd:1;
uint16_t tc:1;
uint16_t aa:1;
uint16_t opcode:4;
uint16_t qr:1;
uint16_t rcode:4;
uint16_t zero:3;
uint16_t ra:1;
uint16_t qcount; /* question count */
uint16_t ancount; /* Answer record count */
uint16_t nscount; /* Name Server (Autority Record) Count */
uint16_t adcount; /* Additional Record Count */
} custom_dnshdr;
custom_dnshdr *dns_hdr = (custom_dnshdr *) ((char *)udp_hdr + sizeof(rte_udp_hdr));
Related
The pcap callback function returns the IP header and data as follows:
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data);
My understanding is the first 4 bits of the pkt_data is the IP version from which I can determine it is is IPv4 or IPv6. However, I've tried a few different ways to read the first 4 bits and I'm getting data that does not make sense.
For example, I defined the following structure:
struct ipdata {
u_char version : 4;
u_char dontcare : 4;
};
And then I tried to get the ip version using this code:
ipdata* pipdata;
pipdata = (ipdata*) pkt_data;
ip_ver = pipdata->version;
printf(" %d ", ip_ver);
The above method prints values of 3, 6, 9, 8 and 12. If I watch the traffic at the same time in Wireshark I see that most of the packets are IPv6.
Could someone who has done this clarify how would I go about reading the IP version?
Figure out the answer. Npcap returns the entire ethernet packet, so the first 14 bytes are the Ethernet header:
/* Length of the Ethernet Header (Data Link Layer) */
#define ETHERNET_HEADER_LEN 14
/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN 6
/* Ethernet header */
struct sniff_ethernet {
u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address (i.e. Destination MAC Address) */
u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address (i.e. Source MAC Address) */
u_short ether_type; /* IP? ARP? RARP? etc */
};
You can figure out whether it is an IPv4 or IPv6 packet by looking at the ether_type in the above structure rather than the version in the IP header, such as:
/* Common ethernet types in Hex*/
#define ETHERNET_TYPE_IPv4 0x0800
#define ETHERNET_TYPE_IPv6 0x86DD
u_short eth_type;
ethernet = (struct sniff_ethernet*)(pkt_data);
eth_type = ntohs(ethernet->ether_type);
if (eth_type == ETHERNET_TYPE_IPv4) {
ipv4_handler(pkt_data);
}
else if (eth_type == ETHERNET_TYPE_IPv6)
{
ipv6_handler(pkt_data);
}
The IP header starts right after the ethernet header, so you can get it with code such as the following example for an IPv6 packet:
/* IPv6 header */
typedef struct ipv6_header
{
unsigned int
version : 4,
traffic_class : 8,
flow_label : 20;
uint16_t length;
uint8_t next_header;
uint8_t hop_limit;
struct in6_addr saddr;
struct in6_addr daddr;
} ipv6_header;
const ipv6_header* iph;
iph = (ipv6_header*)(pkt_data + ETHERNET_HEADER_LEN);
From there you can access the version and other information about the IP header. See this post for more information: Getting Npcap IPv6 source and destination addresses
I am taking a class on computer and network security. We are writing a packet spoofer. I could just download one from the internet and use it, but I prefer writing the stuff myself. Below is the struct that I use to represent the ip header which I am basing off of the wikipedia article. I am attempting to send an icmp ping packet. I have done it successfully, but only after assigning the value of the ip header length to the version field, and vice versa. Somehow I have setup my struct wrong, or I am assigning the values wrong, and I am not sure what I am doing incorrectly.
struct ip_header
{
uint8_t version : 4 // version
, ihl : 4; // ip header length
uint8_t dscp : 6 // differentiated services code point
, ecn : 2; // explicit congestion notification
uint16_t total_length; // entire packet size in bytes
uint16_t identification; // a unique identifier
uint16_t flags : 3 // control and identify fragments
, frag_offset : 13; // offset of fragment relative to the original
uint8_t ttl; // how many hops the packet is allowd to travel
uint8_t protocol; // what protocol is in use
uint16_t checksum; // value used to determine bad packets
uint32_t src_ip; // where the packet is form
uint32_t dest_ip; // where the packet is going
};
If I assign the version and ihl, like below, wireshark reports an error with the header, "Bogus IPV4 version (0, must be 4)".
char buffer[1024];
struct ip_header* ip = (struct ip_header*) buffer;
ip->version = 4;
ip->ihl = 5;
However, after changing to the following listing, the ICMP request goes through just fine.
char buffer[1024];
struct ip_header* ip = (struct ip_header*) buffer;
ip->version = 5;
ip->ihl = 4;
I have tried placing htons around the numbers, but that doesn't seem to do anything useful. What am I missing here?
You simply need to correct your structure's endianness. Look at the IP header structure defined in the <netinet/ip.h> file:
struct iphdr
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ihl:4;
unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
unsigned int version:4;
unsigned int ihl:4;
#else
# error "Please fix <bits/endian.h>"
#endif
uint8_t tos;
uint16_t tot_len;
uint16_t id;
uint16_t frag_off;
uint8_t ttl;
uint8_t protocol;
uint16_t check;
uint32_t saddr;
uint32_t daddr;
/*The options start here. */
};
this is my first time posting on stack overflow so be gentle. I am writing a networking program in c to run on linux machines. The goal of my program is to be able to capture packets sent to it, change the source ip and hw address, rebuild the packet with the new info and send it back out onto the wire. My question relates to the rebuilding process. I have some structs that I am using to hold information about various headers in my programs. Detailed here
struct my_ip
{
u_int8_t ip_vhl; /* header length, version */
#define IP_V(ip) (((ip)->ip_vhl & 0xf0) >> 4)
#define IP_HL(ip) ((ip)->ip_vhl & 0x0f)
u_int8_t ip_tos; /* type of service */
u_int16_t ip_len; /* total length */
u_int16_t ip_id; /* identification */
u_int16_t ip_off; /* fragment offset field */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
u_int8_t ip_ttl; /* time to live */
u_int8_t ip_p; /* protocol */
u_int16_t ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
};
/* UDP header */
struct sniff_udp
{
u_short uh_sport; /* source port */
u_short uh_dport; /* destination port */
u_short uh_ulen; /* udp length */
u_short uh_sum; /* udp checksum */
};
#define SIZE_UDP 8 /* length of UDP header */
#define SIZE_ETHERNET 14
As well as a few other structs from the pcap library(like ether_header). I cast the u_char* to these structs like so
struct my_ip* ip = (struct my_ip*)(packet + sizeof(struct ether_header));
struct ether_header* eptr = (struct ether_header *) packet;
Where packet is a u_char holding the entirety of the packet
My question is, once I have modified data within these structures how do I cast all of my stucts back into a single u_char string? I am trying to cast each struct to fill a different segment of string in the same way a packet is structured
This is the code I have so far.
void buildPacket(sniff_udp *udp, ether_header *ethh, my_ip *ip, u_char *payload, u_char *buffer)
{
memset(buffer,0, (sizeof(udp)+sizeof(ethh)+sizeof(ip)+sizeof(payload)));
buffer=(u_char *)(ethh); // adds layer 2 header
(buffer+SIZE_ETHERNET)= (u_char *)ip; // adds layer 3 header
(buffer+SIZE_ETHERNET+sizeof(ip))=(u_char *) udp; // adds protocol header
(buffer+SIZE_ETHERNET+sizeof(ip)+SIZE_UDP)=(u_char *)payload; // adds payload
}
This isn't the correct way to do it from what I've gathered. How can I cast multiple structs to the same string?
Something like
(buffer+SIZE_ETHERNET)= (u_char *)ip; // adds layer 3 header
isn't valid because the lefthand operator of = won't be a (modifable) lvalue.
You can use memcpy() to copy contents of memory. The correct code should be like this:
void buildPacket(sniff_udp *udp, ether_header *ethh, my_ip *ip, u_char *payload, u_char *buffer)
{
memset(buffer,0, (sizeof(udp)+sizeof(ethh)+sizeof(ip)+sizeof(payload)));
memcpy(buffer, ethh, SIZE_ETHERNET); // adds layer 2 header
memcpy(buffer+SIZE_ETHERNET, ip, sizeof(ip)); // adds layer 3 header
memcpy(buffer+SIZE_ETHERNET+sizeof(ip), udp, SIZE_UDP); // adds protocol header
memcpy(buffer+SIZE_ETHERNET+sizeof(ip)+SIZE_UDP, payload, sizeof(payload)); // adds payload
}
This code doesn't seem correct because sizeof(udp), sizeof(ethh), sizeof(ip) and sizeof(payload) will return the size of pointers, not what is pointed, and I don't think it is what you want. Use correct size instead of them.
I'm trying to send some manually crafted ARP packets over the network,more specifically an ARP request to get the MAC address of a host.
I can't get the final packet right, on wireshark it stills shows some inconsistency.
Let me walk you through :
Here are the struct & typedef I use all over the program ,
I've defined
a IP struct ( => in_addr )
a MAC struct ( => ether_addr )
a Host struct composed of a MAC & IP
Custom struct to represent a Ethernet frame & an ARP frame.
The code:
#define ETH_ADDR_SIZE 6
#define IP_ADDR_SIZE 4
typedef u_char Packet;
typedef struct in_addr IP;
typedef struct ether_addr MAC;
struct Host {
IP ip;
MAC mac;
};
typedef struct pkt_eth {
MAC dest;
MAC src;
u_short type;
} pkt_eth;
typedef struct pkt_arp {
u_short htype;/* hardware type => ethernet , etc */
u_short ptype; /*protocol type => ipv4 or ipv6 */
u_char hard_addr_len; /* usually 6 bytes for ethernet */
u_char proto_addr_len; /*usually 8 bytes for ipv4 */
u_short opcode; /* type of arp */
MAC hard_addr_send;
IP proto_addr_send;
MAC hard_addr_dest;
IP proto_addr_dest;
} pkt_arp;
/* Designate our own MAC / IP addresses of the interface */
extern MAC mac;
extern IP ip;
extern char * interface;
/* Just some vars used to compare with the struct we use */
const MAC broadcast_mac = { 0xff,0xff,0xff,0xff,0xff,0xff };
const MAC null_mac = { 0x00,0x00,0x00,0x00,0x00,0x00 };
const IP broadcast_ip = { 0xffffffff };
const IP null_ip = { 0x00000000 };
const struct Host null_host = {{ 0x00000000 },
{ 0x00,0x00,0x00,0x00,0x00,0x00 }};
/* Empty mac address which can be used as a temp variable */
MAC tmp_mac = { 0x00,0x00,0x00,0x00,0x00,0x00 };
IP tmp_ip = { 0x00000000 };
Here is the relevant function :
int
arp_resolve_mac ( struct Host * host )
{
struct pkt_arp * arp;
struct pkt_eth * eth;
/*Create the request packet */
Packet * request = arp_packet(REQUEST);
eth = (struct pkt_eth *) (request);
arp = (struct pkt_arp *) (request + ETH_SIZE);
/* ethernet frame */
copy_mac(ð->dest,&broadcast_mac);
copy_mac(ð->src,&mac);
/* arp request => mac dest address set to null */
copy_mac(&arp->hard_addr_send,&mac);
copy_mac(&arp->hard_addr_dest,&null_mac);
/* arp request => target ip ! */
copy_ip(&arp->proto_addr_send,&ip);
copy_ip(&arp->proto_addr_dest,&host->ip);
/* Set up sniffing. Better to do it before so less
* prepare time and if any error occurs, no need to send
* the packet. less intrusive */
pcap_init(interface,"arp");
pcap_set_arp_analyzer(arp_analyzer_resolv);
/* Sets the tmp ip variable so we will know if it the right
* response we get or a response coming from another source */
tmp_ip = host->ip;
/* sends the packet */
if(pcap_send_packet(request,ARP_PACKET_SIZE) == -1) {
fprintf(stderr,"Error while sending ARP request packet.\n");
return -1;
}
....
}
Packet *
arp_packet ( int opcode )
{
struct pkt_arp * arp;
struct pkt_eth * eth;
Packet * bytes = (Packet *) malloc(ARP_PACKET_SIZE);
if(bytes == NULL) {
fprintf(stderr,"Could not alloc ARP packet.\n");
return NULL;
}
eth = (struct pkt_eth *) (bytes);
eth->type = htons(ETHERTYPE_ARP);
/* length about hard / proto ... */
arp = (struct pkt_arp *) (bytes + ETH_SIZE);
arp->htype = htons(1);
arp->ptype = htons(0x0800);
arp->hard_addr_len = ETH_ADDR_SIZE;
arp->proto_addr_len = IP_ADDR_SIZE;
/* reply or request */
arp->opcode = opcode == REQUEST ? htons(ARPOP_REQUEST) : htons(ARPOP_REPLY);
return bytes;
} /* ----- end of function arp_empty ----- */
void copy_mac(MAC * m1,const MAC * m2) {
memcpy(m1,m2,ETH_ADDR_SIZE);
}
void copy_ip(IP * i1,const IP * i2) {
memcpy(i1,i2,IP_ADDR_SIZE);
}
void copy_host(struct Host * h1,const struct Host * h2) {
copy_mac(&h1->mac,&h2->mac);
copy_ip(&h1->ip,&h2->ip);
}
Problem:
The created packet is not quite right. Everything is fine up to the hard_addr_send. After this field, there is 2 bytes 0x00,0x00, (seen in GDB) and then the IP address. But due to this offset, it's impossible to correctly parse this packet. For example,in wireshark, instead of getting "10.0.0.1", I've got "0.0.10.0" for IP.
Here is the transcript of GDB :
/** 14 to pass ethernet frame & 4 + 2 + 2 to go to the addresses section*/
(gdb) x/6xb request+14+4+2+2
/** My MAC address , field hard_addr_send. it's GOOD. */
0x606b16: 0x34 0x67 0x20 0x01 0x9a 0x67
(gdb) x/6xb request+14+4+2+2+6
/** 6bytes later, supposedly my IP address.
* It should be 10.0.0.7 but you can see the 0x0a shifted by 2 bytes */
0x606b1c: 0x00 0x00 0x0a 0x00 0x00 0x07
In the method "arp_resolv_mac", everything info is right, i.e. struct Host contains the good information etc; I've checked everything.
I just don't get this offset by 2 bytes ... In a older versions, not using all theses new structs (only char *), I've already succeed at creating a right ARP packet, so I'm kind of wondering if this is not due to the struct, but my knowledge of C does not extend to the memory alignement subject ...!
Thank you.
The problem is that your structs are not packed. One solution would be to use packed structs, i.e.
typedef struct __attribute__ ((__packed__)) pkt_arp {
u_short htype;/* hardware type => ethernet , etc */
u_short ptype; /*protocol type => ipv4 or ipv6 */
u_char hard_addr_len; /* usually 6 bytes for ethernet */
u_char proto_addr_len; /*usually 8 bytes for ipv4 */
u_short opcode; /* type of arp */
MAC hard_addr_send;
IP proto_addr_send;
MAC hard_addr_dest;
IP proto_addr_dest;
} pkt_arp;
However, that is a gcc-specific extension other compilers may not support.
In my opinion, the best solution is accessing the elements of the byte array directly instead of using structs. Yes, it adds a few lines of code, but it's guaranteed to work for compilers that don't implement packed structs too.
i trying to parse a packet. till the ip header everything is fine(i'm able to retrieve all the values correctly). but for the udp header( checked if the protocol is 17) , the values are coming out to be wrong( all the 4 fields).
I'm trying to do this:
struct udp_header{
uint16_t sport;
uint16_t dport;
uint16_t len;
uint16_t chksum;
};
struct udp_header* udp= (struct udp_header*)(packet + 14 + ip_hdr->ip_hl*4);
Packet is the pointer pointing to the beginning of the packet. 14 is for ethernet header.The header length ip when checked is giving out the correct value. But after performing this operation i'm getting all the fields wrongly. when tried with uint8_t as data type( i know its wrong! ) the destintion port somehow is coming out correct.
You have run into endianness. IP packets have all fields in network byte order (aka "big-endian"), and your host system probably runs little-endian. Look into ntohs() and friends for one approach.
The proper approach is to not copy the structure as-is from the network data, but instead extract each field manually and byte-swap it if necessary. This also works around any issues with padding and alignment, there's no guarantee that your struct is mapped into your computer's memory in exactly the same way as the packet is serialized.
So you would do e.g.:
udp_header.sport = ntohs(*(unsigned short*) (packet + 14 + 4 * ip_hdr->ip_hl));
This is also a bit iffy, since it assumes the resulting address can validly be cast into a pointer to unsigned short. On x86 that will work, but it's not epic.
Even better, in my opinion, is to drop the use of pointers and instead write a function called e.g. unsigned short read_u16(void *packet, size_t offset) that extracts the value byte-by-byte and returns it. Then you'd just do:
udp_header.sport = read_u16(packet, 14 + 4 * ip_hdr->ip_hl);
I always use this struct for IP header:
struct sniff_ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */
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 */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
};
#define IP_HL(ip) (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip) (((ip)->ip_vhl) >> 4)
And to get the UDP struct pointer:
udp = (struct sniff_udp*)(packet + SIZE_ETHERNET + (IP_HL(ip)*4));
As another answer remarked, you have to deal with endianness of your data.
The other thing you need to deal with is byte alignment. For speed, when you define a structure in C like this:
struct udp_header{
uint16_t sport;
uint16_t dport;
uint16_t len;
uint16_t chksum;
};
The C compiler may leave padding bytes between these fields so that member accesses can be done with faster single-instruction memory access assembly instructions. You can check if your c compiler is doing this by printf("struct size is: %u\n", sizeof(struct udp_header));
Assuming you are using GCC, you must disable padding bytes by adding #pragma pack(1) before the structure definition. To re-enable padding for speed you should then use #pragma pack().