This is not, as the title implies, a fully functional TCP/IP stack. It does however reply to most incomming TCP/IP connections and other layer requests (including ICMP ECHO requests - Ping). This function should be passed raw ethernet frames, and will generate a response packet that will convince other stacks on a network that this machine can be connected to(SYN/ACK in response to SYN requests). For example, if one telnets to a machine running this function, a connection will be successfully made, and any data sent will be sent back (and printed on the machine running this code.) This code is part of a metwprl operating system Brandon Burr and Martin McCormick (myself), are writing. We cannot release the full code. However I feel that this may be beneficial to others writing network functions. (including TCP/UDP/IP checksum code).
NOTE: This should not be taken seriously as a TCP/IP stack, as that it remembers NO state information. It can be used (as this is) as a lower level protocol handler for your own userlevel protocol).
A raw ethernet frame (I use a driver I wrote for SurfOs passing packets from a 3COM 3c905b-tx network card.
Assumes you have low level access to ethernet frames. (Try WinPCap for windows)
A reply ethernet frame to the given packet.
Very secure, and stable. Remembers no state information.
Submitted On | |
By | Wåzerface |
Level | Intermediate |
User Rating | 5.0 (15 globes from 3 users) |
Compatibility | C, C++ (general), Microsoft Visual C++, Borland C++, UNIX C++ |
Category | Algorithms |
World | C / C++ |
Archive File |
-----BEGIN ETHERNET.H-----
/*
NETWORK.H - The network header file
Written by Martin McCormick (surfos@wazer.net) (c) 2004
for the Surf Operating System
*/
/* because of loopback references, need structure prototypes (weird huh?) */
struct iface;
struct sPacketBuf;
struct sPacket
{
unsigned int iLen;
unsigned char * pBuf;
unsigned int iFlags; /* 32 bit */
/*
-Bits 0 through 7 - CHECKSUM FLAGS
(1<<0) = ETHERNET checksum checked
(1<<1) = ENCAPSULATION checksum checked
(1<<2) = IP checksum checked
(1<<3) = TCP/UDP checksum checked
(1<<4) = ETHERNET checksum correct
(1<<5) = ENCAPSULATION checksum correct
(1<<6) = IP checksum correct
(1<<7) = TCP/UDP checksum correct
-Bits 8 through 15 - USER LEVEL FLAGS
-Bits 16 through 23 - HANDLER FLAGS (if any set, driver will not return packet as 'on buffer')
(1<<16) = Packet is activated for proccessing.
(1<<17) = Packet is on TX queue.
(1<<18) = Packet has been sent and freed.
-Bits 24 through 32 - DRIVER FLAGS
(1<<24) = Packet synched.
*/
};
enum PacketChecksumFlags {
CHECKSUM_FLAGS = (0xff<<0),
ETH_SUM_CHECKED = (1<<0), ENC_SUM_CHECKED = (1<<1), IP_SUM_CHECKED = (1<<2),
TRANS_SUM_CHECKED = (1<<3), ETH_SUM_CORRECT = (1<<4), ENC_SUM_CORRECT = (1<<5),
IP_SUM_CORRECT = (1<<6), TRANS_SUM_CORRECT = (1<<7)
};
enum PacketHandlerFlags {
HANDLER_FLAGS = (0xff<<16),
PACKET_ACTIVE = (1<<16), PACKET_ON_TX = (1<<17), PACKET_SENT = (1<<18)
};
enum PacketDriverFlags {
DRIVER_FLAGS = (0xff<<24),
PACKET_SYNCHED = (1<<24), PACKET_LOST = (1<<25)
};
/* SnagPackets(struct iface * interface, struct sPacketBuf ** packets, unsigned int * count);
SnagPackets receives a given number of packets (count)
iface - the interface to receive on (should be owner of function pointer)
packets - a pointer to the head of an array of packet pointers
count - takes in max number to receive, output is actual count
*/
typedef int(* FuncSnag)
( /* parameters */
struct iface * interface,
struct sPacketBuf ** packets,
unsigned int * count
);
/* SendPackets(struct iface * interface, struct sPacketBuf ** packets, unsigned int * count);
SendPackets sends a given number of packets (count)
iface - the interface to send upon (should be owner of function pointer)
packets - a pointer to the head of an array of pointers to outgoing packets
count - takes in number to send, output is actual sent
*/
typedef int(* FuncSend)
( /* parameters */
struct iface * interface,
struct sPacketBuf ** packets,
unsigned int * count
);
/* Setting(struct iface * interface, int iSetting, void * param);
Setting() sets (or receives) network card information.
iface - interface to use
iSetting - a constant reffering to the type of setting (see note below)
param - Used for I/O operations on settings
*/
typedef int(* FuncSetting)
( /* parameters */
struct iface * interface,
unsigned int iSetting,
void * param
);
/* iface - The main interface structure used for network interfaces */
struct iface
{
unsigned int id;
bool bEnabled;
void * driver_struct; /* used internally by driver */
/* standard io functions */
FuncSetting Setting;
FuncSnag SnagPackets;
FuncSend SendPackets;
unsigned char MacAddress[6];
unsigned char MacMask[6];
};
/* The Setting technique is as follows:
unsigned int iSetting =
32 16 0 (bit)
|XXXX XXXX|XXXX XXXX|YYYY YYYY|YYYY YYYY|
X -> General Command Type (number from 0x0 to 0xFFFF)
Y -> Specific Command Type (number from 0x0 to 0xFFFF)
NOTE: Only one Gereral type per command. Specific commands are OR able (for more than one)
*/
enum GeneralCommands
{
maskGeneralCommands = (0xffff<<16),
IfaceDisable = (0x00<<16), IfaceEnable = (0x01<<16), IfaceReset = (0x02<<16),
IfaceQueryState = (0x03<<16)
};
enum SpecificCommands
{
/* for enable/disable/receive: */
maskSpecificCommands = (0xffff),
SpecificallyAll = (1<<0), SpecificallyReceive = (1<<1), SpecificallyTransmit = (1<<2),
SpecificallyStatistics = (1<<3), SpecificallyInterrupts = (1<<4)
};
----- END ETHERNET.H -----
----- BEGIN PACKETRESPONDER.H -----
#ifndef _PRESP_H
#define _PRESP_H
#include <blibc_common.h>
struct etherhdr {
unsigned char ether_dest[6]; /*Destination MAC address. */
unsigned char ether_src[6]; /*Source MAC address. */
unsigned short ether_type; /*Protocol type. */
};
enum etherTypes
{
etherTypeIP = 0x0008, etherTypeARP = 0x0608
};
struct iphdr {
unsigned char ip_hl:4, /*Header length. */
ip_v:4; /*Version. */
unsigned char ip_tos; /*Type of service. */
short ip_len; /*Total length. */
unsigned short ip_id; /*Identification. */
short ip_off; /*Fragment offset field. */
unsigned char ip_ttl; /*Time to live. */
unsigned char ip_p; /*Protocol. */
unsigned short ip_sum; /*Checksum. */
unsigned int ip_src; /*Source IP address. */
unsigned int ip_dest; /*Destination IP address. */
};
enum ipTypes
{
ipTypeICMP = 0x01, ipTypeUDP = 0x11, ipTypeTCP = 0x06
};
struct icmphdr {
unsigned char icmp_type; /*Type of message. */
unsigned char icmp_code; /*Type sub code. */
unsigned short icmp_cksum; /*Ones complement header checksum. */
unsigned short icmp_id; /* identifier */
unsigned short icmp_seq; /* sequence number */
char icmp_data[1]; /* data */
};
struct udphdr {
unsigned short udp_sport; /*Source port */
unsigned short udp_dport; /*Destination port */
unsigned short udp_ulen; /*UDP length */
unsigned short udp_sum; /*UDP checksum */
};
struct tcphdr {
unsigned short tcp_sport; /* Source port. */
unsigned short tcp_dport; /* Destination port. */
unsigned int tcp_seq; /* Sequence number of first octet in this segment. */
unsigned int tcp_ack; /* Expected sequence number of next octet. */
unsigned char tcp_x2:4, /* Unused. */
tcp_off:4; /* Data offset. */
unsigned char tcp_flags; /* Control flags. */
unsigned short tcp_win; /* Number of acceptable octects. */
unsigned short tcp_sum; /* 96 byte pseudo header checksum. */
unsigned short tcp_urp; /* Urgent data pointer. */
};
enum tcpFlags
{
tcpFin = (1<<0), tcpSyn = (1<<1), tcpReset = (1<<2), tcpPush = (1<<3), tcpAck = (1<<4),
tcpUrgent = (1<<5), tcpEcho = (1<<6), tcpCWR = (1<<7)
};
unsigned int createResponse(struct sPacket * pPacket);
#endif
----- END PACKETRESPONDER.H -----
----- BEGIN PACKETRESPONDER.C -----
#include "ethernet.h"
#include "PacketResponder.h"
#define errBufferUnderrun 1
#define errBadEtherType 2
#define errBadIPHeaderLen 3
#define errBadIpType 4
#define errNotReplyable 5
#define errBadIPLen 6
#define errBadTCPHeaderLen 7
/* icmpReplyCodes: Index = incoming request TYPE, Value = Reply value, 0xff = no reply */
unsigned char icmpReplyCodes[256] =
{0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0xff, 0, 10, 0xff, 0xff, 0xff, 14, 0xff, 16, 0xff, 18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 34, 0xff, 36, 0xff, 38, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* <-50xff */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* <-75 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* <-10xff0xff */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* <-125 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* <-150xff */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* <-175 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* <-20xff0xff */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* <-225 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* <-250xff */
0xff, 0xff, 0xff, 0xff, 0xff}; /* <-255 */
unsigned short h2ns(unsigned short ushort)
{
return ((ushort >> 8) & 0xff) | ((ushort << 8) & 0xff00);
}
unsigned int h2nsl(unsigned int uint)
{
return ((uint >> 24) & 0x000000ff) | ((uint >> 8 ) & 0x0000ff00) |
((uint << 8) & 0x00ff0000) | ((uint << 24) & 0xff000000);
}
/* IP Checksum Function
This is used for both the IP header and an ICMP packet */
unsigned short checksum(unsigned short *buffer, unsigned int size)
{
unsigned long cksum=0;
while (size > 1)
{
cksum += *buffer++;
size -= sizeof(unsigned short);
}
if (size)
cksum += *(unsigned char*)buffer;
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
cksum = (~cksum);
cksum = cksum ? cksum : 0xffff;
return (unsigned short)cksum;
}
unsigned short TransChecksum(struct iphdr * ipHeader)
{
unsigned short * buffer;
unsigned int size;
unsigned long cksum;
cksum = 0;
cksum += (ipHeader->ip_src & 0xffff) + ((ipHeader->ip_src>>16) & 0xffff); /* ip_src */
cksum += (ipHeader->ip_dest & 0xffff) + ((ipHeader->ip_dest>>16) & 0xffff); /* ip_dest */
cksum += (unsigned short)((ipHeader->ip_p << 8) & 0xff00);
cksum += h2ns(h2ns(ipHeader->ip_len)-(4 * ipHeader->ip_hl));
size = h2ns(ipHeader->ip_len) - (4 * ipHeader->ip_hl); /* previously checked for sanity */
buffer = (unsigned short *)((unsigned char *)ipHeader + (4 * ipHeader->ip_hl));
while (size > 1)
{
cksum += *buffer++;
size -= sizeof(unsigned short);
}
if (size)
cksum += *(unsigned char*)buffer & 0x00ff;
cksum = (cksum >>16) + (cksum & 0xffff);
cksum += (cksum >>16);
cksum = (~cksum);
cksum = cksum ? cksum : 0xffff;
return (unsigned short)cksum;
}
unsigned int createResponse(struct sPacket * pPacket)
{
/* Header pointers */
struct etherhdr * etherHeader;
struct iphdr * ipHeader;
struct udphdr * udpHeader;
struct icmphdr * icmpHeader;
struct tcphdr * tcpHeader;
/* Length currently left to proccess */
unsigned int iLenUnCasted;
unsigned char * pProcessPoint;
/* Temp variables for creating response */
int i;
unsigned char tempMac[6];
unsigned int tempIP, tempSeq;
unsigned short tempPort;
pProcessPoint = pPacket->pBuf;
iLenUnCasted = pPacket->iLen;
/* Look for ethernet header */
if(iLenUnCasted < sizeof(struct etherhdr))
return errBufferUnderrun;
etherHeader = (struct etherhdr *)pProcessPoint;/* cast it to ethernet header */
pProcessPoint += sizeof(struct etherhdr);
iLenUnCasted -= sizeof(struct etherhdr);
for(i=0;i<6;i++)
{ /* flip source / destination MAC */
tempMac[i] = etherHeader->ether_src[i];
etherHeader->ether_src[i] = etherHeader->ether_dest[i];
etherHeader->ether_dest[i] = tempMac[i];
}
switch(etherHeader->ether_type)
{
case etherTypeIP:
{
/* look for ip headers */
if(iLenUnCasted < sizeof(struct iphdr))
return errBufferUnderrun;
ipHeader = (struct iphdr *)pProcessPoint;/* cast it to ip header */
if((4 * ipHeader->ip_hl) < sizeof(struct iphdr) || (4 * ipHeader->ip_hl) > iLenUnCasted)
return errBadIPHeaderLen;
if(h2ns(ipHeader->ip_len) < sizeof(struct iphdr) || h2ns(ipHeader->ip_len) > iLenUnCasted)
return errBadIPLen;
pProcessPoint += (4 * ipHeader->ip_hl);
iLenUnCasted = h2ns(ipHeader->ip_len) - (4 * ipHeader->ip_hl);
ipHeader->ip_id--; /* just simply decrement to make it different */
ipHeader->ip_off = h2ns(h2ns(ipHeader->ip_off) & (0x3<<13)); /* keep flags same, but zero offset */
tempIP = ipHeader->ip_src; /* flip source / destination IP */
ipHeader->ip_src = ipHeader->ip_dest;
ipHeader->ip_dest = tempIP;
switch(ipHeader->ip_p)
{
case ipTypeICMP:
{
if(iLenUnCasted < sizeof(struct icmphdr))
return errBufferUnderrun;
icmpHeader = (struct icmphdr *)pProcessPoint;/* cast it to icmp header */
pProcessPoint += sizeof(struct icmphdr);
iLenUnCasted -= sizeof(struct icmphdr);
icmpHeader->icmp_type = icmpReplyCodes[icmpHeader->icmp_type];
if(icmpHeader->icmp_type==0xff)
return errNotReplyable;
/* proccess subsequent data (note: icmp header may be larger) */
for(i=0;i<iLenUnCasted;i++)
putch(pProcessPoint[i]);
icmpHeader->icmp_cksum = 0;
icmpHeader->icmp_cksum = checksum((unsigned short *)icmpHeader, iLenUnCasted + sizeof(struct icmphdr));
/* TODO: fix checksum */
break;
}
case ipTypeUDP:
{
if(iLenUnCasted < sizeof(struct udphdr))
return errBufferUnderrun;
udpHeader = (struct udphdr *)pProcessPoint;/* cast it to icmp header */
pProcessPoint += sizeof(struct udphdr);
iLenUnCasted -= sizeof(struct udphdr);
tempPort = udpHeader->udp_sport; /* flip source /destination port */
udpHeader->udp_sport = udpHeader->udp_dport;
udpHeader->udp_dport = tempPort;
/* pass to proccess (if replyable) */
for(i=0;i<iLenUnCasted;i++)
putch(pProcessPoint[i]);
/* fix length then checksum */
udpHeader->udp_sum = 0;
udpHeader->udp_sum = TransChecksum(ipHeader);
break;
}
case ipTypeTCP:
{
if(iLenUnCasted < sizeof(struct tcphdr))
return errBufferUnderrun;
tcpHeader = (struct tcphdr *)pProcessPoint;/* cast it to icmp header */
if((4 * tcpHeader->tcp_off) < sizeof(struct tcphdr) || (4 * tcpHeader->tcp_off) > iLenUnCasted)
return errBadTCPHeaderLen;
pProcessPoint += (4 * tcpHeader->tcp_off);
iLenUnCasted -= (4 * tcpHeader->tcp_off);
/* don't respond to packets that don't need responses. */
if(!iLenUnCasted && !(tcpHeader->tcp_flags & (tcpSyn | tcpFin)) )
return errNotReplyable;
/* flip source /destination port */
tempPort = tcpHeader->tcp_sport;
tcpHeader->tcp_sport = tcpHeader->tcp_dport;
tcpHeader->tcp_dport = tempPort;
/* sequence numbers */
tempSeq = tcpHeader->tcp_seq; /* save their seq */
if(tcpHeader->tcp_flags & tcpSyn) /* this is the first packet */
{
tcpHeader->tcp_seq = h2nsl(h2nsl(tcpHeader->tcp_seq)+0xff); /* use one based on theirs */
}else{ /* just use what they are ack'ing */
tcpHeader->tcp_seq = tcpHeader->tcp_ack;
}
tcpHeader->tcp_flags |= tcpAck; /* we are always ack'ing a packet */
tcpHeader->tcp_ack = h2nsl(h2nsl(tempSeq)+1); /* ack their sequence */
/* proccess options */
for(i=0;i<iLenUnCasted;i++)
putch(pProcessPoint[i]);
/* checksum */
tcpHeader->tcp_sum = 0;
tcpHeader->tcp_sum = TransChecksum(ipHeader);
//pPacket->iFlags &= ~TRANS_SUM_CHECKED;
break;
}
default:
return errBadIpType;
} /* END switch(ipHeader->ip_p) */
/* TODO: fix ip len and checksum */
ipHeader->ip_sum = 0;
ipHeader->ip_sum = checksum((unsigned short *)ipHeader, sizeof(struct iphdr));
break;
}
case etherTypeARP:
{
return 10;
break;
}
default:
printf("BADETHER:0x%x",etherHeader->ether_type);
return errBadEtherType;
} /* END switch(etherHeader->ether_type) */
return 0;
}
----- END PACKETRESPONDER.C -----