Impl
inline unsigned short ip_standard_chksum(void* dataptr, int len) noexcept {
unsigned int acc;
unsigned short src;
unsigned char* octetptr;
acc = 0;
/* dataptr may be at odd or even addresses */
octetptr = (unsigned char*)dataptr;
while (len > 1) {
/* declare first octet as most significant
thus assume network order, ignoring host order */
src = (unsigned short)((*octetptr) << 8);
octetptr++;
/* declare second octet as least significant */
src |= (*octetptr);
octetptr++;
acc += src;
len -= 2;
}
if (len > 0) {
/* accumulate remaining octet */
src = (unsigned short)((*octetptr) << 8);
acc += src;
}
/* add deferred carry bits */
acc = (unsigned int)((acc >> 16) + (acc & 0x0000ffffUL));
if ((acc & 0xffff0000UL) != 0) {
acc = (unsigned int)((acc >> 16) + (acc & 0x0000ffffUL));
}
/* This maybe a little confusing: reorder sum using htons()
instead of ntohs() since it has a little less call overhead.
The caller must invert bits for Internet sum ! */
return ntohs((unsigned short)acc);
}
inline unsigned short inet_chksum(void* dataptr, int len) noexcept {
return (unsigned short)~ip_standard_chksum(dataptr, len);
}
inline unsigned int FOLD_U32T(unsigned int u) noexcept {
return ((unsigned int)(((u) >> 16) + ((u) & 0x0000ffffUL)));
}
inline unsigned int SWAP_BYTES_IN_WORD(unsigned int w) noexcept {
return (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8);
}
inline unsigned short inet_cksum_pseudo_base(unsigned char* payload, unsigned int proto, unsigned int proto_len, unsigned int acc) noexcept {
bool swapped = false;
acc += ip_standard_chksum(payload, (int)proto_len);
acc = FOLD_U32T(acc);
if (proto_len % 2 != 0) {
swapped = !swapped;
acc = SWAP_BYTES_IN_WORD(acc);
}
if (swapped) {
acc = SWAP_BYTES_IN_WORD(acc);
}
acc += htons((unsigned short)proto);
acc += htons((unsigned short)proto_len);
acc = FOLD_U32T(acc);
acc = FOLD_U32T(acc);
return (unsigned short)~(acc & 0xffffUL);
}
inline unsigned short inet_chksum_pseudo(unsigned char* payload, unsigned int proto, unsigned int proto_len, unsigned int src, unsigned int dest) noexcept {
unsigned int acc;
unsigned int addr;
addr = src;
acc = (addr & 0xffff);
acc = (acc + ((addr >> 16) & 0xffff));
addr = dest;
acc = (acc + (addr & 0xffff));
acc = (acc + ((addr >> 16) & 0xffff));
/* fold down to 16 bits */
acc = FOLD_U32T(acc);
acc = FOLD_U32T(acc);
return inet_cksum_pseudo_base(payload, proto, proto_len, acc);
}
C/C++ Header.
#pragma pack(push, 1)
struct ip_hdr {
public:
enum Flags {
IP_RF = 0x8000, /* reserved fragment flag */
IP_DF = 0x4000, /* dont fragment flag */
IP_MF = 0x2000, /* more fragments flag */
IP_OFFMASK = 0x1fff, /* mask for fragmenting bits */
};
public:
/* version / header length / type of service */
unsigned char v_hl;
/* type of service */
unsigned char tos;
/* total length */
unsigned short len;
/* identification */
unsigned short id;
/* fragment offset field */
unsigned short flags;
/* time to live */
unsigned char ttl;
/* protocol */
unsigned char proto;
/* checksum */
unsigned short chksum;
/* source and destination IP addresses */
unsigned int src;
union {
unsigned int dst;
unsigned int dest;
};
public:
static int IPH_V(struct ip_hdr* hdr) noexcept {
return ((hdr)->v_hl >> 4);
}
static int IPH_HL(struct ip_hdr* hdr) noexcept {
return ((hdr)->v_hl & 0x0f);
}
static int IPH_PROTO(struct ip_hdr* hdr) noexcept {
return ((hdr)->proto & 0xff);
}
static int IPH_OFFSET(struct ip_hdr* hdr) noexcept {
return (hdr)->flags;
}
static int IPH_TTL(struct ip_hdr* hdr) noexcept {
return ((hdr)->ttl & 0xff);
}
public:
static int Mtu(int mtu, bool v4_or_v6) noexcept {
static constexpr int MTU_V4_MIN = 68;
static constexpr int MTU_V6_MIN = 1280;
if (mtu > ip_hdr::MTU) {
mtu = ip_hdr::MTU;
}
elif(v4_or_v6) {
if (mtu < MTU_V4_MIN) {
mtu = MTU_V4_MIN;
}
}
elif(mtu < MTU_V6_MIN) {
mtu = MTU_V6_MIN;
}
return mtu;
}
static int Mss(int mtu, bool v4_or_v6) noexcept {
mtu = ip_hdr::Mtu(mtu, v4_or_v6);
if (v4_or_v6) {
return mtu - (ip_hdr::IP_HLEN << 0);
}
else {
return mtu - (ip_hdr::IP_HLEN << 1);
}
}
public:
static struct ip_hdr* Parse(const void* packet, int size) noexcept;
static unsigned short NewId() noexcept;
public:
static const int IP_HLEN;
static const unsigned char IP_DFT_TTL;
public:
static constexpr unsigned char IP_VER = 4;
static constexpr unsigned int IP_ADDR_ANY_VALUE = INADDR_ANY;
static constexpr unsigned int IP_ADDR_BROADCAST_VALUE = INADDR_BROADCAST;
static constexpr int TOS_ROUTIN_MODE = 0;
static constexpr unsigned char IP_PROTO_IP = 0;
static constexpr unsigned char IP_PROTO_ICMP = 1;
static constexpr unsigned char IP_PROTO_UDP = 17;
static constexpr unsigned char IP_PROTO_TCP = 6;
static constexpr int MTU = 1500;
};
#pragma pack(pop)
C/C++ Source.
const int ip_hdr::IP_HLEN = sizeof(struct ip_hdr);
const int tcp_hdr::TCP_HLEN = sizeof(struct tcp_hdr);
const unsigned char ip_hdr::IP_DFT_TTL = Socket::GetDefaultTTL();
unsigned short ip_hdr::NewId() noexcept
{
static std::atomic<unsigned int> aid = ATOMIC_FLAG_INIT;
for (;;)
{
unsigned short r = ++aid;
if (r != 0)
{
return r;
}
}
}
struct ip_hdr* ip_hdr::Parse(const void* packet, int len) noexcept
{
struct ip_hdr* iphdr = (struct ip_hdr*)packet;
if (NULL == iphdr)
{
return NULL;
}
int iphdr_ver = IPH_V(iphdr);
if (iphdr_ver != ip_hdr::IP_VER)
{
return NULL;
}
int iphdr_hlen = IPH_HL(iphdr) << 2;
if (iphdr_hlen > len)
{
return NULL;
}
if (iphdr_hlen < IP_HLEN)
{
return NULL;
}
int ttl = IPH_TTL(iphdr);
if (ttl < 1)
{
return NULL;
}
if (len != ntohs(iphdr->len))
{
return NULL;
}
/* all ones (broadcast) or all zeroes (old skool broadcast) */
if (iphdr->dest == IP_ADDR_ANY_VALUE)
{
return NULL;
}
/* ~iphdr->dest == IP_ADDR_ANY_VALUE */
if (iphdr->src == IP_ADDR_ANY_VALUE || iphdr->src == IP_ADDR_BROADCAST_VALUE)
{
return NULL;
}
// if ((IPH_OFFSET(iphdr) & ntohs((UInt16)(ip_hdr::IP_OFFMASK | ip_hdr::IP_MF))))
// {
// return NULL;
// }
#if defined(PACKET_CHECKSUM)
if (iphdr->chksum != 0)
{
int checksum = inet_chksum(iphdr, iphdr_hlen);
if (checksum != 0)
{
return NULL;
}
}
#endif
int proto = IPH_PROTO(iphdr);
return proto == IP_PROTO_UDP || proto == IP_PROTO_TCP || proto == IP_PROTO_ICMP ? iphdr : NULL;
}