1235 lines
36 KiB
C
1235 lines
36 KiB
C
//
|
|
// Sample: Raw IPv4/IPv6 UDP with IP_HDRINCL option
|
|
//
|
|
// Files:
|
|
// rawudp.c - this file
|
|
// iphdr.h - IPv4, IPv6, and UDP structure definitions
|
|
// resolve.c - common name resolution routines
|
|
// resolve.h - header file for common name resolution routines
|
|
//
|
|
//
|
|
// Description:
|
|
// This is a simple app that demonstrates the usage of the
|
|
// IP_HDRINCL socket option. A raw socket is created of the
|
|
// UDP protocol where we will build our own IP and UDP header
|
|
// that we submit to sendto().
|
|
//
|
|
// For IPv4 this is fairly simple. Create a raw socket, set the
|
|
// IP_HDRINCL option, build the IPv4 and UDP headers, and do a
|
|
// sendto. The IPv4 stack will fragment the data as necessary and
|
|
// generally leaves the packet unmodified -- it performs fragmentation
|
|
// and sets the IPv4 ID field.
|
|
//
|
|
// For IPv6 its a bit more involved as it does not perform any
|
|
// fragmentation, you have to do it and build the headers yourself.
|
|
//
|
|
// The IP_HDRINCL option only works on Windows 2000 or greater.
|
|
//
|
|
// NOTE:
|
|
// From Network Programming for Microsoft Windows, Second Edition
|
|
// by Anthony Jones and James Ohlund. Copyright 2002.
|
|
// Reproduced by permission of Microsoft Press. All rights reserved.
|
|
//
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 4127)
|
|
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "resolve.h"
|
|
|
|
#include "iphdr.h"
|
|
|
|
//
|
|
// Setup some default values
|
|
//
|
|
#define DEFAULT_MTU 1496 // default MTU size
|
|
#define DEFAULT_TTL 8 // default TTL value
|
|
|
|
#define MAX_PACKET (0xFFFF + sizeof(IPV6_HDR)) // maximum datagram size
|
|
#define MAX_PACKET_FRAGMENTS 100
|
|
|
|
#define DEFAULT_PORT 5150 // default port to send to
|
|
#define DEFAULT_COUNT 5 // default number of messages to send
|
|
#define DEFAULT_MESSAGE "This is a test" // default message
|
|
|
|
#define DEFAULT_SEND_COUNT 10
|
|
|
|
#define FRAGMENT_HEADER_PROTOCOL 44 // protocol value for IPv6 fragmentation header
|
|
|
|
//
|
|
// Global variables
|
|
//
|
|
char *gSrcAddress=NULL, // IP address to send from
|
|
*gDestAddress=NULL, // IP address to send to
|
|
*gSrcPort=NULL, // port to send from
|
|
*gDestPort=NULL, // port to send to
|
|
*gMessage=NULL; // Message to send as UDP payload
|
|
int gAddressFamily=AF_UNSPEC,
|
|
gSocketType=SOCK_DGRAM, // Socket type to pass to name resolution routines
|
|
gProtocol=IPPROTO_UDP, // Protocol value that we're sending
|
|
gSendSize=0, // Data size of message to send
|
|
gMtuSize=DEFAULT_MTU; // Maximum transmission unit to use
|
|
DWORD gSendCount=DEFAULT_SEND_COUNT; // number of times to send
|
|
BOOL gSender=TRUE, // sending or receiving data
|
|
gReadRaw=TRUE; // Use raw sockets when reading
|
|
|
|
//
|
|
// Function: usage:
|
|
//
|
|
// Description:
|
|
// Print usage information and exit.
|
|
//
|
|
void usage(char *progname)
|
|
{
|
|
printf("usage: %s [-sp int] [-sa str] [-dp int] [-da str] [-n int] [-m str] ...\n"
|
|
" -a 4|6 Address family\n"
|
|
" -sa addr From (sender) port number\n"
|
|
" -sp int From (sender) IP address\n"
|
|
" -da addr To (recipient) port number\n"
|
|
" -dp int To (recipient) IP address\n"
|
|
" -n int Number of times to read message\n"
|
|
" -m str String message to fill packet data with\n"
|
|
" -p proto Protocol value\n"
|
|
" -r port Receive raw (SOCK_RAW) datagrams on the given port\n"
|
|
" -rd port Receive datagram (SOCK_DGRAM) on the given port\n"
|
|
" -t mtu MTU size (required for fragmentation)\n"
|
|
" -z int Size of message to send\n",
|
|
progname
|
|
);
|
|
ExitProcess(1);
|
|
}
|
|
|
|
//
|
|
// Function: ValidateArgs
|
|
//
|
|
// Description:
|
|
// Parse the command line arguments and set some global flags to
|
|
// indicate what actions to perform.
|
|
//
|
|
void ValidateArgs(int argc, char **argv)
|
|
{
|
|
int i;
|
|
|
|
gMessage = DEFAULT_MESSAGE;
|
|
for(i=1; i < argc ;i++)
|
|
{
|
|
if ((argv[i][0] == '-') || (argv[i][0] == '/'))
|
|
{
|
|
switch (tolower(argv[i][1]))
|
|
{
|
|
case 'a': // Address family
|
|
if (i+1 > argc)
|
|
usage(argv[0]);
|
|
if (argv[i+1][0] == '4')
|
|
gAddressFamily = AF_INET;
|
|
else if (argv[i+1][0] == '6')
|
|
gAddressFamily = AF_INET6;
|
|
else
|
|
usage(argv[0]);
|
|
i++;
|
|
break;
|
|
case 's': // source address
|
|
if (i+1 > argc)
|
|
usage(argv[0]);
|
|
if (tolower(argv[i][2]) == 'a')
|
|
{
|
|
gSrcAddress = argv[++i];
|
|
}
|
|
else if (tolower(argv[i][2]) == 'p')
|
|
{
|
|
gSrcPort = argv[++i];
|
|
}
|
|
else
|
|
{
|
|
usage(argv[0]);
|
|
break;
|
|
}
|
|
break;
|
|
case 'd': // destination address
|
|
if (i+1 > argc)
|
|
usage(argv[0]);
|
|
if (tolower(argv[i][2]) == 'a')
|
|
{
|
|
gDestAddress = argv[++i];
|
|
}
|
|
else if (tolower(argv[i][2]) == 'p')
|
|
{
|
|
gDestPort = argv[++i];
|
|
}
|
|
else
|
|
{
|
|
usage(argv[0]);
|
|
break;
|
|
}
|
|
break;
|
|
case 'n': // number of times to send message
|
|
if (i+1 >= argc)
|
|
usage(argv[0]);
|
|
gSendCount = atol(argv[++i]);
|
|
break;
|
|
case 'm': // String message to copy into payload
|
|
if (i+1 >= argc)
|
|
usage(argv[0]);
|
|
gMessage = argv[++i];
|
|
break;
|
|
case 'p': // Protocol value
|
|
if (i+1 >= argc)
|
|
usage(argv[0]);
|
|
gProtocol = atoi(argv[++i]);
|
|
break;
|
|
case 'r': // Port to receive data on
|
|
if (i+1 >= argc)
|
|
usage(argv[0]);
|
|
if (strlen(argv[i]) == 3)
|
|
gReadRaw = FALSE;
|
|
gSrcPort = argv[++i];
|
|
gSender = FALSE;
|
|
break;
|
|
case 't': // MTU size
|
|
if (i+1 >= argc)
|
|
usage(argv[0]);
|
|
gMtuSize = atoi(argv[++i]);
|
|
break;
|
|
case 'z': // Send size
|
|
if (i+1 >= argc)
|
|
usage(argv[0]);
|
|
gSendSize = atoi(argv[++i]);
|
|
break;
|
|
default:
|
|
usage(argv[0]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If no data size was given, initialize it to the message supplied
|
|
if (gSendSize == 0)
|
|
{
|
|
gSendSize = (int) strlen(gMessage);
|
|
}
|
|
|
|
// Do some simple validation of input
|
|
if (gSender)
|
|
{
|
|
if ((gSrcAddress == NULL) || (gDestAddress == NULL))
|
|
{
|
|
fprintf(stderr, "\nSource and destination addresses must be specified!\n\n");
|
|
usage(argv[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (gSrcAddress == NULL)
|
|
{
|
|
fprintf(stderr, "\nSource address must be specified!\n\n");
|
|
usage(argv[0]);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Function: checksum
|
|
//
|
|
// Description:
|
|
// This function calculates the 16-bit one's complement sum
|
|
// for the supplied buffer.
|
|
//
|
|
USHORT checksum(USHORT *buffer, int size)
|
|
{
|
|
unsigned long cksum=0;
|
|
|
|
while (size > 1)
|
|
{
|
|
cksum += *buffer++;
|
|
size -= sizeof(USHORT);
|
|
}
|
|
// If the buffer was not a multiple of 16-bits, add the last byte
|
|
if (size)
|
|
{
|
|
cksum += *(UCHAR*)buffer;
|
|
}
|
|
// Add the low order 16-bits to the high order 16-bits
|
|
cksum = (cksum >> 16) + (cksum & 0xffff);
|
|
cksum += (cksum >>16);
|
|
|
|
// Take the 1's complement
|
|
return (USHORT)(~cksum);
|
|
}
|
|
|
|
//
|
|
// Function: InitIpv4Header
|
|
//
|
|
// Description:
|
|
// Initialize the IPv4 header with the version, header length,
|
|
// total length, ttl, protocol value, and source and destination
|
|
// addresses.
|
|
//
|
|
int InitIpv4Header(
|
|
char *buf,
|
|
SOCKADDR *src,
|
|
SOCKADDR *dest,
|
|
int ttl,
|
|
int proto,
|
|
int payloadlen
|
|
)
|
|
{
|
|
IPV4_HDR *v4hdr=NULL;
|
|
|
|
v4hdr = (IPV4_HDR *)buf;
|
|
|
|
v4hdr->ip_verlen = (4 << 4) | (sizeof(IPV4_HDR) / sizeof(unsigned long));
|
|
v4hdr->ip_tos = 0;
|
|
v4hdr->ip_totallength = htons(sizeof(IPV4_HDR) + (USHORT) payloadlen);
|
|
v4hdr->ip_id = 0;
|
|
v4hdr->ip_offset = 0;
|
|
v4hdr->ip_ttl = (unsigned char)ttl;
|
|
v4hdr->ip_protocol = (unsigned char)proto;
|
|
v4hdr->ip_checksum = 0;
|
|
v4hdr->ip_srcaddr = ((SOCKADDR_IN *)src)->sin_addr.s_addr;
|
|
v4hdr->ip_destaddr = ((SOCKADDR_IN *)dest)->sin_addr.s_addr;
|
|
|
|
v4hdr->ip_checksum = checksum((unsigned short *)v4hdr, sizeof(IPV4_HDR));
|
|
|
|
return sizeof(IPV4_HDR);
|
|
}
|
|
|
|
//
|
|
// Function: InitIpv6Header
|
|
//
|
|
// Description:
|
|
// Initialize the IPv6 header with the version, payload length, next
|
|
// hop protocol, TTL, and source and destination addresses.
|
|
//
|
|
int InitIpv6Header(
|
|
char *buf,
|
|
SOCKADDR *src,
|
|
SOCKADDR *dest,
|
|
int ttl,
|
|
int proto,
|
|
int payloadlen
|
|
)
|
|
{
|
|
IPV6_HDR *v6hdr=NULL;
|
|
|
|
v6hdr = (IPV6_HDR *)buf;
|
|
|
|
// We don't explicitly set the traffic class or flow label fields
|
|
v6hdr->ipv6_vertcflow = htonl(6 << 28);
|
|
v6hdr->ipv6_payloadlen = htons((unsigned short)payloadlen);
|
|
v6hdr->ipv6_nexthdr = (unsigned char)proto;
|
|
v6hdr->ipv6_hoplimit = (unsigned char)ttl;
|
|
v6hdr->ipv6_srcaddr = ((SOCKADDR_IN6 *)src)->sin6_addr;
|
|
v6hdr->ipv6_destaddr = ((SOCKADDR_IN6 *)dest)->sin6_addr;
|
|
|
|
return sizeof(IPV6_HDR);
|
|
}
|
|
|
|
//
|
|
// Function: InitIpv6FragmentHeader
|
|
//
|
|
// Description:
|
|
// Initialize the IPv6 fragmentation header. The offset is the offset
|
|
// from the start of the IPv6 total payload (which includes the UDP
|
|
// header along with the data) which is why we add the length of
|
|
// the UDP header if this fragment is not the first fragment. Also,
|
|
// the lastfragment parameter is a boolean value (0 == not the last
|
|
// fragment while 1 == this is the last fragment) which is the opposite
|
|
// value thats supposed to be indicated in the header (i.e. 0 indicates
|
|
// that this fragment is the last fragment).
|
|
//
|
|
int InitIpv6FragmentHeader(
|
|
char *buf,
|
|
unsigned long offset,
|
|
int nextproto,
|
|
int id,
|
|
int lastfragment
|
|
)
|
|
{
|
|
IPV6_FRAGMENT_HDR *frag=NULL;
|
|
|
|
frag = (IPV6_FRAGMENT_HDR *)buf;
|
|
|
|
// Swap the value of this field
|
|
lastfragment = (lastfragment ? 0 : 1);
|
|
|
|
// Account for the size of the UDP header
|
|
if (offset != 0)
|
|
offset += sizeof(UDP_HDR);
|
|
|
|
frag->ipv6_frag_nexthdr = (unsigned char)nextproto;
|
|
frag->ipv6_frag_offset = htons( (unsigned short)(((offset/8) << 3) | lastfragment));
|
|
frag->ipv6_frag_id = htonl(id);
|
|
|
|
return sizeof(IPV6_FRAGMENT_HDR);
|
|
}
|
|
|
|
//
|
|
// Function: InitUdpHeader
|
|
//
|
|
// Description:
|
|
// Setup the UDP header which is fairly simple. Grab the ports and
|
|
// stick in the total payload length.
|
|
//
|
|
int InitUdpHeader(
|
|
char *buf,
|
|
SOCKADDR *src,
|
|
SOCKADDR *dest,
|
|
int payloadlen
|
|
)
|
|
{
|
|
UDP_HDR *udphdr=NULL;
|
|
|
|
udphdr = (UDP_HDR *)buf;
|
|
|
|
// Port numbers are already in network byte order
|
|
if (src->sa_family == AF_INET)
|
|
{
|
|
udphdr->src_portno = ((SOCKADDR_IN *)src)->sin_port;
|
|
udphdr->dst_portno = ((SOCKADDR_IN *)dest)->sin_port;
|
|
}
|
|
else if (src->sa_family == AF_INET6)
|
|
{
|
|
udphdr->src_portno = ((SOCKADDR_IN6 *)src)->sin6_port;
|
|
udphdr->dst_portno = ((SOCKADDR_IN6 *)dest)->sin6_port;
|
|
}
|
|
udphdr->udp_length = htons(sizeof(UDP_HDR) + (USHORT)payloadlen);
|
|
|
|
return sizeof(UDP_HDR);
|
|
}
|
|
|
|
//
|
|
// Function: ComputeUdpPseudoHeaderChecksumV4
|
|
//
|
|
// Description:
|
|
// Compute the UDP pseudo header checksum. The UDP checksum is based
|
|
// on the following fields:
|
|
// o source IP address
|
|
// o destination IP address
|
|
// o 8-bit zero field
|
|
// o 8-bit protocol field
|
|
// o 16-bit UDP length
|
|
// o 16-bit source port
|
|
// o 16-bit destination port
|
|
// o 16-bit UDP packet length
|
|
// o 16-bit UDP checksum (zero)
|
|
// o UDP payload (padded to the next 16-bit boundary)
|
|
//
|
|
// This routine uses a temporary buffer passed into the function (pseudobuf)
|
|
// for computing the pseudo-checksum. It is assumed this buffer is large enough.
|
|
//
|
|
void ComputeUdpPseudoHeaderChecksumV4(
|
|
char *pseudobuf,
|
|
void *iphdr,
|
|
UDP_HDR *udphdr,
|
|
char *payload,
|
|
int payloadlen
|
|
)
|
|
{
|
|
IPV4_HDR *v4hdr=NULL;
|
|
unsigned long zero=0;
|
|
char *ptr=NULL;
|
|
int chksumlen=0,
|
|
i;
|
|
|
|
ptr = pseudobuf;
|
|
|
|
v4hdr = (IPV4_HDR *)iphdr;
|
|
|
|
// Include the source and destination IP addresses
|
|
memcpy(ptr, &v4hdr->ip_srcaddr, sizeof(v4hdr->ip_srcaddr));
|
|
ptr += sizeof(v4hdr->ip_srcaddr);
|
|
chksumlen += sizeof(v4hdr->ip_srcaddr);
|
|
|
|
memcpy(ptr, &v4hdr->ip_destaddr, sizeof(v4hdr->ip_destaddr));
|
|
ptr += sizeof(v4hdr->ip_destaddr);
|
|
chksumlen += sizeof(v4hdr->ip_destaddr);
|
|
|
|
// Include the 8 bit zero field
|
|
memcpy(ptr, &zero, 1);
|
|
ptr++;
|
|
chksumlen += 1;
|
|
|
|
// Protocol
|
|
memcpy(ptr, &v4hdr->ip_protocol, sizeof(v4hdr->ip_protocol));
|
|
ptr += sizeof(v4hdr->ip_protocol);
|
|
chksumlen += sizeof(v4hdr->ip_protocol);
|
|
|
|
// UDP length
|
|
memcpy(ptr, &udphdr->udp_length, sizeof(udphdr->udp_length));
|
|
ptr += sizeof(udphdr->udp_length);
|
|
chksumlen += sizeof(udphdr->udp_length);
|
|
|
|
// UDP source port
|
|
memcpy(ptr, &udphdr->src_portno, sizeof(udphdr->src_portno));
|
|
ptr += sizeof(udphdr->src_portno);
|
|
chksumlen += sizeof(udphdr->src_portno);
|
|
|
|
// UDP destination port
|
|
memcpy(ptr, &udphdr->dst_portno, sizeof(udphdr->dst_portno));
|
|
ptr += sizeof(udphdr->dst_portno);
|
|
chksumlen += sizeof(udphdr->dst_portno);
|
|
|
|
// UDP length again
|
|
memcpy(ptr, &udphdr->udp_length, sizeof(udphdr->udp_length));
|
|
ptr += sizeof(udphdr->udp_length);
|
|
chksumlen += sizeof(udphdr->udp_length);
|
|
|
|
// 16-bit UDP checksum, zero
|
|
memcpy(ptr, &zero, sizeof(unsigned short));
|
|
ptr += sizeof(unsigned short);
|
|
chksumlen += sizeof(unsigned short);
|
|
|
|
// payload
|
|
memcpy(ptr, payload, payloadlen);
|
|
ptr += payloadlen;
|
|
chksumlen += payloadlen;
|
|
|
|
// pad to next 16-bit boundary
|
|
for(i=0 ; i < payloadlen%2 ; i++, ptr++)
|
|
{
|
|
printf("pad one byte\n");
|
|
*ptr = 0;
|
|
ptr++;
|
|
chksumlen++;
|
|
}
|
|
|
|
// Compute the checksum and put it in the UDP header
|
|
udphdr->udp_checksum = checksum((USHORT *)pseudobuf, chksumlen);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Function: CompouteUdpPseudoHeaderChecksumV6
|
|
//
|
|
// Description:
|
|
// Compute the pseudo header checksum for IPv6. The required fields for
|
|
// computing the pseudo checksum are:
|
|
//
|
|
// o 128-bit source address
|
|
// o 128-bit destination address
|
|
// o 4-byte payload length
|
|
// o 3-byte zero pad
|
|
// o 1-byte protocol value
|
|
// o 2-byte source port
|
|
// o 2-byte destination port
|
|
// o 2-byte UDP length
|
|
// o 2-byte UDP checksum (zero)
|
|
// o UDP payload (padded to the next 16-bit boundary)
|
|
//
|
|
// This routine uses a temporary buffer passed into the function (pseudobuf)
|
|
// for computing the pseudo-checksum. It is assumed this buffer is large enough.
|
|
//
|
|
void ComputeUdpPseudoHeaderChecksumV6(
|
|
char *pseudobuf,
|
|
void *iphdr,
|
|
UDP_HDR *udphdr,
|
|
char *payload,
|
|
int payloadlen
|
|
)
|
|
{
|
|
IPV6_HDR *v6hdr=NULL;
|
|
unsigned long length=0;
|
|
char proto,
|
|
*ptr=NULL;
|
|
int chksumlen=0,
|
|
i;
|
|
|
|
ptr = pseudobuf;
|
|
|
|
v6hdr = (IPV6_HDR *)iphdr;
|
|
|
|
// 128-bit source address
|
|
memcpy(ptr, &v6hdr->ipv6_srcaddr, sizeof(v6hdr->ipv6_srcaddr));
|
|
ptr += sizeof(v6hdr->ipv6_srcaddr);
|
|
chksumlen += sizeof(v6hdr->ipv6_srcaddr);
|
|
|
|
// 128-bit destination address
|
|
memcpy(ptr, &v6hdr->ipv6_destaddr, sizeof(v6hdr->ipv6_destaddr));
|
|
ptr += sizeof(v6hdr->ipv6_destaddr);
|
|
chksumlen += sizeof(v6hdr->ipv6_destaddr);
|
|
|
|
// Compute payload length in IPv6 header
|
|
length = htonl(payloadlen + sizeof(UDP_HDR));
|
|
|
|
// 4 byte length
|
|
memcpy(ptr, &length, sizeof(length));
|
|
ptr += sizeof(length);
|
|
chksumlen += sizeof(length);
|
|
|
|
// 3 bytes of zero
|
|
memset(ptr, 0, 3);
|
|
ptr += 3;
|
|
chksumlen +=3;
|
|
|
|
proto = IPPROTO_UDP;
|
|
|
|
// 1 byte protocol value
|
|
memcpy(ptr, &proto, sizeof(proto));
|
|
ptr += sizeof(proto);
|
|
chksumlen += sizeof(proto);
|
|
|
|
// UDP source port
|
|
memcpy(ptr, &udphdr->src_portno, sizeof(udphdr->src_portno));
|
|
ptr += sizeof(udphdr->src_portno);
|
|
chksumlen += sizeof(udphdr->src_portno);
|
|
|
|
// UDP destination port
|
|
memcpy(ptr, &udphdr->dst_portno, sizeof(udphdr->dst_portno));
|
|
ptr += sizeof(udphdr->dst_portno);
|
|
chksumlen += sizeof(udphdr->dst_portno);
|
|
|
|
// UDP length again
|
|
memcpy(ptr, &udphdr->udp_length, sizeof(udphdr->udp_length));
|
|
ptr += sizeof(udphdr->udp_length);
|
|
chksumlen += sizeof(udphdr->udp_length);
|
|
|
|
// 16-bit UDP checksum, zero
|
|
memset(ptr, 0, sizeof(unsigned short));
|
|
ptr += sizeof(unsigned short);
|
|
chksumlen += sizeof(unsigned short);
|
|
|
|
// payload
|
|
memcpy(ptr, payload, payloadlen);
|
|
ptr += payloadlen;
|
|
chksumlen += payloadlen;
|
|
|
|
// pad to next 16-bit boundary
|
|
for(i=0 ; i < payloadlen%2 ; i++, ptr++)
|
|
{
|
|
printf("pad one byte\n");
|
|
*ptr = 0;
|
|
ptr++;
|
|
chksumlen++;
|
|
}
|
|
|
|
// Compute the checksum and put it in the UDP header
|
|
udphdr->udp_checksum = checksum((USHORT *)pseudobuf, chksumlen);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Function: memfill
|
|
//
|
|
// Description:
|
|
// Fills a block of memory with a given string pattern.
|
|
//
|
|
void memfill(
|
|
char *dest,
|
|
int destlen,
|
|
char *data,
|
|
int datalen
|
|
)
|
|
{
|
|
char *ptr=NULL;
|
|
int copylen;
|
|
|
|
ptr = dest;
|
|
while (destlen > 0)
|
|
{
|
|
copylen = ((destlen > datalen) ? datalen : destlen);
|
|
memcpy(ptr, data, copylen);
|
|
|
|
destlen -= copylen;
|
|
ptr += copylen;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Function: PacketizeIpv4
|
|
//
|
|
// Description:
|
|
// This routine takes the data buffer and packetizes it for IPv4.
|
|
// Since the IPv4 stack takes care of fragmentation for us, this
|
|
// routine simply initializes the IPv4 and UDP headers. The data
|
|
// is returned in an array of WSABUF structures.
|
|
//
|
|
int
|
|
PacketizeIpv4(
|
|
WSABUF *Packets,
|
|
int *PacketCount,
|
|
char *PseudoBuffer,
|
|
struct addrinfo *src,
|
|
struct addrinfo *dest,
|
|
char *payload,
|
|
int payloadlen
|
|
)
|
|
{
|
|
int iphdrlen,
|
|
udphdrlen;
|
|
|
|
// Check the parameters
|
|
if ((Packets == NULL) || (PacketCount == NULL) || (src == NULL) ||
|
|
(dest == NULL) || (payload == NULL) || (*PacketCount < 1))
|
|
{
|
|
WSASetLastError(WSAEINVAL);
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
// Allocate memory for the packet
|
|
Packets[0].buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IPV4_HDR) + sizeof(UDP_HDR) + payloadlen);
|
|
if (Packets[0].buf == NULL)
|
|
{
|
|
fprintf(stderr, "PacetizeV4: HeapAlloc failed: %d\n", GetLastError());
|
|
WSASetLastError(WSAENOBUFS);
|
|
return NO_ERROR;
|
|
}
|
|
Packets[0].len = sizeof(IPV4_HDR) + sizeof(UDP_HDR) + payloadlen;
|
|
|
|
// Initialize the v4 header
|
|
iphdrlen = InitIpv4Header(
|
|
Packets[0].buf,
|
|
src->ai_addr,
|
|
dest->ai_addr,
|
|
DEFAULT_TTL,
|
|
gProtocol,
|
|
sizeof(UDP_HDR) + payloadlen
|
|
);
|
|
|
|
// Initialize the UDP header
|
|
udphdrlen = InitUdpHeader(
|
|
&Packets[0].buf[iphdrlen],
|
|
src->ai_addr,
|
|
dest->ai_addr,
|
|
payloadlen
|
|
);
|
|
|
|
// Compute the UDP checksum
|
|
ComputeUdpPseudoHeaderChecksumV4(
|
|
PseudoBuffer,
|
|
Packets[0].buf,
|
|
(UDP_HDR *)&Packets[0].buf[iphdrlen],
|
|
payload,
|
|
payloadlen
|
|
);
|
|
|
|
// Copy the payload to the end of the header
|
|
memcpy(&Packets[0].buf[iphdrlen + udphdrlen], payload, payloadlen);
|
|
|
|
*PacketCount = 1;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Function: PacketizeIpv6
|
|
//
|
|
// Description:
|
|
// This routine fragments data payload with the appropriate IPv6
|
|
// headers. The individual fragments are returned via an array of
|
|
// WSABUF structures. Each structure is a separate fragment of the
|
|
// whole message. The end of the fragments is indicated by a WSABUF
|
|
// entry with a NULL buffer pointer.
|
|
//
|
|
int
|
|
PacketizeIpv6(
|
|
WSABUF *Packets,
|
|
int *PacketCount,
|
|
char *PseudoBuffer,
|
|
struct addrinfo *src,
|
|
struct addrinfo *dest,
|
|
char *payload,
|
|
int payloadlen
|
|
)
|
|
{
|
|
static ULONG fragid=1;
|
|
int offset=0, // offset into payload
|
|
datalen, // length of the payload
|
|
hdrlen, // length of the header(s)
|
|
fragment, // is this a fragment?
|
|
lastfragment, // is this the last fragment?
|
|
iphdrlen, // length of ip header
|
|
udphdrlen, // length of the udp header
|
|
plushdrs, // IPv6 length field includes encapsulated headers
|
|
numpackets=0, // number of fragments
|
|
originalpayload;
|
|
|
|
// Check the parameters
|
|
if ((Packets == NULL) || (PacketCount == NULL) || (src == NULL) ||
|
|
(dest == NULL) || (payload == NULL))
|
|
return SOCKET_ERROR;
|
|
|
|
originalpayload = payloadlen;
|
|
do
|
|
{
|
|
if (numpackets >= *PacketCount)
|
|
{
|
|
fprintf(stderr, "PacketizeIpv6: Fragment count exceeded limit of %d\n",
|
|
*PacketCount);
|
|
WSASetLastError(WSAEFAULT);
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
// Compute the size of this fragment
|
|
lastfragment = 0;
|
|
fragment = 0;
|
|
if ((payloadlen > gMtuSize) && (numpackets == 0))
|
|
{
|
|
// Data needs to be fragmented, this is the first packet
|
|
hdrlen = sizeof(IPV6_HDR) + sizeof(UDP_HDR) + sizeof(IPV6_FRAGMENT_HDR);
|
|
datalen = gMtuSize - hdrlen;
|
|
plushdrs = sizeof(UDP_HDR) + sizeof(IPV6_FRAGMENT_HDR);
|
|
fragment = 1;
|
|
|
|
printf("Require fragmentation: FIRST packet\n");
|
|
}
|
|
else if ((payloadlen > gMtuSize) && (numpackets > 0))
|
|
{
|
|
// Data needs to be fragmented, this is packet number > 0
|
|
hdrlen = sizeof(IPV6_HDR) + sizeof(IPV6_FRAGMENT_HDR);
|
|
datalen = gMtuSize - hdrlen;
|
|
fragment = 1;
|
|
plushdrs = sizeof(IPV6_FRAGMENT_HDR);
|
|
|
|
printf("Require fragmentation: packet number > 0\n");
|
|
}
|
|
else if (numpackets == 0)
|
|
{
|
|
// Data doesn't need to be fragmented
|
|
hdrlen = sizeof(IPV6_HDR) + sizeof(UDP_HDR);
|
|
datalen = payloadlen;
|
|
fragment = 0;
|
|
plushdrs = sizeof(UDP_HDR);
|
|
|
|
printf("No fragmentation required\n");
|
|
}
|
|
else
|
|
{
|
|
// This is the last fragment
|
|
hdrlen = sizeof(IPV6_HDR) + sizeof(IPV6_FRAGMENT_HDR);
|
|
datalen = payloadlen;
|
|
fragment = 1;
|
|
plushdrs = sizeof(IPV6_FRAGMENT_HDR);
|
|
lastfragment = 1;
|
|
|
|
printf("Require fragmentation: Last packet\n");
|
|
}
|
|
|
|
// Build packet
|
|
|
|
// Allocate buffer for this fragment
|
|
printf("numpackets = %d; hdrlen = %d; datlen = %d\n", numpackets, hdrlen, datalen);
|
|
Packets[numpackets].buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, hdrlen + datalen);
|
|
if (Packets[numpackets].buf == NULL)
|
|
{
|
|
fprintf(stderr, "PacketizeV6: HeapAlloc failed: %d\n", GetLastError());
|
|
WSASetLastError(WSAENOBUFS);
|
|
return SOCKET_ERROR;
|
|
}
|
|
Packets[numpackets].len = hdrlen + datalen;
|
|
|
|
printf("[%02d] .buf = 0x%p .len = %d\n", numpackets, Packets[numpackets].buf, Packets[numpackets].len);
|
|
|
|
// Initialize the V6 header, if we have to fragment the next header field of
|
|
// the v6 header is that of the fragmentation header. Also the payload
|
|
// length includes the headers (UDP + fragmentation) and the payload itself.
|
|
iphdrlen = InitIpv6Header(
|
|
Packets[numpackets].buf,
|
|
src->ai_addr,
|
|
dest->ai_addr,
|
|
DEFAULT_TTL,
|
|
(fragment ? FRAGMENT_HEADER_PROTOCOL : gProtocol),
|
|
datalen + plushdrs
|
|
);
|
|
|
|
// Build the fragmentation header if necessary
|
|
if (fragment)
|
|
{
|
|
iphdrlen += InitIpv6FragmentHeader(
|
|
&Packets[numpackets].buf[iphdrlen],
|
|
offset, // offset from start of packet
|
|
gProtocol,
|
|
fragid,
|
|
lastfragment
|
|
);
|
|
}
|
|
|
|
// The first fragment includes the UDP header, subsequent fragments don't
|
|
if (numpackets == 0)
|
|
{
|
|
udphdrlen = InitUdpHeader(
|
|
&Packets[numpackets].buf[iphdrlen],
|
|
src->ai_addr,
|
|
dest->ai_addr,
|
|
originalpayload //payloadlen
|
|
);
|
|
|
|
// Compute the checksum
|
|
ComputeUdpPseudoHeaderChecksumV6(
|
|
PseudoBuffer,
|
|
Packets[numpackets].buf,
|
|
(UDP_HDR *)&Packets[numpackets].buf[iphdrlen],
|
|
payload,
|
|
payloadlen);
|
|
}
|
|
else
|
|
{
|
|
udphdrlen = 0;
|
|
}
|
|
|
|
// Copy the payload into this fragment
|
|
memcpy(&Packets[numpackets].buf[iphdrlen + udphdrlen], &payload[offset], datalen);
|
|
|
|
// Adjust our counters
|
|
payloadlen = payloadlen - datalen;
|
|
offset += datalen;
|
|
numpackets++;
|
|
|
|
} while (payloadlen > 0);
|
|
|
|
fragid++;
|
|
|
|
// Update the count to indicate how many fragments were generated
|
|
*PacketCount = numpackets;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Function: main
|
|
//
|
|
// Description:
|
|
// First, parse command line arguments and load Winsock. Then
|
|
// create the raw socket and then set the IP_HDRINCL option.
|
|
// Following this assemble the IP and UDP packet headers by
|
|
// assigning the correct values and calculating the checksums.
|
|
// Then fill in the data and send to its destination.
|
|
//
|
|
int _cdecl main(int argc, char **argv)
|
|
{
|
|
WSADATA wsd;
|
|
SOCKET s=INVALID_SOCKET;
|
|
DWORD bytes;
|
|
WSABUF *fragments=NULL;
|
|
int fragmentcount=0;
|
|
char *buffer=NULL, // Used as receive buffer or
|
|
// to compute pseudo header
|
|
*payload=NULL; // Buffer containing payload
|
|
struct addrinfo *ressrc=NULL,
|
|
*resdest=NULL;
|
|
int rc,
|
|
i, j;
|
|
|
|
// Parse command line arguments and print them out
|
|
ValidateArgs(argc, argv);
|
|
|
|
srand(GetTickCount());
|
|
|
|
if ((rc = WSAStartup(MAKEWORD(2,2), &wsd)) != 0)
|
|
{
|
|
printf("WSAStartup() failed: %d\n", rc);
|
|
return -1;
|
|
}
|
|
|
|
// Convert the source and destination addresses/ports
|
|
ressrc = ResolveAddress(gSrcAddress, gSrcPort, gAddressFamily, gSocketType, gProtocol);
|
|
if (ressrc == NULL)
|
|
{
|
|
fprintf(stderr, "Unable to resolve address '%s' and port '%s'\n",
|
|
gSrcAddress, gSrcPort);
|
|
goto cleanup;
|
|
}
|
|
|
|
printf("Source Address : ");
|
|
PrintAddress(ressrc->ai_addr, (int)ressrc->ai_addrlen);
|
|
printf("\n");
|
|
|
|
// Resolve the destination address if necessary and print some parameters
|
|
if (gSender)
|
|
{
|
|
resdest = ResolveAddress(gDestAddress, gDestPort, ressrc->ai_family, ressrc->ai_socktype, ressrc->ai_protocol);
|
|
if (resdest == NULL)
|
|
{
|
|
fprintf(stderr, "Unable to resolve address '%s' and port '%s'\n",
|
|
gDestAddress, gDestPort);
|
|
goto cleanup;
|
|
}
|
|
|
|
printf("Destination Address: ");
|
|
PrintAddress(resdest->ai_addr, (int) resdest->ai_addrlen);
|
|
printf("\n");
|
|
|
|
printf("Message Size : %d\n", gSendSize);
|
|
printf("Message Count : %lu\n", gSendCount);
|
|
printf("MTU Size : %d\n", gMtuSize);
|
|
}
|
|
else
|
|
{
|
|
printf("Socket Type : %s\n", (gReadRaw ? "SOCK_RAW" : "SOCK_DGRAM"));
|
|
}
|
|
|
|
|
|
// Creating a raw socket
|
|
|
|
// BUG - For IPv6 if we create the raw socket with IPPROTO_UDP then the Ipv6
|
|
// stack will thow away our IPv6 and UDP headers and put "valid" ones in their
|
|
// place. As a workaround, create the socket with a protocol value of an
|
|
// unhandled protocol. Of course the IPv6 header should still indicate that
|
|
// the encapsulated protocol is UDP.
|
|
|
|
if (gSender)
|
|
s = socket(ressrc->ai_family, SOCK_RAW, ((ressrc->ai_family == AF_INET6) ? 3 : ressrc->ai_protocol));
|
|
else if (!gSender && gReadRaw)
|
|
s = socket(ressrc->ai_family, SOCK_RAW, ressrc->ai_protocol);
|
|
else
|
|
s = socket(ressrc->ai_family, SOCK_DGRAM, ressrc->ai_protocol);
|
|
if (s == INVALID_SOCKET)
|
|
{
|
|
fprintf(stderr, "socket failed: %d\n", WSAGetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
// Allocate a buffer for computing the pseudo header checksum
|
|
buffer = (char *)HeapAlloc(GetProcessHeap(), 0, MAX_PACKET);
|
|
if (buffer == NULL)
|
|
{
|
|
fprintf(stderr, "HeapAlloc failed: %d\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
if (gSender)
|
|
{
|
|
int optlevel,
|
|
option,
|
|
optval;
|
|
|
|
// Allocate the payload
|
|
payload = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, gSendSize);
|
|
if (payload == NULL)
|
|
{
|
|
fprintf(stderr, "HeapAlloc failed: %d\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
memfill(payload, gSendSize, gMessage, (int)strlen(gMessage));
|
|
|
|
// Allocate storage for the fragments
|
|
fragmentcount = MAX_PACKET_FRAGMENTS;
|
|
fragments = (WSABUF *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, fragmentcount * sizeof(WSABUF));
|
|
if (fragments == NULL)
|
|
{
|
|
fprintf(stderr, "HeapAlloc failed: %d\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
// Enable the IP header include option
|
|
optval = 1;
|
|
if (ressrc->ai_family == AF_INET)
|
|
{
|
|
optlevel = IPPROTO_IP;
|
|
option = IP_HDRINCL;
|
|
}
|
|
else if (ressrc->ai_family == AF_INET6)
|
|
{
|
|
optlevel = IPPROTO_IPV6;
|
|
option = IPV6_HDRINCL;
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Address family returned is unsupported: %d\n",
|
|
ressrc->ai_family);
|
|
goto cleanup;
|
|
}
|
|
|
|
rc = setsockopt(s, optlevel, option, (char *)&optval, sizeof(optval));
|
|
if (rc == SOCKET_ERROR)
|
|
{
|
|
fprintf(stderr, "setsockopt: IP_HDRINCL failed: %d\n", WSAGetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
// Packetize and/or perform necessary fragmentation on data
|
|
if (ressrc->ai_family == AF_INET)
|
|
{
|
|
rc = PacketizeIpv4(
|
|
fragments,
|
|
&fragmentcount,
|
|
buffer,
|
|
ressrc,
|
|
resdest,
|
|
payload,
|
|
gSendSize
|
|
);
|
|
}
|
|
else if (ressrc->ai_family == AF_INET6)
|
|
{
|
|
rc = PacketizeIpv6(
|
|
fragments,
|
|
&fragmentcount,
|
|
buffer,
|
|
ressrc,
|
|
resdest,
|
|
payload,
|
|
gSendSize
|
|
);
|
|
}
|
|
|
|
// Verify packetization succeeded
|
|
if (rc == SOCKET_ERROR)
|
|
{
|
|
fprintf(stderr, "Packetizing failed\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
printf("%d fragments required\n", fragmentcount);
|
|
|
|
// Apparently, this SOCKADDR_IN structure makes no difference.
|
|
// Whatever we put as the destination IP addr in the IP
|
|
// header is what goes. Specifying a different dest in remote
|
|
// will be ignored.
|
|
for(i=0; i < (int)gSendCount ;i++)
|
|
{
|
|
for(j=0; j < fragmentcount ;j++)
|
|
{
|
|
rc = sendto(
|
|
s,
|
|
fragments[j].buf,
|
|
fragments[j].len,
|
|
0,
|
|
resdest->ai_addr,
|
|
(int) resdest->ai_addrlen
|
|
);
|
|
bytes = rc;
|
|
if (rc == SOCKET_ERROR)
|
|
{
|
|
printf("sendto() failed: %d\n", WSAGetLastError());
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
printf("sent %d bytes\n", rc);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
SOCKADDR_STORAGE safrom;
|
|
WSAEVENT hEvent;
|
|
int fromlen;
|
|
|
|
// Bind the socket to the receiving address
|
|
rc = bind(
|
|
s,
|
|
ressrc->ai_addr,
|
|
(int) ressrc->ai_addrlen
|
|
);
|
|
if (rc == SOCKET_ERROR)
|
|
{
|
|
fprintf(stderr, "bind failed: %d\n", WSAGetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
printf("binding to: ");
|
|
PrintAddress(ressrc->ai_addr, (int)ressrc->ai_addrlen);
|
|
printf("\n");
|
|
|
|
// The IPv6 stack currently doesn't allow receiving raw UDP packets so
|
|
// print a warning.
|
|
if (gReadRaw && (ressrc->ai_family == AF_INET6))
|
|
printf("\nNOTE: The IPv6 stack currently does not allow reading raw UDP packets\n\n");
|
|
|
|
hEvent = WSACreateEvent();
|
|
if (hEvent == NULL)
|
|
{
|
|
fprintf(stderr, "WSACreateEvent failed: %d\n", WSAGetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
#ifdef WSAEVENTSELECT
|
|
rc = WSAEventSelect(s, hEvent, FD_READ);
|
|
if (rc == SOCKET_ERROR)
|
|
{
|
|
fprintf(stderr, "WSAEventSelect failed: %d\n", WSAGetLastError());
|
|
goto cleanup;
|
|
}
|
|
#endif
|
|
|
|
// Receive the data
|
|
while (1)
|
|
{
|
|
#ifdef WSAEVENTSELECT
|
|
rc = WaitForSingleObject(hEvent, INFINITE);
|
|
if ( (rc == WAIT_FAILED) || (rc == WAIT_TIMEOUT) )
|
|
{
|
|
fprintf(stderr, "WaitForSingleObject failed: %d\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
#endif
|
|
|
|
fromlen = sizeof(safrom);
|
|
rc = recvfrom(
|
|
s,
|
|
buffer,
|
|
MAX_PACKET,
|
|
0,
|
|
(SOCKADDR *)&safrom,
|
|
&fromlen
|
|
);
|
|
if (rc == SOCKET_ERROR)
|
|
{
|
|
fprintf(stderr, "recvfrom failed: %d\n", WSAGetLastError());
|
|
break;
|
|
}
|
|
printf("Read %d bytes from ", rc);
|
|
PrintAddress((SOCKADDR *)&safrom, fromlen);
|
|
printf("\n");
|
|
|
|
WSAResetEvent(hEvent);
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
|
|
//
|
|
// Cleanup allocations and sockets
|
|
//
|
|
|
|
if (ressrc)
|
|
freeaddrinfo(ressrc);
|
|
|
|
if (resdest)
|
|
freeaddrinfo(resdest);
|
|
|
|
if (buffer)
|
|
HeapFree(GetProcessHeap(), 0, buffer);
|
|
|
|
if (payload)
|
|
HeapFree(GetProcessHeap(), 0, payload);
|
|
|
|
// Free the packet buffers
|
|
if (fragments)
|
|
{
|
|
for(i=0; i < fragmentcount ;i++)
|
|
{
|
|
if (fragments[i].buf != NULL)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, fragments[i].buf);
|
|
fragments[i].buf = NULL;
|
|
}
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, fragments);
|
|
}
|
|
|
|
if (s != INVALID_SOCKET)
|
|
closesocket(s);
|
|
|
|
WSACleanup();
|
|
|
|
return 0;
|
|
}
|
|
|
|
#pragma warning(pop)
|