1 /* 2 * Copyright (C) 2010-2013 Codership Oy <info@codership.com> 3 */ 4 5 #ifndef GCOMM_DATAGRAM_HPP 6 #define GCOMM_DATAGRAM_HPP 7 8 #include "gu_buffer.hpp" 9 #include "gu_serialize.hpp" 10 #include "gu_utils.hpp" 11 #include "gu_throw.hpp" 12 13 #include <limits> 14 #include <cassert> 15 16 #include <cstring> 17 #include <stdint.h> 18 19 namespace gcomm 20 { 21 22 //! 23 // @class NetHeader 24 // 25 // @brief Header for datagrams sent over network 26 // 27 // Header structure is the following (MSB first) 28 // 29 // | version(4) | reserved(2) | F_CRC(2) | len(24) | 30 // | CRC(32) | 31 // 32 class NetHeader 33 { 34 public: 35 36 typedef enum checksum 37 { 38 CS_NONE = 0, 39 CS_CRC32, 40 CS_CRC32C 41 } checksum_t; 42 43 static checksum_t checksum_type (int i); 44 NetHeader()45 NetHeader() 46 : 47 len_(), 48 crc32_() 49 { } 50 NetHeader(uint32_t len,int version)51 NetHeader(uint32_t len, int version) 52 : 53 len_(len), 54 crc32_(0) 55 { 56 if (len > len_mask_) 57 gu_throw_error(EINVAL) << "msg too long " << len_; 58 len_ |= (static_cast<uint32_t>(version) << version_shift_); 59 } 60 len() const61 uint32_t len() const { return (len_ & len_mask_); } 62 set_crc32(uint32_t crc32,checksum_t type)63 void set_crc32(uint32_t crc32, checksum_t type) 64 { 65 assert (CS_CRC32 == type || CS_CRC32C == type); 66 crc32_ = crc32; 67 CS_CRC32 == type ? len_ |= F_CRC32 : len_ |= F_CRC32C; 68 } 69 has_crc32() const70 bool has_crc32() const { return (len_ & F_CRC32); } has_crc32c() const71 bool has_crc32c() const { return (len_ & F_CRC32C); } 72 crc32() const73 uint32_t crc32() const { return crc32_; } 74 version() const75 int version() const 76 { 77 return ((len_ & version_mask_) >> version_shift_); 78 } 79 80 friend size_t serialize(const NetHeader& hdr, gu::byte_t* buf, 81 size_t buflen, size_t offset); 82 83 friend size_t unserialize(const gu::byte_t* buf, size_t buflen, 84 size_t offset, NetHeader& hdr); 85 86 friend size_t serial_size(const NetHeader& hdr); 87 88 static const size_t serial_size_ = 8; 89 90 private: 91 92 static const uint32_t len_mask_ = 0x00ffffff; 93 static const uint32_t flags_mask_ = 0x0f000000; 94 static const uint32_t flags_shift_ = 24; 95 static const uint32_t version_mask_ = 0xf0000000; 96 static const uint32_t version_shift_ = 28; 97 98 static const uint32_t F_CRC32 = 1 << 24; /* backward compatible */ 99 static const uint32_t F_CRC32C = 1 << 25; 100 101 uint32_t len_; 102 uint32_t crc32_; 103 }; 104 serialize(const NetHeader & hdr,gu::byte_t * buf,size_t buflen,size_t offset)105 inline size_t serialize(const NetHeader& hdr, gu::byte_t* buf, 106 size_t buflen, size_t offset) 107 { 108 offset = gu::serialize4(hdr.len_, buf, buflen, offset); 109 offset = gu::serialize4(hdr.crc32_, buf, buflen, offset); 110 return offset; 111 } 112 unserialize(const gu::byte_t * buf,size_t buflen,size_t offset,NetHeader & hdr)113 inline size_t unserialize(const gu::byte_t* buf, size_t buflen, 114 size_t offset, NetHeader& hdr) 115 { 116 offset = gu::unserialize4(buf, buflen, offset, hdr.len_); 117 offset = gu::unserialize4(buf, buflen, offset, hdr.crc32_); 118 119 switch (hdr.version()) 120 { 121 case 0: 122 if ((hdr.len_ & NetHeader::flags_mask_) & 123 ~(NetHeader::F_CRC32 | NetHeader::F_CRC32C)) 124 { 125 gu_throw_error(EPROTO) 126 << "invalid flags " 127 << ((hdr.len_ & NetHeader::flags_mask_) >> 128 NetHeader::flags_shift_); 129 } 130 break; 131 default: 132 gu_throw_error(EPROTO) << "invalid protocol version " 133 << hdr.version(); 134 } 135 136 return offset; 137 } 138 serial_size(const NetHeader & hdr)139 inline size_t serial_size(const NetHeader& hdr) 140 { 141 return NetHeader::serial_size_; 142 } 143 144 /*! 145 * @brief Datagram container 146 * 147 * Datagram class provides consistent interface for managing 148 * datagrams/byte buffers. 149 */ 150 class Datagram 151 { 152 public: Datagram()153 Datagram() 154 : 155 header_ (), 156 header_offset_(header_size_), 157 payload_ (new gu::Buffer()), 158 offset_ (0) 159 { } 160 /*! 161 * @brief Construct new datagram from byte buffer 162 * 163 * @param[in] buf Const pointer to data buffer 164 * @param[in] buflen Length of data buffer 165 * 166 * @throws std::bad_alloc 167 */ 168 Datagram(const gu::Buffer & buf,size_t offset=0)169 Datagram(const gu::Buffer& buf, size_t offset = 0) 170 : 171 header_ (), 172 header_offset_(header_size_), 173 payload_ (new gu::Buffer(buf)), 174 offset_ (offset) 175 { 176 assert(offset_ <= payload_->size()); 177 } 178 Datagram(const gu::SharedBuffer & buf,size_t offset=0)179 Datagram(const gu::SharedBuffer& buf, size_t offset = 0) 180 : 181 header_ (), 182 header_offset_(header_size_), 183 payload_ (buf), 184 offset_ (offset) 185 { 186 assert(offset_ <= payload_->size()); 187 } 188 189 /*! 190 * @brief Copy constructor. 191 * 192 * @note Only for normalized datagrams. 193 * 194 * @param[in] dgram Datagram to make copy from 195 * @param[in] off 196 * @throws std::bad_alloc 197 */ Datagram(const Datagram & dgram,size_t off=std::numeric_limits<size_t>::max ())198 Datagram(const Datagram& dgram, 199 size_t off = std::numeric_limits<size_t>::max()) : 200 // header_(dgram.header_), 201 header_offset_(dgram.header_offset_), 202 payload_(dgram.payload_), 203 offset_(off == std::numeric_limits<size_t>::max() ? dgram.offset_ : off) 204 { 205 assert(offset_ <= dgram.len()); 206 memcpy(header_ + header_offset_, 207 dgram.header_ + dgram.header_offset(), 208 dgram.header_len()); 209 } 210 211 /*! 212 * @brief Destruct datagram 213 */ ~Datagram()214 ~Datagram() { } 215 normalize()216 void normalize() 217 { 218 const gu::SharedBuffer old_payload(payload_); 219 payload_ = gu::SharedBuffer(new gu::Buffer); 220 payload_->reserve(header_len() + old_payload->size() - offset_); 221 222 if (header_len() > offset_) 223 { 224 payload_->insert(payload_->end(), 225 header_ + header_offset_ + offset_, 226 header_ + header_size_); 227 offset_ = 0; 228 } 229 else 230 { 231 offset_ -= header_len(); 232 } 233 header_offset_ = header_size_; 234 payload_->insert(payload_->end(), 235 old_payload->begin() 236 + gu::Buffer::difference_type(offset_), 237 old_payload->end()); 238 offset_ = 0; 239 } 240 header()241 gu::byte_t* header() { return header_; } header() const242 const gu::byte_t* header() const { return header_; } header_size() const243 size_t header_size() const { return header_size_; } header_len() const244 size_t header_len() const { return (header_size_ - header_offset_); } header_offset() const245 size_t header_offset() const { return header_offset_; } 246 set_header_offset(const size_t off)247 void set_header_offset(const size_t off) 248 { 249 // assert(off <= header_size_); 250 if (off > header_size_) gu_throw_fatal << "out of hdrspace"; 251 header_offset_ = off; 252 } 253 payload() const254 const gu::Buffer& payload() const 255 { 256 assert(payload_); 257 return *payload_; 258 } 259 payload()260 gu::Buffer& payload() 261 { 262 assert(payload_); 263 return *payload_; 264 } 265 len() const266 size_t len() const 267 { 268 return (header_size_ - header_offset_ + payload_->size()); 269 } 270 offset() const271 size_t offset() const { return offset_; } 272 273 private: 274 275 friend uint16_t crc16(const Datagram&, size_t); 276 friend uint32_t crc32(NetHeader::checksum_t, const Datagram&, size_t); 277 278 static const size_t header_size_ = 128; 279 gu::byte_t header_[header_size_]; 280 size_t header_offset_; 281 gu::SharedBuffer payload_; 282 size_t offset_; 283 }; 284 285 uint16_t crc16(const Datagram& dg, size_t offset = 0); 286 uint32_t crc32(NetHeader::checksum_t type, const Datagram& dg, 287 size_t offset = 0); 288 289 /* returns true if checksum fails */ check_cs(const NetHeader & hdr,const Datagram & dg)290 inline bool check_cs (const NetHeader& hdr, const Datagram& dg) 291 { 292 if (hdr.has_crc32c()) 293 return (crc32(NetHeader::CS_CRC32C, dg) != hdr.crc32()); 294 295 if (hdr.has_crc32()) 296 return (crc32(NetHeader::CS_CRC32, dg) != hdr.crc32()); 297 298 return (hdr.crc32() != 0); 299 } 300 } /* namespace gcomm */ 301 302 #endif // GCOMM_DATAGRAM_HPP 303