Memory alignment issue in received network packets - c

I'm writing a custom protocol in the linux kernel. I'm using the following structures
struct syn {
__be32 id;
__be64 cookie;
};
struct ack {
__be32 id; // Right now, setting it to 14 (Just a random choice)
__be32 sequence;
};
struct hdr {
............
__be32 type; //last element
};
When I send and receive packets, I map the structures syn and ack (for different packets) to the address of hdr->type.
This should ideally mean that the id (in syn and ack structures) should be mapped to the hdr->type and whatever follows the struct hdr should be mapped to either syn->cookie or ack->sequence, depending on which struct I'm mapping on to the hdr->type.
But on printing out the memory addresses for these variables, I get the following
//For struct syn
hdr->type at ffff880059f55444
syn->id at ffff880059f55444
syn->cookie at ffff880059f5544c //See the last two bits
//For struct ack_frame
hdr->type at ffff880059f55044
ack->id at ffff880059f55044
ack->sequence at ffff880059f55048 //See the last two bits
So why do syn->cookie and ack->sequence start at different offsets relative to hdr->type when ack->id and syn->id have the same size?
EDIT 1: I map these structures using
char *ptr = (char *)&hdr->type;
//For syn
struct syn *syn1 = (struct syn *)ptr
//For ack
struct ack *ack1 = (struct ack *)ptr

since you work in 64 bits the compiler fills struct the following:
struct syn {
uint32_t id
uint32_t hole -- the compiler must add here cause it mist align
uint64_t seq
}
I guess the data doesn't have holes, so to fix it you will need to set seq to uint32_t and cast it later.

https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#Common-Type-Attributes
Look at packed. For whatever reason, GCC doesn't let me link directly to that section.

You should define your structure as following. Attribute packed means allignment should be as 1 byte.
Following structure will be 12 bytes longs. If you dont use "attribyte" key word, your structure would be 16 bytes long.
struct yours{
__be32 id;
__be64 cookie;
}__attribute__((__packed__));

Related

How to display or do eDNS in dpdk packets?

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));

How can I capture dns packets in c?

I'm writing a packet sniffer program in c. Now it can only find HTTP packets but I want to make it in a way to get also DNS packets. I know DNS packets are UDP but I don't know how to identify DNS ones. Is there a specific thing in DNS packets to check to find them? I know port 53 is default port for DNS requests, but is it a reliable way to find them?
There is no good way to tell if a UDP packet contains DNS data: There is nothing in the UDP header, or IP header that directly tells you the data is DNS. However what you could do is first see if the source port in the UDP header is port 53 (DNS's standard UDP port) and second see if the data fits the data structure you're using to decode the header (most likely a struct). This is a very good question.
To fit the packet to a strcut you can use the following code:
This is the actual structure of a DNS header packet laid out in a struct in c:
#pragma pack(push, 1)
typedef struct
{
uint16_t id; // identification number 2b
uint8_t rd : 1; // recursion desired
uint8_t tc : 1; // truncated message
uint8_t aa : 1; // authoritive answer
uint8_t opcode : 4; // purpose of message
uint8_t qr : 1; // query/response flag
uint8_t rcode : 4; // response code
uint8_t cd : 1; // checking disabled
uint8_t ad : 1; // authenticated data
uint8_t z : 1; // its z! reserved
uint8_t ra : 1; // recursion available 4b
uint16_t q_count; // number of question entries 6b
uint16_t ans_count; // number of answer entries 8b
uint16_t auth_count; // number of authority entries 10b
uint16_t add_count; // number of resource entries 12b
}Dns_Header, *Dns_Header_P;
#pragma pack(pop)
To test this you can do this:
Dns_Header_P header = (Dns_Header_P)capture;
capture being a byte array with you DNS header.
Depending on how you're capturing the packets and how you're storing them you might need to change the endianness of the struct. If you test this with your program and it doesn't seem to have the right data or the data is switched around let me know.

Layout of an ip header and network programming

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. */
};

Build vlan header in c

I need to build a vlan header.
I have code that build eh header (struct ether_header) and it work ok.
/* Ethernet header */
memcpy(eh->ether_shost,src_mac_.data(), 6);
memcpy(eh->ether_dhost,socketAddress.sll_addr , 6);
/* Ethertype field */
eh->ether_type = htons(ETH_P_IP);
I didnt find struct for vlan_eth_header , so i create my own and populate it like this:
struct vlan_ethhdr {
u_int8_t ether_dhost[ETH_ALEN]; /* destination eth addr */
u_int8_t ether_shost[ETH_ALEN]; /* source ether addr */
u_int16_t h_vlan_proto;
u_int16_t h_vlan_TCI;
u_int16_t ether_type;
};
/* Ethernet header */
memcpy(eh->ether_shost,src_mac_.data(), 6);
memcpy(eh->ether_dhost,socketAddress.sll_addr , 6);
eh->h_vlan_proto = htons(0x8100);
eh->h_vlan_TCI = htons(VLAN_ID);
/* Ethertype field */
eh->ether_type = htons(ETH_P_IP);
It seems that i did it wrong.
It seems that Wireshak even didnt recognize the packet (the old code sent tcp packet and send them correct).
Any advice?
http://wiki.wireshark.org/VLAN
DstMAC [6 bytes]
SrcMAC [6 bytes]
Type [2 bytes]
0x8100
VLANTag [4 bytes]
Priority [0..2 bit]
0
CFI [3 bit]
0
ID [4..15 bit]
Ethernet Type [16..31]
0x0800 (IP)
My guess is you're not setting the VLAN_ID correctly.
At first you should avoid any structure padding issues by just creating some test packets in a byte[] buffer that way you can be sure you have everything correct. Then you can debug your structure and htons byte ordering with confidence because you know what the correct values should be.
My code to construct VLAN is correct. It dosnt worked for me at the first place , because i forgot to change the size of the packet to be bigger now.
Pay attention that TCI is not only VID its included priority and CFI. In my case they both zero , so i dont need to use mask and padding for TCI.
Are you sure your structure is properly packed? The compiler adds some padding by default. It can be easily disabled.
For example, with GCC:
#pragma pack(push)
#pragma pack(0)
struct vlan_ethhdr {
u_int8_t ether_dhost[ETH_ALEN]; /* destination eth addr */
u_int8_t ether_shost[ETH_ALEN]; /* source ether addr */
u_int16_t h_vlan_proto;
u_int16_t h_vlan_TCI;
u_int16_t ether_type;
};
#pragma pack(pop)
802.1Q 4-byte VLAN tag is generally considered as in the form of 0x8100+pri+cfi+vlan.
The wireshark way of depicting vlan tag as pri+cfi+vlan+etype (excluding 8100, but including etype of the data payload) is somewhat unique.
However, the overall packet format and length is correct either way.

Error in packet parsing

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().

Resources