6fb6eb4ebb5e6d1acb6f867bae8a9f68e60c03de
[pingcheck] / src / ip / ipv4header.cpp
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 }