| 1 | // Copyright (c) 2003-2010 Christopher M. Kohlhoff |
| 2 | // Modifications (c) 2011 by Guilherme Maciel Ferreira / Intra2net AG |
| 3 | // |
| 4 | // Distributed under the Boost Software License, Version 1.0. |
| 5 | // (See accompanying file LICENSE_1_0.txt or copy at |
| 6 | // http://www.boost.org/LICENSE_1_0.txt) |
| 7 | #include "ip/ipv4header.h" |
| 8 | |
| 9 | #include <boost/scoped_array.hpp> |
| 10 | |
| 11 | #include <logfunc.hpp> |
| 12 | |
| 13 | using namespace std; |
| 14 | using boost::asio::ip::address_v4; |
| 15 | using boost::asio::ip::address; |
| 16 | using boost::scoped_array; |
| 17 | using I2n::Logger::GlobalLogger; |
| 18 | |
| 19 | //----------------------------------------------------------------------------- |
| 20 | // Ipv4Header |
| 21 | //----------------------------------------------------------------------------- |
| 22 | |
| 23 | static const size_t Ipv4HeaderSizeInBytes_withoutOptions = 20; |
| 24 | |
| 25 | /** |
| 26 | * @brief Default constructor. |
| 27 | */ |
| 28 | Ipv4Header::Ipv4Header() : |
| 29 | IpHeader( Ipv4HeaderSizeInBytes_withoutOptions ) |
| 30 | { |
| 31 | // encode a 4 into the first 4 bits |
| 32 | Payload.encode1(0, 0, 0); |
| 33 | Payload.encode1(0, 1, 1); |
| 34 | Payload.encode1(0, 2, 0); |
| 35 | Payload.encode1(0, 3, 0); |
| 36 | } |
| 37 | |
| 38 | /** |
| 39 | * @brief Get the IP version. |
| 40 | * |
| 41 | * The Version field keeps track of which version of the protocol the |
| 42 | * packet belongs to. |
| 43 | * |
| 44 | * @return The 4-bits representing the Version field. |
| 45 | */ |
| 46 | uint8_t Ipv4Header::get_version() const |
| 47 | { |
| 48 | return ( Payload[ 0 ] >> 4 ) & 0x0F; |
| 49 | } |
| 50 | |
| 51 | /** |
| 52 | * @brief Get the header length. |
| 53 | * |
| 54 | * The Internet Header Length tells the number of 32-bit words in the header. |
| 55 | * Since an IPv4 header may contain a variable number of options, this field |
| 56 | * specifies the size of the header (this also coincides with the offset to |
| 57 | * the data). The minimum value for this field is 5 (RFC 791), which is a |
| 58 | * length of 5×32 = 160 bits = 20 bytes. Being a 4-bit value, the maximum |
| 59 | * length is 15 words (15×32 bits) or 480 bits = 60 bytes. |
| 60 | * |
| 61 | * @return A 16-bits number representing the size in bytes (not words) of |
| 62 | * the header. |
| 63 | */ |
| 64 | uint16_t Ipv4Header::get_header_length() const |
| 65 | { |
| 66 | return static_cast<uint16_t>( (Payload[ 0 ] & 0x0F) * 4 ); |
| 67 | } |
| 68 | |
| 69 | /** |
| 70 | * @brief Get the Differentiated Services, originally called Type of Service. |
| 71 | * |
| 72 | * The Differentiated Services field is used to provide QoS. |
| 73 | * |
| 74 | * @return The 8-bits representing the Differentiated Services. |
| 75 | */ |
| 76 | uint8_t Ipv4Header::get_differentiated_services() const |
| 77 | { |
| 78 | return Payload[ 1 ]; |
| 79 | } |
| 80 | |
| 81 | /** |
| 82 | * @brief Get the packet Total Length in bytes. |
| 83 | * |
| 84 | * The Total Length includes everything in the datagram - both header and data. |
| 85 | * The maximum length is 65,535 bytes. |
| 86 | * |
| 87 | * @return The 16-bits representing the packet's Total Length. |
| 88 | */ |
| 89 | uint16_t Ipv4Header::get_total_length() const |
| 90 | { |
| 91 | return Payload.decode16( 2, 3 ); |
| 92 | } |
| 93 | |
| 94 | /** |
| 95 | * @brief Get the packet's fragment identification. |
| 96 | * |
| 97 | * The Identification field is needed to allow the destination host to |
| 98 | * determine which packet a newly arrived fragment belongs to. All the fragments |
| 99 | * of a packet contain the same Identification value. |
| 100 | * |
| 101 | * @return The 16-bits representing the packet identification number. |
| 102 | */ |
| 103 | uint16_t Ipv4Header::get_identification() const |
| 104 | { |
| 105 | return Payload.decode16( 4, 5 ); |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * @brief Get if the packet was ordered to don't be fragmented. |
| 110 | * |
| 111 | * The DF field (Don't Fragment) is an order to the routers not to fragment the |
| 112 | * packet. It is used as part of the process to discover the path MTU, which is |
| 113 | * the largest packet that can travel along a path without being fragmented. |
| 114 | * |
| 115 | * @return @c true if the packet was ordered to don't fragment, or @c false if |
| 116 | * it could be fragmented. |
| 117 | */ |
| 118 | bool Ipv4Header::dont_fragment() const |
| 119 | { |
| 120 | return ( ( Payload[ 6 ] & 0x40 ) != 0 ); |
| 121 | } |
| 122 | |
| 123 | /** |
| 124 | * @brief Get if this is the last fragment of a packet. |
| 125 | * |
| 126 | * The MF field (More Fragments) is set to all fragments of a packet, except the |
| 127 | * last one. It is needed to know when all fragments of a datagram have arrived. |
| 128 | * |
| 129 | * @return @c false if this is the last fragment of a packet, or @c true if |
| 130 | * there are more fragments of this packet. |
| 131 | */ |
| 132 | bool Ipv4Header::more_fragments() const |
| 133 | { |
| 134 | return ( ( Payload[ 6 ] & 0x20 ) != 0 ); |
| 135 | } |
| 136 | |
| 137 | /** |
| 138 | * @brief Get the packet's fragment offset. |
| 139 | * |
| 140 | * The Fragment Offset field tells where in the current packet this fragment |
| 141 | * belongs. It is measured in units multiple of 8-bytes and specifies the offset |
| 142 | * of a particular fragment relative to the beginning of the original |
| 143 | * unfragmented IP datagram. |
| 144 | * |
| 145 | * @return The 13-bits representing the packet offset. |
| 146 | */ |
| 147 | uint16_t Ipv4Header::get_fragment_offset() const |
| 148 | { |
| 149 | return Payload.decode16( 6, 7 ) & 0x1FFF; |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * @brief Get the time to live of the packet. |
| 154 | * |
| 155 | * The Time to Live field is a counter used to limit packet lifetime. It must be |
| 156 | * decremented on each hop, and when it reaches zero it is discarded. |
| 157 | * |
| 158 | * @return The 8-bit representing the Time to Live field. |
| 159 | */ |
| 160 | uint8_t Ipv4Header::get_time_to_live() const |
| 161 | { |
| 162 | return Payload[ 8 ]; |
| 163 | } |
| 164 | |
| 165 | /** |
| 166 | * @brief Get the identification of the transport protocol. |
| 167 | * |
| 168 | * The Protocol field tells which transport process to give the packet to. |
| 169 | * |
| 170 | * @return The 8-bits number representing the transport protocol. |
| 171 | */ |
| 172 | uint8_t Ipv4Header::get_protocol() const |
| 173 | { |
| 174 | return Payload[ 9 ]; |
| 175 | } |
| 176 | |
| 177 | /** |
| 178 | * @brief Get the header checksum. |
| 179 | * |
| 180 | * The Header Checksum is used to protect the header data. |
| 181 | * |
| 182 | * @brief The 16-bits representing the header checksum. |
| 183 | */ |
| 184 | uint16_t Ipv4Header::get_header_checksum() const |
| 185 | { |
| 186 | return Payload.decode16( 10, 11 ); |
| 187 | } |
| 188 | |
| 189 | /** |
| 190 | * @brief Get the source address. |
| 191 | * |
| 192 | * The Source Address field indicates the source network host address. |
| 193 | * |
| 194 | * @brief The source address. |
| 195 | */ |
| 196 | address Ipv4Header::get_source_address() const |
| 197 | { |
| 198 | uint32_t address = Payload.decode32( 12, 15 ); |
| 199 | |
| 200 | return address_v4( address ); |
| 201 | } |
| 202 | |
| 203 | /** |
| 204 | * @brief Get the destination address. |
| 205 | * |
| 206 | * The Destination Address field indicates the destination network host address. |
| 207 | * |
| 208 | * @return The destination address. |
| 209 | */ |
| 210 | address Ipv4Header::get_destination_address() const |
| 211 | { |
| 212 | uint32_t address = Payload.decode32( 16, 19 ); |
| 213 | |
| 214 | return address_v4( address ); |
| 215 | } |
| 216 | |
| 217 | istream &operator>>( |
| 218 | istream &is, |
| 219 | Ipv4Header &header |
| 220 | ) |
| 221 | { |
| 222 | // read the first 20 bytes (mandatory for IP header) from the input stream |
| 223 | // and stores in the buffer object |
| 224 | (void) header.Payload.read( is ); |
| 225 | |
| 226 | uint8_t header_version = header.get_version(); |
| 227 | if ( header_version != 4 ) |
| 228 | { |
| 229 | GlobalLogger.error() << "Invalid IP header version: " << static_cast<int>(header_version) << endl; |
| 230 | |
| 231 | is.setstate( ios::failbit ); |
| 232 | return is; |
| 233 | } |
| 234 | |
| 235 | // read the consecutive N bytes (for options field) from the input stream |
| 236 | // and stores in the buffer object |
| 237 | streamsize options_length = static_cast<streamsize>( header.get_header_length() ) - |
| 238 | static_cast<streamsize>( Ipv4HeaderSizeInBytes_withoutOptions ); |
| 239 | if ( ( options_length < 0 ) || ( 40 < options_length ) ) |
| 240 | { |
| 241 | GlobalLogger.error() << "Invalid IP options length value:" << options_length << endl; |
| 242 | is.setstate( ios::failbit ); |
| 243 | } |
| 244 | else if ( ( 0 < options_length ) && ( options_length <= 40 ) ) |
| 245 | { |
| 246 | size_t options_size = static_cast<size_t>( options_length ); |
| 247 | scoped_array<uint8_t> options_data( new uint8_t[options_size] ); |
| 248 | memset(options_data.get(), 0, options_size*sizeof(uint8_t)); |
| 249 | char *options_data_array = reinterpret_cast<char *>( options_data.get() ); |
| 250 | |
| 251 | (void) is.read( options_data_array, options_length ); |
| 252 | |
| 253 | header.Payload.append( options_data.get(), options_size ); |
| 254 | } |
| 255 | else // ( options_length == 0 ) |
| 256 | { |
| 257 | //GlobalLogger.info() << "Info: No options available in this packet." << endl; |
| 258 | } |
| 259 | |
| 260 | return is; |
| 261 | } |