The code included below is a stripped-down implementation of a function that generates a UDP packet for a given fixed-size payload and sends it.
After switching to a newer version of gcc this code suddenly shows an error: the UDP checksum is not calculated correctly, and this can be traced down to the line
pseudoHeader->protocol = IPPROTO_UDP;
for which the compiler seemingly does not generate an instruction if at least -O2 optimization is used.
The following workarounds resolve the issue (each suggestion works independently, i.e. you do not to have to apply all of them at once!):
move the mentioned line before the two calls to inet_pton
remove the call to memset(ipHeader, 0, sizeof(struct ip)) after the checksum calculation
make ip_checksum() an external function outside of this translation unit
The fact that the code makes heavy use of casting together with the error only appearing for -O2 or higher and the nature of the workarounds virtually calls for this to be an aliasing error in the code. Is there an actual error and if so, how can it be fixed?
#include <string.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netpacket/packet.h>
#define UDP_PORT 2345
#define REPLY_PAYLOAD_SIZE 360
typedef struct UDPPseudoHeader
{
unsigned long int source_ip;
unsigned long int dest_ip;
unsigned char reserved;
unsigned char protocol;
unsigned short int udp_length;
} UDPPseudoHeader;
void sendPacket(unsigned char* packet, int len);
static unsigned short ip_checksum(unsigned short *ptr, int len)
{
int sum = 0;
unsigned short answer = 0;
unsigned short *w = ptr;
int nleft = len;
while(nleft > 1) {
sum += *w++;
nleft -= 2;
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
answer = ~sum;
return(answer);
}
void sendBroadcastPacket(uint16_t destPort, char* packet) {
unsigned char buffer[REPLY_PAYLOAD_SIZE + sizeof(struct ip) + sizeof(struct udphdr)];
int bufferLen = REPLY_PAYLOAD_SIZE + sizeof(struct ip) + sizeof(struct udphdr);
/* initialize header pointers */
struct udphdr* udpHeader = (struct udphdr*)(buffer + sizeof(struct ip));
UDPPseudoHeader* pseudoHeader = (UDPPseudoHeader*)(buffer + sizeof(struct ip) - sizeof(UDPPseudoHeader));
struct ip* ipHeader = (struct ip*)(buffer);
memset(buffer, 0, bufferLen);
/* copy user data */
memcpy(buffer + sizeof(struct ip) + sizeof(struct udphdr), packet, REPLY_PAYLOAD_SIZE);
/* fill in UDP header */
udpHeader->source = htons(UDP_PORT);
udpHeader->dest = htons(destPort);
udpHeader->len = htons(sizeof(struct udphdr) + REPLY_PAYLOAD_SIZE);
udpHeader->check = 0;
/* create UDP pseudo header for checksum calculation */
inet_pton(AF_INET, "0.0.0.0", &pseudoHeader->source_ip);
inet_pton(AF_INET, "255.255.255.255", &pseudoHeader->dest_ip);
pseudoHeader->reserved = 0;
pseudoHeader->protocol = IPPROTO_UDP;
pseudoHeader->udp_length = htons(sizeof(struct udphdr) + REPLY_PAYLOAD_SIZE);
/* calculate UDP checksum */
udpHeader->check = ip_checksum((unsigned short*) pseudoHeader, bufferLen - sizeof(struct ip) + sizeof(UDPPseudoHeader));
/* fill in IP header */
memset(ipHeader, 0, sizeof(struct ip));
ipHeader->ip_v = 4;
ipHeader->ip_hl = 5;
ipHeader->ip_tos = IPTOS_LOWDELAY;
ipHeader->ip_len = htons(bufferLen);
ipHeader->ip_off = htons(IP_DF);
ipHeader->ip_id = 0;
ipHeader->ip_ttl = 16;
ipHeader->ip_p = IPPROTO_UDP;
inet_pton(AF_INET, "0.0.0.0", &ipHeader->ip_src);
inet_pton(AF_INET, "255.255.255.255", &ipHeader->ip_dst);
ipHeader->ip_sum = 0;
/* calculate IP checksum */
ipHeader->ip_sum = ip_checksum((unsigned short*) ipHeader, ipHeader->ip_hl * 4);
sendPacket(buffer, bufferLen);
}
The code indeed violates the strict aliasing rule. The compiler assumes that the call to ip_checksum() does not depend on the assignments to the struct members reserved and protocol because these modify chars and ip_checksum() is calculated over an array of unsigned shorts. Therefore the assignments are completely optimized away since the following call to memset() overwrites the memory anyway.
A possible solution is to declare the pseudo header as
typedef union {
struct {
unsigned long int source_ip;
unsigned long int dest_ip;
unsigned char reserved;
unsigned char protocol;
unsigned short int udp_length;
} hdr;
unsigned short as_short[6];
} UDPPseudoHeader;
and replace the generation of the pseudo header and the checksum calculation by
/* create UDP pseudo header for checksum calculation */
inet_pton(AF_INET, "0.0.0.0", &pseudoHeader->hdr.source_ip);
inet_pton(AF_INET, "255.255.255.255", &pseudoHeader->hdr.dest_ip);
pseudoHeader->hdr.reserved = 0;
pseudoHeader->hdr.protocol = IPPROTO_UDP;
pseudoHeader->hdr.udp_length = htons(sizeof(struct udphdr) + REPLY_PAYLOAD_SIZE);
/* calculate UDP checksum */
udpHeader->check = ip_checksum(pseudoHeader->as_short, bufferLen - sizeof(struct ip) + sizeof(UDPPseudoHeader));
Another issue:
Alignment
unsigned char buffer[REPLY_PAYLOAD_SIZE + sizeof(struct ip) + sizeof(struct udphdr)];
...
struct ip* ipHeader = (struct ip*)(buffer);
buffer is not certainly aligned for a struct ip.
Related
I am writing a program which actually send raw Packets and Sniffing the Network. I have the Program to build, to send the Packets and to Sniff the Network. Each of them work (mostly) great. If i try to implement a main function which calls first the Function to build and send the Packet and after that the function which sniff, I get a free(): Invalid next size (normal) | Cancelled (memory dump written) Error.
I tried to find the Problem by my own. I called the startSniffer() - Function in different places. Actually in send_tcp_packet befor building the tcp Packet (build_tcp_packet), and after that too. If I call startSniffer() Function befor build_tcp_packet it works. So I call the startSniffer() - Function in build_tcp_packet befor memcpy(...) and it works. So I assume that the error is because of memcpy(...). I removed memcpy(...) and the Programm works well. But actually the Programm needs memcpy(...) to set the Payload in TCP-Header.
So did you have any Idea and helps for my Code???
void startBuilding() {
unsigned char *data;
unsigned int packet_size;
unsigned int data_size;
src_addr.sin_family = AF_INET;
inet_aton(srcHost, &src_addr.sin_addr);
dst_addr.sin_family = AF_INET;
inet_aton(destHost, &dst_addr.sin_addr);
data = (char*) malloc(strlen(sendingData) + 1);
strcpy((char *) data, sendingData);
data_size = strlen(sendingData);
switch (ipProto) {
case IPPROTO_TCP:
printf("[+] Send TCP packet...\n");
src_addr.sin_port = htons(tcpSourcePort);
dst_addr.sin_port = htons(tcpDestPort);
if (open_RawSocket(ipProto)) {
send_tcp_packet(raw_socket, src_addr, dst_addr, data, data_size);
}
}
unsigned int build_tcp_packet(struct sockaddr_in src_addr,
struct sockaddr_in dst_addr, unsigned char *tcp_packet,
unsigned char *data, unsigned int data_size) {
printf("[+] Build TCP packet...\n\n");
unsigned int length;
struct tcpheader *tcp;
length = TCPHDRSIZE + data_size;
tcp = (struct tcpheader *) tcp_packet;
tcp->th_sport = src_addr.sin_port;
tcp->th_dport = dst_addr.sin_port;
tcp->th_seq = htonl(tcpSeqNum);
tcp->th_acknum = tcpAcknum;
tcp->th_reserved = tcpReserved;
tcp->th_off = tcpOffSet;
tcp->th_fin = tcpFinFlag;
tcp->th_syn = tcpSynFlag;
tcp->th_rst = tcpRstFlag;
tcp->th_psh = tcpPshFlag;
tcp->th_ack = tcpAckFlag;
tcp->th_urg = tcpUrgFlag;
tcp->th_cwr = tcpCwrFlag;
tcp->th_ece = tcpEceFlag;
tcp->th_win = htons(tcpWin);
tcp->th_urp = tcpUrp;
tcp->th_sum = tcpSum;
// startSniffer(); // If i start startSniffer here, it works fine.
memcpy(tcp_packet + TCPHDRSIZE, data, data_size);
// startSniffer(); //If i start sartSniffer here, it did not work, and i get the error.
return length;
}
unsigned int build_ip_packet(struct in_addr src_addr, struct in_addr dst_addr,
uint8_t protocol, unsigned char *ip_packet, unsigned char *data,
unsigned int data_size) {
printf("[+] Build IP packet...\n\n");
struct ipheader *ip;
ip = (struct ipheader*) ip_packet;
ip->ip_v = ipv;
ip->ip_hl = iphl;
ip->ip_tos = iptype;
ip->ip_id = htons(ipId);
if (calculateIpLenInBuildPacket) {
ip->ip_len = htons(iphl * 4 + data_size);
} else {
ip->ip_len = htons(ipLen);
}
ip->ip_off = ipOffset;
ip->ip_moreFrag = ipMoreFragmentFlag;
ip->ip_doNotFrag = ipDoNotFragmentFlag;
ip->ip_reserved = ipReserved;
ip->ip_frag_offset1 = ipFragOffset;
ip->ip_ttl = ipTTL;
ip->ip_p = ipProto;
ip->ip_sum = ipSum;
ip->iph_sourceip = src_addr.s_addr;
ip->iph_destip = dst_addr.s_addr;
return sizeof(struct ipheader) + data_size;
}
int main(int argc, char **argv) {
startBuilding();
startSniffer();
}
void send_tcp_packet(int raw_sock, struct sockaddr_in src_addr,
struct sockaddr_in dst_addr, uint8_t *data, unsigned int data_size) {
unsigned int packet_size;
unsigned int ip_payload_size;
unsigned char *packet;
packet = (char*) malloc(strlen(data) + 1);
memset(packet, 0, ETH_DATA_LEN);
ip_payload_size = build_tcp_packet(src_addr, dst_addr,
packet + sizeof(struct ipheader), data, data_size);
packet_size = build_ip_packet(src_addr.sin_addr, dst_addr.sin_addr,
IPPROTO_TCP, packet, packet + sizeof(struct ipheader), ip_payload_size);
setOption_RawSocket(raw_sock);
if (sendto(raw_sock, packet, packet_size, 0, (struct sockaddr *) &dst_addr,
sizeof(dst_addr)) < 0) {
perror("sendto");
exit(1);
} else {
printf("Send! \n\n");
}
close(raw_sock);
}
This is dangerous:
packet = (char*) malloc(strlen(data) + 1);
memset(packet, 0, ETH_DATA_LEN);
It will corrupt the heap whenever ETH_DATA_LEN is larger than strlen(data) + 1, the heap corruption will not be immediately detected but may cause subsequent allocations or deallocations (free) to fail.
Suggest:
packet = calloc( ETH_DATA_LEN, 1 ) ;
You made a rash assumption that data is a null terminated string and should have used data_size to determine the length of data in any event, but since packet size is necessarily larger then just the data payload, both are incorrect.
You fail to free packet in this case, so also have a memory leak. Since ETH_DATA_LEN is a constant, there is no need to dynamically allocate in any event. Instead you might have:
char* packet[ETH_DATA_LEN] = {0} ;
To be precise I have created this simple TCP program to send a simple TCP packet over network to a destination machine, but somehow Don't really know, what am I doing wrong but it don't appear to be sending any packets to the destination host. also I can't find it in my wireshark.
rawtcp.c:
//---cat rawtcp.c---
// Run as root or SUID 0, just datagram no data/payload
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
// Packet length
#define PCKT_LEN 8192
#pragma pack(push, 1)
// May create separate header file (.h) for all
// headers' structures
// IP header's structure
struct ipheader {
unsigned char iph_ihl:4, /* Little-endian */
iph_ver:4;
unsigned char iph_tos;
unsigned short int iph_len;
unsigned short int iph_ident;
unsigned short int iph_offset;
unsigned char iph_ttl;
unsigned char iph_protocol;
unsigned short int iph_chksum;
unsigned int iph_sourceip;
unsigned int iph_destip;
};
/* Structure of a TCP header */
struct tcpheader {
unsigned short int tcph_srcport;
unsigned short int tcph_destport;
unsigned int tcph_seqnum;
unsigned int tcph_acknum;
unsigned char tcph_reserved:4, tcph_offset:4;
// unsigned char tcph_flags;
unsigned int
tcp_res1:4, /*little-endian*/
tcph_hlen:4, /*length of tcp header in 32-bit words*/
tcph_fin:1, /*Finish flag "fin"*/
tcph_syn:1, /*Synchronize sequence numbers to start a connection*/
tcph_rst:1, /*Reset flag */
tcph_psh:1, /*Push, sends data to the application*/
tcph_ack:1, /*acknowledge*/
tcph_urg:1, /*urgent pointer*/
tcph_res2:2;
unsigned short int tcph_win;
unsigned short int tcph_chksum;
unsigned short int tcph_urgptr;
};
#pragma pack(pop)
// Simple checksum function, may use others such as Cyclic Redundancy Check, CRC
unsigned short csum(unsigned short *buf, int len)
{
unsigned long sum;
for(sum=0; len>0; len--)
sum += *buf++;
sum = (sum >> 16) + (sum &0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}
int main(int argc, char *argv[])
{
int sd;
// No data, just datagram
char buffer[PCKT_LEN];
// The size of the headers
struct ipheader *ip = (struct ipheader *) buffer;
struct tcpheader *tcp = (struct tcpheader *) (buffer + sizeof(struct ipheader));
struct sockaddr_in sin, din;
int one = 1;
const int *val = &one;
memset(buffer, 0, PCKT_LEN);
if(argc != 5)
{
printf("- Invalid parameters!!!\n");
printf("- Usage: %s <source hostname/IP> <source port> <target hostname/IP> <target port>\n", argv[0]);
exit(-1);
}
sd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
if(sd < 0)
{
perror("socket() error");
exit(-1);
} else
printf("socket()-SOCK_RAW and tcp protocol is OK.\n");
// The source is redundant, may be used later if needed
// Address family
sin.sin_family = AF_INET;
din.sin_family = AF_INET;
// Source port, can be any, modify as needed
sin.sin_port = htons(atoi(argv[2]));
din.sin_port = htons(atoi(argv[4]));
// Source IP, can be any, modify as needed
sin.sin_addr.s_addr = inet_addr(argv[1]);
din.sin_addr.s_addr = inet_addr(argv[3]);
// IP structure
ip->iph_ihl = 5;
ip->iph_ver = 4;
ip->iph_tos = 16;
ip->iph_len = htons(sizeof(struct ipheader) + sizeof(struct tcpheader));
ip->iph_ident = htons(54321);
ip->iph_offset = 0;
ip->iph_ttl = 64;
ip->iph_protocol = 6; // TCP
ip->iph_chksum = 0; // Done by kernel
// Source IP, modify as needed, spoofed, we accept through command line argument
ip->iph_sourceip = inet_addr(argv[1]);
// Destination IP, modify as needed, but here we accept through command line argument
ip->iph_destip = inet_addr(argv[3]);
// The TCP structure. The source port, spoofed, we accept through the command line
tcp->tcph_srcport = htons(atoi(argv[2]));
// The destination port, we accept through command line
tcp->tcph_destport = htons(atoi(argv[4]));
tcp->tcph_seqnum = htonl(1);
tcp->tcph_acknum = 0;
tcp->tcph_offset = 5;
tcp->tcph_syn = 1;
tcp->tcph_ack = 0;
tcp->tcph_win = htons(32767);
tcp->tcph_chksum = 0; // Done by kernel
tcp->tcph_urgptr = 0;
// IP checksum calculation
ip->iph_chksum = htons(csum((unsigned short *) buffer, (sizeof(struct ipheader) + sizeof(struct tcpheader))));
// Inform the kernel do not fill up the headers' structure, we fabricated our own
if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0)
{
perror("setsockopt() error");
exit(-1);
} else
printf("setsockopt() is OK\n");
printf("Using:::::Destination IP: %s port: %u, Source IP: %s port: %u.\n", argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));
// sendto() loop, send every 2 second for 50 counts
unsigned int count;
for(count = 0; count < 20; count++)
{
if(sendto(sd, buffer, ip->iph_len, 0, (struct sockaddr *)&din, sizeof(din)) < 0)
// Verify
{
perror("sendto() error");
exit(-1);
} else
printf("Count #%u - sendto() is OK\n", count);
sleep(2);
}
close(sd);
return 0;
}
while compiling it, it compiles just fine. also when I run the program as it suppose to be as ./rawtcp 192.168.1.152 1000 192.168.1.151 1000 it says:
socket()-SOCK_RAW and tcp protocol is OK.
setsockopt() is OK
Using:::::Destination IP: 192.168.1.151 port: 1000, Source IP: 192.168.1.152 port: 1000.
Count #0 - sendto() is OK
Count #1 - sendto() is OK
but while filtering wireshark as tcp.port == 1000 it never show up (Note: source ip is 192.168.1.152 and destination ip is 192.168.1.151). but If I do a simple hping3 -p 1000 192.168.1.151 it works just fine and I can see it in wireshark too.
I would really appreciate if you could tell me what am I doing wrong in my code :)
First of all, you are missing the <arpa/inet.h> header, which provides the inet_addr function you are using. Your program should still compile under C89 without this due to implicit function declaration but it is bad practice to rely on that and it could lead to subtle bugs.
One issue is your misuse of #pragma pack.
Before you declare your structs, you should do #pragma pack(push, 1), which both sets struct packing alignment to 1, and pushes the previous state of packing alignment to a stack. After you are finished declaring your structs, you can then do #pragma pack(pop) to reset struct packing back to normal in any following declarations. In the case of your program, omitting #pragma pack(pop) after your declarations should still work, but it is good practice to reset this, in case you ever do declare any other structs which you don't want to be tightly-packed, or you include any other header files afterwards.
#pragma pack(push, 1)
struct a {
/* ... */
};
struct b {
/* ... */
};
#pragma pack(pop)
The other problem with your struct ipheader is that you have both an 8-bit iph_flags and a 16-bit iph_offset, while the flags field in an IP header should be 3 bits and the offset field should be 13 bits (both of these together adding up to 16 bits). This means you have an extra 8 bits in your header which shouldn't be there. Since you are filling both of these fields with zero anyway, you can fix this by removing iph_flags from your struct completely, and just keeping the one 16-bit iph_offset field, filled with zero, which actually spans the space of the 3-bit and the 13-bit field in the IP header.
With these fixes, your program works for me and a packet can be seen in Wireshark.
I think your port is already used.
You should change port for 30000 or higher, the 1000 ports are often take by other applications
You should also debug using telnet [ip] [port]
#include<stdlib.h>
#include<netinet/ip.h>
#include<netinet/tcp.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#define P 5000 /* lets flood the port */
unsigned short /* this function generates header checksums */
csum (unsigned short *buf, int nwords)
{
unsigned long sum;
for (sum = 0; nwords > 0; nwords--)
sum += *buf++;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return ~sum;
}
int
main (void)
{
char ipp[10] = "1.2.3.4";
int s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP); /* open raw socket */
char datagram[4096]; /* this buffer will contain ip header, tcp header,
and payload. we'll point an ip header structure
at its beginning, and a tcp header structure after
that to write the header values into it */
struct ip *iph = (struct ip *) datagram;
struct tcphdr *tcph = (struct tcphdr *) datagram + sizeof (struct ip);
struct sockaddr_in sin;
/* the sockaddr_in containing the dest. address is used
in sendto() to determine the datagrams path */
sin.sin_family = AF_INET;
sin.sin_port = htons (P);/* you byte-order >1byte header values to network
byte order (not needed on big endian machines) */
sin.sin_addr.s_addr = inet_addr ("192.168.1.12");
memset (datagram, 0, 4096); /* zero out the buffer */
/* we'll now fill in the ip/tcp header values, see above for explanations */
iph->ip_hl = 5;
iph->ip_v = 4;
iph->ip_tos = 0;
iph->ip_len = sizeof (struct ip) + sizeof (struct tcphdr); /* no payload */
iph->ip_id = htonl (54321); /* the value doesn't matter here */
iph->ip_off = 0;
iph->ip_ttl = 255;
iph->ip_p = 6;
iph->ip_sum = 0; /* set it to 0 before computing the actual checksum later */
iph->ip_src.s_addr = inet_addr (ipp);/* SYN's can be blindly spoofed */
iph->ip_dst.s_addr = sin.sin_addr.s_addr;
tcph->source = htons (1234); /* arbitrary port */
tcph->dest = htons (P);
tcph->seq = random ();/* in a SYN packet, the sequence is a random */
tcph->ack_seq = 0;/* number, and the ack sequence is 0 in the 1st packet */
tcph->doff = 0; /* first and only tcp segment */
tcph->syn = 1;
tcph->window = htonl (65535); /* maximum allowed window size */
tcph->check = 0;/* if you set a checksum to zero, your kernel's IP stack
should fill in the correct checksum during transmission */
tcph->urg_ptr = 0;
iph->ip_sum = csum ((unsigned short *) datagram, iph->ip_len >> 1);
/* finally, it is very advisable to do a IP_HDRINCL call, to make sure
that the kernel knows the header is included in the data, and doesn't
insert its own header into the packet before our data */
{ /* lets do it the ugly way.. */
int one = 1;
const int *val = &one;
if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
printf ("Warning: Cannot set HDRINCL!\n");
}
int i1=49;
char i2 = 49;
int cc = 0;
int r1;
while (cc<99)
{
ipp[6] = i1;
ipp[7] = i2;
if(i1 == 57)
{
break;
}
if(i2 == 57 )
{ i2 = 48;
i1++;
}
i2++;
cc++;
iph->ip_src.s_addr = inet_addr (ipp);
if (sendto (s, /* our socket */
datagram, /* the buffer containing headers and data */
iph->ip_len, /* total length of our datagram */
0, /* routing flags, normally always 0 */
(struct sockaddr *) &sin, /* socket addr, just like in */
sizeof (sin)) < 0) /* a normal send() */
printf ("error\n");
else
printf ("sent \n");
}
return 0;
}
the code creates a raw socket with proper ip address and the source addrewss is 192.168.1.12...port 5000 ... the problem is that when i try to capture the packets sent using wireshark, it says something like bogus tcp headee length...should be atleast 20 ... how can i correct this error ...
P.S. you can refer link for the code explanation if you find the code lengthy ...
After further reading: change this line
struct tcphdr *tcph = (struct tcphdr *) datagram + sizeof (struct ip);
to this
struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ip));
In the first case tcph would pointer far beyond iph.
char *datagramshould be unsigned char *too.
I want to know how to generate a packet in c. Supposed we have a type as follows:
struct ipheader {
int version;
int hdLen;
int tos;
int totLen;
int id;
......
int dstIp;
}
And we have a ipheader type:
struct ipheader ip;
//init the ip
.....
How can I generate a packet(just ip header part) from "ip". Could anyone show me how?
I want to know to generate packets with information such as mac address, ip header, tcp header, payload. Then I can use the "pcap_sendpacket" function to send the packets I have generated. Could someone give me a little example.
You can create custom ip packet as below. The below code snippet also makes the custom TCP part which is inside the ip packet. Also the custom function checksum generates the checksum for the packet. You can use rawsocket to send this packet directly to the network. I think the code is easy and self explanatory. Let me know if you have any queries.
#include <netinet/ip.h>
#include <netinet/tcp.h>
****************************************
ipv4 packet structure
****************************************
struct ipv4_packet {
struct iphdr iph;
struct tcphdr tcph;
void *data;
};
****************************************
snippet of the IPV4 packet making code
****************************************
struct iphdr ip_head;
struct tcphdr tcp_head;
struct sockaddr_in target;
char packet[2048];
int i;
struct tcp_pseudo /*the tcp pseudo header*/
{
__u32 src_addr;
__u32 dst_addr;
__u8 dummy;
__u8 proto;
__u16 length;
} pseudohead;
struct help_checksum /*struct for checksum calculation*/
{
struct tcp_pseudo pshd;
struct tcphdr tcphd;
char tcpdata[1024];
} tcp_chk_construct;
/*Prepare IP header*/
ip_head.ihl = 5; /*headerlength with no options*/
ip_head.version = 4;
ip_head.tos = 0;
ip_head.tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr) + len);
ip_head.id = htons(31337 + (rand() % 100));
ip_head.frag_off = 0;
ip_head.ttl = 255;
ip_head.protocol = IPPROTO_TCP;
ip_head.check = 0; /*Fill in later*/
ip_head.saddr = htonl(src);
ip_head.daddr = htonl(dst);
ip_head.check = in_cksum((unsigned short *) &ip_head, sizeof(struct iphdr));
/*Prepare TCP header*/
tcp_head.source = htons(src_p);
tcp_head.dest = htons(dst_p);
tcp_head.seq = htonl(seq);
tcp_head.ack_seq = htonl(ack);
tcp_head.doff = 5;
/* set or reset ack, fin or syn flags as needed */
tcp_head.ack = 0;
tcp_head.syn = 0;
tcp_head.fin = 0;
tcp_head.res1 = 0;
tcp_head.urg = 0;
tcp_head.psh = 0;
tcp_head.rst = 0;
tcp_head.res2 = 0;
tcp_head.window = htons(0x7c00);
tcp_head.check = 0; /*Fill in later*/
tcp_head.urg_ptr = 0;
/*Assemble structure for checksum calculation and calculate checksum*/
pseudohead.src_addr = ip_head.saddr;
pseudohead.dst_addr = ip_head.daddr;
pseudohead.dummy = 0;
pseudohead.proto = ip_head.protocol;
pseudohead.length = htons(sizeof(struct tcphdr) + len);
tcp_chk_construct.pshd = pseudohead;
tcp_chk_construct.tcphd = tcp_head;
memcpy(tcp_chk_construct.tcpdata, buffer, len);
tcp_head.check = in_cksum((unsigned short *) &tcp_chk_construct,
sizeof(struct tcp_pseudo) + sizeof(struct tcphdr) + len);
/*Assemble packet*/
memcpy(packet, (char *) &ip_head, sizeof(ip_head));
memcpy(packet + sizeof(ip_head), (char *) &tcp_head, sizeof(tcp_head));
memcpy(packet + sizeof(ip_head) + sizeof(tcp_head), buffer, len);
/*Send packet*/
target.sin_family = AF_INET;
target.sin_addr.s_addr = ip_head.daddr;
target.sin_port = tcp_head.dest;
i = sendto(sfd, packet, sizeof(struct iphdr) + sizeof(struct tcphdr) + len,
0, (struct sockaddr *) &target, sizeof(struct sockaddr_in));
if (i < 0)
return (-1); /*Error*/
else
return (i); /*Return number of bytes sent*/
****************************************
FUNCTION FOR CHECKSUM
****************************************
/* function to calculate the checksum for the packet */
unsigned short in_cksum(unsigned short *ptr, int nbytes) {
register long sum; /* assumes long == 32 bits */
u_short oddbyte;
register u_short answer; /* assumes u_short == 16 bits */
/*
* the algorithm is simple, using a 32-bit accumulator (sum),
* we add sequential 16-bit words to it, and at the end, fold back
* all the carry bits from the top 16 bits into the lower 16 bits.
*/
sum = 0;
while (nbytes > 1) {
sum += *ptr++;
nbytes -= 2;
}
/* mop up an odd byte, if necessary */
if (nbytes == 1) {
oddbyte = 0; /* make sure top half is zero */
*((u_char *) &oddbyte) = *(u_char *) ptr; /* one byte only */
sum += oddbyte;
}
/*
* Add back carry outs from top 16 bits to low 16 bits.
*/
sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* ones-complement, then truncate to 16 bits */
return (answer);
}
Since you haven't specified the details (looks suspiciously like homework to me as well), all i can do is give pointers.
Look up how structures are formed in the memory.
Look up Ip header format which will show you what member is where in an Ip header.
Look up Raw sockets.
So, provided you properly structure your struct and use raw sockets, you only need to write this struct to the socket.
I'm trying to send a raw packet using UDP, with the IP and UDP headers that I have constructed in my code. Raw packet successfully initialized with socket(PF_INET, SOCK_RAW, IPPROTO_UDP) and socket option set using setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int)).
The problem is when I send the packet using sendto(), I get the error 'Message too long'.
My IP header is 20 bytes and UDP header 8 bytes and my data is 12 bytes. So the total is only 40 bytes. This can't possibly be too long for a single UDP packet. Can someone help?
The type of val is a pointer to an int
Here is my code:
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
//The packet length in byes
#define PCKT_LEN 50
//Date size in bytes
#define DATA_SIZE 12
//PseudoHeader struct used to calculate UDP checksum.
typedef struct PseudoHeader{
unsigned long int source_ip;
unsigned long int dest_ip;
unsigned char reserved;
unsigned char protocol;
unsigned short int udp_length;
}PseudoHeader;
// Ripped from Richard Stevans Book
unsigned short ComputeChecksum(unsigned char *data, int len)
{
long sum = 0; /* assume 32 bit long, 16 bit short */
unsigned short *temp = (unsigned short *)data;
while(len > 1){
sum += *temp++;
if(sum & 0x80000000) /* if high order bit set, fold */
sum = (sum & 0xFFFF) + (sum >> 16);
len -= 2;
}
if(len) /* take care of left over byte */
sum += (unsigned short) *((unsigned char *)temp);
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
return ~sum;
}
int BindRawSocketToInterface(int rawsock, char *addr, short int port)
{
struct sockaddr_in s_addr;
s_addr.sin_family = AF_INET;
s_addr.sin_addr.s_addr = inet_addr(addr);
s_addr.sin_port = htons(port);
if((bind(rawsock, (struct sockaddr *)&s_addr, sizeof(s_addr)))== -1)
{
perror("Error binding raw socket to interface\n");
exit(-1);
}
return 1;
}
// Fabricate the IP header or we can use the
// standard header structures but assign our own values.
struct ip *CreateIPHeader(char *srcip, char *destip)
{
struct ip *ip_header;
ip_header = (struct ip *)malloc(sizeof(struct ip));
ip_header->ip_v = 4;
ip_header->ip_hl = 5;
ip_header->ip_tos = 0;
ip_header->ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE);
ip_header->ip_id = htons(111);
ip_header->ip_off = 0;
ip_header->ip_ttl = 111;
ip_header->ip_p = IPPROTO_TCP;
ip_header->ip_sum = 0; /* We will calculate the checksum later */
inet_pton(AF_INET, srcip, &ip_header->ip_src);
inet_pton(AF_INET, destip, &ip_header->ip_dst);
/* Calculate the IP checksum now :
The IP Checksum is only over the IP header */
ip_header->ip_sum = ComputeChecksum((unsigned char *)ip_header, ip_header->ip_hl*4);
return (ip_header);
}
// Creates a the UDP header.
struct udphdr *CreateUdpHeader(char *srcport, char *destport )
{
struct udphdr *udp_header;
/* Check netinet/udp.h for header definiation */
udp_header = (struct udphdr *)malloc(sizeof(struct udphdr));
udp_header->source = htons(atoi(srcport));
udp_header->dest = htons(atoi(destport));
udp_header->len = htons(sizeof(struct udphdr) + DATA_SIZE); //TODO: need to specify this
udp_header->check = htons(0);
return (udp_header);
}
void CreatePseudoHeaderAndComputeUdpChecksum(struct udphdr *udp_header, struct ip *ip_header, unsigned char *data)
{
/*The TCP Checksum is calculated over the PseudoHeader + TCP header +Data*/
/* Find the size of the TCP Header + Data */
int segment_len = ntohs(ip_header->ip_len) - ip_header->ip_hl*4;
/* Total length over which TCP checksum will be computed */
int header_len = sizeof(PseudoHeader) + segment_len;
/* Allocate the memory */
unsigned char *hdr = (unsigned char *)malloc(header_len);
/* Fill in the pseudo header first */
PseudoHeader *pseudo_header = (PseudoHeader *)hdr;
pseudo_header->source_ip = ip_header->ip_src.s_addr;
pseudo_header->dest_ip = ip_header->ip_dst.s_addr;
pseudo_header->reserved = 0;
pseudo_header->protocol = ip_header->ip_p;
pseudo_header->udp_length = htons(segment_len);
/* Now copy TCP */
memcpy((hdr + sizeof(PseudoHeader)), (void *)udp_header, 8);
/* Now copy the Data */
memcpy((hdr + sizeof(PseudoHeader) + 8), data, DATA_SIZE);
/* Calculate the Checksum */
udp_header->check = ComputeChecksum(hdr, header_len);
/* Free the PseudoHeader */
free(hdr);
}
// Source IP, source port, target IP, target port from the command line arguments
int main(int argc, char *argv[])
{
int sd;
char buffer[PCKT_LEN];
char *data = "Hello World!";
// Source and destination addresses: IP and port
struct sockaddr_in to_addr;
int one = 1;
const int *val = &one;
printf("IP Header Size: %lu \n", sizeof(struct ip));
printf("UDP Header Size: %lu \n", sizeof(struct udphdr));
printf("Data Size: %d\n", DATA_SIZE);
printf("IP Total: %lu \n", sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE);
memset(buffer, 0, PCKT_LEN);
if(argc != 5)
{
printf("- Invalid parameters!!!\n");
printf("- Usage %s <source hostname/IP> <source port> <target hostname/IP> <target port>\n", argv[0]);
exit(-1);
}
// Create a raw socket with UDP protocol
sd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
if(sd < 0)
{
perror("socket() error");
exit(-1);
}
else
printf("socket() - Using SOCK_RAW socket and UDP protocol is OK.\n");
//Bind the socket to the source address and port.
BindRawSocketToInterface(sd, argv[1], atoi(argv[2]));
// Inform the kernel do not fill up the packet structure. we will build our own...
if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int)) < 0)
{
perror("setsockopt() error");
close(sd);
exit(-1);
}
else
printf("setsockopt() is OK.\n");
// The source is redundant, may be used later if needed
// The address family
to_addr.sin_family = AF_INET;
to_addr.sin_addr.s_addr = inet_addr(argv[3]);
to_addr.sin_port = htons(atoi(argv[4]));
//Create the IP header.
struct ip *ip_header = CreateIPHeader(argv[1], argv[3]);
//Create the UDP header.
struct udphdr *udp_header = CreateUdpHeader(argv[2], argv[4]);
//Compute UDP checksum
CreatePseudoHeaderAndComputeUdpChecksum(udp_header, ip_header, (unsigned char*)data);
//Copy IP header, UDP header, and data to the packet buffer.
memcpy(buffer, ip_header, sizeof(struct ip));
memcpy(buffer + sizeof(struct ip), udp_header, 8 /*sizeof(struct udphdr)*/);
memcpy(buffer + sizeof(struct ip) + 8, data, DATA_SIZE);
printf("Using raw socket and UDP protocol\n");
printf("Using Source IP: %s port: %u, Target IP: %s port: %u.\n", argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));
if(sendto(sd, buffer, htons(20)/*ip_header->ip_len*/, 0, (struct sockaddr *)&to_addr, sizeof(to_addr)) < 0)
{
perror("sendto() error");
}
else
{
printf("sendto() is OK.\n");
}
free(ip_header);
free(udp_header);
close(sd);
return 0;
}
if(sendto(sd, buffer, htons(20)/*ip_header->ip_len*/, 0, (struct sockaddr *)&to_addr, sizeof(to_addr)) < 0)
The length here is wrong, you should not specify that in network byte order.
A simple 20 is enough. htons(20) will be a very large number on a little endian machine.
( if you want to send other things that just the IP header, you should include that too in the length,, sounds like your buffer is 40 bytes, not 20)
You can use my own function that i don't have your problem:
#define NDEBUG
#define COUNTMAX 3000
#define MAX_FAKE 100
#define ERROR -1
#define TYPE_A 1
#define TYPE_PTR 12
#define CLASS_INET 1
#define ERROR -1
#ifndef IPVERSION
#define IPVERSION 4
#endif /* IPVERISON */
#ifndef IP_MAXPACKET
#define IP_MAXPACKET 65535
#endif /* IP_MAXPACKET */
#define DNSHDRSIZE 12
#ifndef IP_MAXPACKET
#define IP_MAXPACKET 65535
#endif /* IP_MAXPACKET */
#define IPHDRSIZE sizeof(struct iphdr)
#define UDPHDRSIZE sizeof(struct udphdr)
int udp_send(int s, unsigned long saddr, unsigned long daddr,unsigned short sport,unsigned short dport,char *datagram, unsigned datasize)
{
struct sockaddr_in sin;
struct iphdr *ip;
struct udphdr *udp;
unsigned char *data;
unsigned char packet[4024];
ip = (struct iphdr *)packet;
udp = (struct udphdr *)(packet+IPHDRSIZE);
data = (unsigned char *)(packet+IPHDRSIZE+UDPHDRSIZE);
memset(packet,0,sizeof(packet));
udp->source = htons(sport);
udp->dest = htons(dport);
udp->len = htons(UDPHDRSIZE+datasize);
udp->check = 0;
memcpy(data,datagram,datasize);
ip->saddr.s_addr = saddr;
ip->daddr.s_addr = daddr;
ip->version = 4; /*ip version*/
ip->ihl = 5;
ip->ttl = 245;
ip->id = random()%5985;
ip->protocol = IPPROTO_UDP; /*protocol type*/
ip->tot_len = htons((IPHDRSIZE + UDPHDRSIZE + datasize));
ip->check = 0;
ip->check = in_cksum((char *)packet,IPHDRSIZE);
sin.sin_family=AF_INET;
sin.sin_addr.s_addr=daddr;
sin.sin_port=udp->dest;
printf ("socket: %d, packet: %s,size: %d, struct addr: %p, size: %i", s, packet, IPHDRSIZE+UDPHDRSIZE+datasize, (struct sockaddr*)&sin, sizeof(struct sockaddr));
return sendto(s, packet, IPHDRSIZE+UDPHDRSIZE+datasize, 0, (struct sockaddr*)&sin, sizeof(struct sockaddr));
}// end of udp_send function
The call to sendto() doesn't appear to be right as shown, even corrected per the comments:
Original: sendto(sd, buffer, htons(20)/ip_header->ip_len/,...);
Orr as corrected: sendto(sd, buffer, 20/ip_header->ip_len/,...);
The data length parameter should be the sum of header and data lengths: 20+8+12 = 40. A general solution could be:
sendto(sd, buffer, sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE,...);
With this change it started working for me.