2025-11-28 00:35:46 +09:00

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)