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. */
};
Related
On my system in /usr/include/netinet/iphdr this is the struct iphdr looks like
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. */
};
Assume I have crafted all the ip header fields with values. Now I want to calculate ip header length which is iphdr->ihl field on my system. I am assuming this is ip header length, so the length of my made ip header can't be sizeof(struct iphdr) then what that might be.
Also the struct tcphdr looks like this
struct tcphdr
{
__extension__ union
{
struct
{
uint16_t th_sport; /* source port */
uint16_t th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
# if __BYTE_ORDER == __LITTLE_ENDIAN
uint8_t th_x2:4; /* (unused) */
uint8_t th_off:4; /* data offset */
# endif
# if __BYTE_ORDER == __BIG_ENDIAN
uint8_t th_off:4; /* data offset */
uint8_t th_x2:4; /* (unused) */
# endif
uint8_t th_flags;
# define TH_FIN 0x01
# define TH_SYN 0x02
# define TH_RST 0x04
# define TH_PUSH 0x08
# define TH_ACK 0x10
# define TH_URG 0x20
uint16_t th_win; /* window */
uint16_t th_sum; /* checksum */
uint16_t th_urp; /* urgent pointer */
};
struct
{
uint16_t source;
uint16_t dest;
uint32_t seq;
uint32_t ack_seq;
# if __BYTE_ORDER == __LITTLE_ENDIAN
uint16_t res1:4;
uint16_t doff:4;
uint16_t fin:1;
uint16_t syn:1;
uint16_t rst:1;
uint16_t psh:1;
uint16_t ack:1;
uint16_t urg:1;
uint16_t res2:2;
# elif __BYTE_ORDER == __BIG_ENDIAN
uint16_t doff:4;
uint16_t res1:4;
uint16_t res2:2;
uint16_t urg:1;
uint16_t ack:1;
uint16_t psh:1;
uint16_t rst:1;
uint16_t syn:1;
uint16_t fin:1;
# else
# error "Adjust your <bits/endian.h> defines"
# endif
uint16_t window;
uint16_t check;
uint16_t urg_ptr;
};
};
};
so assume again that I have populated all the field of tcphdr with values now I want doff(data offset). how to calculate it
struct iphdr *iph=buffer;
// populate the fields of iph
//but what is iph->ihl? to calculate how?
int iphdrlen = iph->ihl*4;
struct tcphdr *tcph=(struct tcphdr *)(buffer + iphdrlen);
//populate all the fields of tcph
//but also how to calculate tcph->doff
should it be like for iphdr->ihl
//after populating all the fields of iph then just do following to get iph->ihl
iph->ihl=sizeof(iph);
and should it be like for tcph->doff
//after populating all the fields of tcph just do following
tcph->doff = (unsigned int)(sizeof(iph))+sizeof(tcph)));
and that will work, so will it?
IP header has two "parts" - a mandatory fixed sized part, which is represented by struct iphdr and the latter is represented by options that optionally can be present (see the comment /*The options start here. */). The header length is the whole header length with options. So, unless you have actually added some options it is only the length of the header, which you can either calculate or read in the standard:
IHL: 4 bits
Internet Header Length is the length of the internet header in 32
bit words, and thus points to the beginning of the data. Note that
the minimum value for a correct header is 5.
Now, one byte is 4 bits, and 32 bits are 4 words. Thus it is either sizeof(struct iphdr)/4 or you can set it to 5.
TCP data offset is basically the same, but for TCP. Since TCP header can also contain options, the same logic applies. According to the standard (scroll to next page):
Data Offset: 4 bits
The number of 32 bit words in the TCP Header. This indicates where
the data begins. The TCP header (even one including options) is an
integral number of 32 bits long.
So, basically it is calculated the same way. It is not a pointer, it is a number which indicates length of the header. According to wikipedia if your header has no options, the value of this field is also 5.
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));
Hi there for fun i'm developing a tiny dns client on a unix system.
I've read the documentation about dns protocol i wrote a tiny function
int makeQuestion(char* dns_addr,char *name){
int s = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
register int len_name = strlen(name);
if(s<0)
return errno;
struct sockaddr_in address;
bzero(&address,sizeof(address));
address.sin_port = htons(53);
address.sin_addr.s_addr = inet_addr(dns_addr);
dns_header header;
memset(&header,0,sizeof(dns_header));
header.id = htons(getpid());
header.q_count = htons(1);
dns_question quest = {
.qclass = htons(IN),
.qtype = htons(A)
};
register int pack_size = sizeof(dns_header)+len_name+2+sizeof(dns_question);
char *packet = malloc(pack_size);
memcpy(packet,&header,sizeof(dns_header));
for(int i = 0;i<len_name;i++)
*(packet +i +sizeof(dns_header)) = name[i];
packet[len_name+sizeof(dns_header)] = '.';
packet[len_name+sizeof(dns_header)+1] = '\0';
memcpy(packet+sizeof(dns_header)+len_name+2,&quest,sizeof(dns_question));
sendto(s,packet,pack_size,NULL,&address,sizeof(address));
return OK;
}
The structure for the dns header and dns query are declared like:
//DNS header structures
typedef struct dns_header
{
uint16_t id; // identification number
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
uint16_t q_count; // number of question entries
uint16_t ans_count; // number of answer entries
uint16_t auth_count; // number of authority entries
uint16_t add_count; // number of resource entries
}dns_header;
typedef struct dns_question
{
uint16_t qtype;
uint16_t qclass;
}dns_question;
Now i executed the code while wireshark was running and i saw the packet that seemed to be correct but in the query section wireshark said
Name: <Unknown extended label>
So the question is there is a way i have to use to store the dns name of the queried host in the packet or there is something wrong in the implementation. Sorry for the loosing of time and sorry for my English. Thanks indeed
I solved finally. Studing better the protocoll ( the domain name system) where the reference is at this link a the wrong part was in the section called qname ( the name of the host that in my case the protocoll wasn't able to determinate the size)
So as the document said qname is:
a domain name represented as a sequence of labels, where
each label consists of a length octet followed by that
number of octets. The domain name terminates with the
zero length octet for the null label of the root. Note
that this field may be an odd number of octets; no
padding is used.
So i changed my code to transform www.example.com in 3www7example3com
and everything works
I am reading packets in pcap format, and have determined that some have the protocol ICMP. I think that if that's the case, the ICMP header immediately follows the IP header. However, I get the wrong ICMP type (echo reply, request) for each of my results. I.e, here is the correct output for one such packet which has an echo reply:
Packet number: 2 Packet Len: 74
Ethernet Header
Dest MAC: 0:2:2d:90:75:89
Source MAC: 0:6:25:78:c4:7d
Type: IP
IP Header
TOS: 0xff
TTL: 52
Protocol: ICMP
Checksum: Incorrect (0x5565)
Sender IP: 66.94.230.35
Dest IP: 192.168.1.102
ICMP Header
Type: Reply
I know that echo reply has a code of 8, but instead of that I get a 0, indicating "request", and this happens for all packets when I try to get the type. I feel that I may be pointing my ICMP header to the wrong location following the IP header. My IP information though is correct. Here is how I am adjusting the pointers to the IP and ICMP headers:
EtherHeader *eth = (EtherHeader *)packet;
IPHeader *iph;
TCPHeader *tcp;
ICMPHeader *icm;
---
ipLen = ntohs(iph->totLen * 4);
if(iph->protocol == ICMP) {
icm = (ICMPHeader *)(packet + ETHER_SIZE + (ntohs(iph->totLen)));
printf("%d\n", icm->type);;
}
Is there anything wrong with how I am setting my pointer to the ICMP header?
Here are my headers for IP and ICMP:
typedef struct __attribute__((__packed__)) IPHeader {
#if __BYTE_ORDER__ == __LITTLE_ENDIAN__
uint8_t hdrLen:4;
uint8_t version:4;
#else
uint8_t version:4;
uint8_t hdrLen:4;
#endif
uint8_t TOS;
uint16_t totLen;
uint16_t id;
uint16_t offset;
#define DF 0x4
#define MF 0x2
#define OFF 0
uint8_t TTL;
uint8_t protocol;
uint16_t checksum;
struct in_addr srcIP;
struct in_addr destIP;
}IPHeader;
typedef struct __attribute__((__packed__)) ICMPHeader {
uint8_t type;
}ICMPHeader;
Your understanding of the control message type numbers is backwards:
I know that echo reply has a code of 8, but instead of that I get a 0, indicating "request",
The correct values are:
0 Echo Reply
....
8 Echo Request
Internet Control Message Protocol - Control Messages
Also, although this doesn't appear to be used, you have an order of operations problem here - you can't do anything with the value until you change its byte order:
ntohs(iph->totLen * 4);
needs to be
ntohs(iph->totLen) * 4;
I know that echo reply has a code of 8, but instead of that I get a 0,
No it doesn't. ICMP echo reply has a type of 0. type 8 is Echo (request).
See e.g. this
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().