I truly need help on this. I am trying to write a test program that simulate a network environment that have 5 clients and 1 server. To do so, I need to be able to edit IP header to change it's source address and various other values in the header. To change it, I need to use socket raw and IP_HRINCL socket option.
However, it doesn't work during the send with error 10049, "Address not available" from WSAGetLastError(). I am wondering how would I do so, and can I please look at a simple example on how to change your IP header and send it. By the way, I am using winsock with VC++, and my os is XP. I know there are some issues regarding XP's compatibility with Raw socket. In addition, if winsock is not a viable option, is there anyway for me to write in lower socket?
Here's my code
Code:
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
using namespace std;
int initialize();
void main()
{
initialize();
SOCKET sock = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
int error;
char on = 1;
setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on));
if(sock == INVALID_SOCKET)
cout << "Crap raw socket " << endl;
/* Header data */
unsigned char header[32] = {0x45,0x00,0x00,0x20,0x01,0xf1,0x00,0x00,0x05,0x11,0x9c,0x0a,0x05,0x3a,0x38,0x02,0xe0,
0x02,0xfa,0x93};
memset(header+20,0,12);
SOCKADDR_IN sock_add;
sock_add.sin_family = AF_INET;
sock_add.sin_port = htons(1542);
sock_add.sin_addr.s_addr = inet_addr("192.168.1.1");
error = sendto(sock,(char*)header,32,0,(LPSOCKADDR)&sock_add,sizeof(SOCKADDR_IN));
}
int initialize()
{
int t;
/* Before any Winsock function to work, we need to call to wsock.dll to initialize those
functions */
WSADATA wsa_data;
t = WSAStartup(MAKEWORD(1,1), &wsa_data);
if(t != 0)
return -1;
else
return 0;
}
Well '10049' WSAEADDRNOTAVAIL "The requested address is not valid in its context". Seems like the source address is not right. Using struct for header is always better and easier. For using UDP you may have to change the socket to IPPROTO_UDP. As for IP_HDRINCL you should be getting in XP if you have admin previlage. I'm pasting the header of IP and UDP for your easy reference.
Code:
// The IP header structure
//
typedef struct _iphdr
{
unsigned char h_len:4; // Length
unsigned char ver:4; // IP Version
unsigned char tos; // Type of service
unsigned short totlen; // Total length of the packet
unsigned short id; // Unique identifier
unsigned short offset; // Fragment offset field
unsigned char ttl; // Time to live
unsigned char proto; // Protocol(TCP,UDP,ICMP,IGMP...)
unsigned short checksum; // IP checksum
unsigned int srcIP; // Source IP address
unsigned int destIP; // Destination IP address
}IpHeader, * LPIpHeader;
// The UDP header structure
//
typedef struct _udphdr
{
unsigned short sport; // Source Port
unsigned short dport; // Destination Port
unsigned short Length; // Length
unsigned short Checksum; // Checksum
}UdpHeader, * LPUdpHeader;
PS: You also need winsock 2 (link to ws2_32.lib)
Even if our suggestions didn't help, please post the answer once you find it. We took the effort to help you, please return it to others.
Well, the question now is that I cannot write the newly created data into header. What happen is that the socket treat the information as data and attach the appropriate IP header and UDP header with it. Let say if i created a data with all the appropriate header information, how do I send it? and what socket option should I set?
If I correctly got what you asked, you send using the sendto() as it is showed in your code which is pasted. All you have to do to the above code(yours) is to fill the header using struct as I've shown, rather that the way you are using. Rest every thing is fine. The SOCKADDR_IN structure that you fill just before calling sendto() won't matter much (whether it is right or wrong) because the information is already in your header.
Hope that helps
Even if our suggestions didn't help, please post the answer once you find it. We took the effort to help you, please return it to others.
Well, this is my code. the problem I have now is that 10049 from WSAGetLastError(). One possible reason is that the socket cannot find the appropriate interface to send the data. However, i highly doubt that's the case. I don't think checksum is an issue since the receiving end will simply drop the packet. The main concern is how to send it.
Code:
void main()
{
initialize();
SOCKET sock = socket(AF_INET,SOCK_RAW,IPPROTO_UDP);
SOCKADDR_IN sock_addr;
char on = 1;
setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on));
unsigned char sendBuf[2048];
memset(sendBuf,0,2048);
unsigned char data[20];
memset(data,0,20);
IpHeader *iphdr;
UdpHeader *udphdr, udpHdr;
int iUdpSize, error;
PSHeader pseudo_header;
iphdr = (IpHeader *) sendBuf; // the ip header now points to the top of the sendBuf
udphdr= (UdpHeader *) (sendBuf + sizeof(IpHeader)); // the udp header points to the part next to the ip header
sock_addr.sin_family = AF_INET;
sock_addr.sin_port = htons (1526);
sock_addr.sin_addr.s_addr = inet_addr("2.3.4.5");
iphdr->ver = 4;
iphdr->h_len = 5;
iphdr->tos = 0;
iphdr->totlen = sizeof (IpHeader) + sizeof (UdpHeader) + sizeof(data);
iphdr->id = 1;
iphdr->offset = 0;
iphdr->ttl = 255;
iphdr->proto = IPPROTO_UDP; //UDP
iphdr->checksum = 0;
iphdr->srcIP = inet_addr ("5.57.52.11"); // your source id
iphdr->destIP = sock_addr.sin_addr.s_addr;
// Initalize the UDP header
//
iUdpSize = sizeof(udpHdr) + strlen((char*)data);
udphdr->sport = htons(1564) ;
udphdr->dport = htons(1700) ;
udphdr->Length = htons(iUdpSize) ;
udphdr->Checksum = 0 ;
//calculate UDP CheckSum
pseudo_header.destaddr = inet_addr("192.168.1.1");
pseudo_header.srcaddr = inet_addr("1.2.3.4");
pseudo_header.zero = 0;
pseudo_header.protocol = 17;
pseudo_header.len = sizeof(data) + sizeof(udphdr);
// fill the data
udpHdr.Checksum = checksum((unsigned short*) udphdr,sizeof(pseudo_header));
iphdr->checksum = checksum((unsigned short *) iphdr, sizeof(IpHeader));
for(int i=0;i<40;i++)
printf("%x",sendBuf[i]);
error = sendto(sock,(char*)sendBuf,40,0,(LPSOCKADDR)&sock_addr,sizeof(SOCKADDR_IN));
if(error == SOCKET_ERROR)
cout << WSAGetLastError() << endl;
else
cout << "sent" << endl;
}
The error that you got is the address is wrong. The checksum was calculated wrongly including many other omission and mistakes. I've quickly created a working version of your code and attached. I recommend you to compare with your code study/play with it before setting out. Point is that you should understand the underlying working principle of it.
Even if our suggestions didn't help, please post the answer once you find it. We took the effort to help you, please return it to others.
Does this method works with IPv6? If not, what are other ways to config header parameters in ipV6? I understand that IPv6 uses different header than IPv4, but does the socket recognize it by simply look at the "version parameter"?
It should work with IPv6. However you should note that the header struct for v6 and that of v4 are entirely different. Version feild is the same, but simply changing the vesion no won't change from 4 to 6. For instance IPv4's header is 20 bytes(not fixed though), but IPv6's header is fixed to 40 bytes. 4 does have checksum, but 6 relies on the upper layer protocols to do it.
The most important thing, however is, your os should support IPv6 before you can send IPv6 packets
Even if our suggestions didn't help, please post the answer once you find it. We took the effort to help you, please return it to others.
I get your code working perfectly on Windows 2000.
Thank you very much.
But when I run it on Windows XP (SP3), the 'sendto' function fails with error 10004.
I've read that, starting XP SP2, Windows does not allow sending packets with spoofed IP.
BUT I do not spoof the IP: Even when I enter my own IP as the source and a valid IP within my LAN as destination, it fails with error 10004.
Any hints on how to work around this and get your code working under Windows XP?
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.