diff --git a/ip.cc b/ip.cc new file mode 100644 index 0000000000..526cfa2533 --- /dev/null +++ b/ip.cc @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 Cloudius Systems, Ltd. + * + */ + +#include "ip.hh" + +namespace net { + +uint16_t ip_checksum(void* data, size_t len) { + uint64_t csum = 0; + auto p64 = reinterpret_cast(data); + while (len >= 8) { + auto old = csum; + csum += ntohq(*p64++); + csum += (csum < old); + len -= 8; + } + auto p16 = reinterpret_cast(p64); + while (len >= 2) { + auto old = csum; + csum += ntohs(*p16++); + csum += (csum < old); + len -= 2; + } + auto p8 = reinterpret_cast(p16); + if (len) { + auto old = csum; + csum += *p8++; + csum += (csum < old); + len -= 1; + } + csum = (csum & 0xffff) + ((csum >> 16) & 0xffff) + ((csum >> 32) & 0xffff) + (csum >> 48); + csum += csum >> 16; + return htons(~csum); +} + +} diff --git a/ip.hh b/ip.hh new file mode 100644 index 0000000000..ed3a8bd281 --- /dev/null +++ b/ip.hh @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014 Cloudius Systems, Ltd. + * + */ + +#ifndef IP_HH_ +#define IP_HH_ + +#include +#include +#include + +namespace net { + +inline uint64_t ntohq(uint64_t v) { + return __builtin_bswap64(v); +} + +uint16_t ip_checksum(void* data, size_t len); + +inline void ntoh() {} +inline void hton() {} + +inline void ntoh(uint16_t& x) { x = ntohs(x); } +inline void hton(uint16_t& x) { x = htons(x); } +inline void ntoh(uint32_t& x) { x = ntohl(x); } +inline void hton(uint32_t& x) { x = htonl(x); } + +// gcc (correctly) won't bind a packed uint16_t to a reference, +// so bypass it by sending a pointer. +// FIXME: this is broken on machines that don't support unaligned +// loads with the normal instructions. +inline void ntoh(uint16_t* x) { *x = ntohs(*x); } +inline void hton(uint16_t* x) { *x = htons(*x); } +inline void ntoh(uint32_t* x) { *x = ntohl(*x); } +inline void hton(uint32_t* x) { *x = htonl(*x); } + +template +inline +void ntoh(First&& first, Rest&&... rest) { + ntoh(*(std::remove_reference_t*)&first); + ntoh(std::forward(rest)...); +} + +template +inline +void hton(First&& first, Rest... rest) { + hton(*(std::remove_reference_t*)&first); + hton(std::forward(rest)...); +} + +template +inline +void ntoh(T& x) { + x.adjust_endianness([] (auto&&... what) { ntoh(std::forward(what)...); }); +} + +template +inline +void hton(T& x) { + x.adjust_endianness([] (auto&&... what) { hton(std::forward(what)...); }); +} + +struct eth_hdr { + std::array dst_mac; + std::array src_mac; + uint16_t eth_proto; + template + auto adjust_endianness(Adjuster a) { + return a(ð_proto); + } +} __attribute__((packed)); + +struct ip_hdr { + uint8_t ihl : 4; + uint8_t ver : 4; + uint8_t dscp : 6; + uint8_t ecn : 2; + uint16_t len; + uint16_t id; + uint16_t frag; + uint8_t ttl; + uint8_t ip_proto; + uint16_t csum; + uint32_t src_ip; + uint32_t dst_ip; + uint8_t options[0]; + template + auto adjust_endianness(Adjuster a) { + return a(&len, &id, &frag, &csum, &src_ip, &dst_ip); + } +} __attribute__((packed)); + +struct icmp_hdr { + uint8_t type; + uint8_t code; + uint16_t csum; + uint32_t rest; + template + auto adjust_endianness(Adjuster a) { + return a(csum); + } +} __attribute__((packed)); + +} + +#endif /* IP_HH_ */