1 /*******************************************************************************
2 * Copyright (c) 2014 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Vincent Perot - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.pcap
.core
.protocol
.ipv4
;
15 import java
.nio
.ByteBuffer
;
16 import java
.nio
.ByteOrder
;
17 import java
.util
.Arrays
;
20 import org
.eclipse
.jdt
.annotation
.NonNull
;
21 import org
.eclipse
.jdt
.annotation
.Nullable
;
22 import org
.eclipse
.linuxtools
.pcap
.core
.packet
.BadPacketException
;
23 import org
.eclipse
.linuxtools
.pcap
.core
.packet
.Packet
;
24 import org
.eclipse
.linuxtools
.pcap
.core
.protocol
.Protocol
;
25 import org
.eclipse
.linuxtools
.pcap
.core
.protocol
.tcp
.TCPPacket
;
26 import org
.eclipse
.linuxtools
.pcap
.core
.protocol
.udp
.UDPPacket
;
27 import org
.eclipse
.linuxtools
.pcap
.core
.protocol
.unknown
.UnknownPacket
;
28 import org
.eclipse
.linuxtools
.pcap
.core
.trace
.PcapFile
;
29 import org
.eclipse
.linuxtools
.pcap
.core
.util
.ConversionHelper
;
30 import org
.eclipse
.linuxtools
.pcap
.core
.util
.IPProtocolNumberHelper
;
32 import com
.google
.common
.collect
.ImmutableMap
;
33 import com
.google
.common
.collect
.ImmutableMap
.Builder
;
36 * Class that represents an Ethernet II packet.
38 * @author Vincent Perot
40 public class IPv4Packet
extends Packet
{
42 private final @Nullable Packet fChildPacket
;
43 private final @Nullable ByteBuffer fPayload
;
45 private final int fVersion
;
46 private final int fInternetHeaderLength
; // in 4 bytes blocks
47 private final int fDSCP
;
48 private final int fExplicitCongestionNotification
;
49 private final int fTotalLength
; // in bytes
50 private final int fIdentification
;
51 private final boolean fReservedFlag
;
52 private final boolean fDontFragmentFlag
;
53 private final boolean fMoreFragmentFlag
;
54 private final int fFragmentOffset
;
55 private final int fTimeToLive
;
56 private final int fIpDatagramProtocol
;
57 private final int fHeaderChecksum
;
58 private final byte[] fSourceIpAddress
;
59 private final byte[] fDestinationIpAddress
;
60 private final @Nullable byte[] fOptions
;
62 private @Nullable IPv4Endpoint fSourceEndpoint
;
63 private @Nullable IPv4Endpoint fDestinationEndpoint
;
65 private @Nullable ImmutableMap
<String
, String
> fFields
;
67 // TODO Interpret options. See
68 // http://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml
71 * Constructor of the IPv4 Packet class.
74 * The file that contains this packet.
76 * The parent packet of this packet (the encapsulating packet).
78 * The entire packet (header and payload).
79 * @throws BadPacketException
80 * Thrown when the packet is erroneous.
82 public IPv4Packet(PcapFile file
, @Nullable Packet parent
, ByteBuffer packet
) throws BadPacketException
{
83 super(file
, parent
, Protocol
.IPV4
);
85 // The endpoints are lazy loaded. They are defined in the get*Endpoint()
87 fSourceEndpoint
= null;
88 fDestinationEndpoint
= null;
92 packet
.order(ByteOrder
.BIG_ENDIAN
);
95 byte storage
= packet
.get();
96 fVersion
= ((storage
& 0xF0) >> 4) & 0x000000FF;
97 fInternetHeaderLength
= storage
& 0x0F;
99 storage
= packet
.get();
100 fDSCP
= ((storage
& 0b11111100
) >> 2) & 0x000000FF;
101 fExplicitCongestionNotification
= storage
& 0b00000011
;
103 fTotalLength
= ConversionHelper
.unsignedShortToInt(packet
.getShort());
104 fIdentification
= ConversionHelper
.unsignedShortToInt(packet
.getShort());
106 storage
= packet
.get();
107 fReservedFlag
= isBitSet(storage
, 7);
108 fDontFragmentFlag
= isBitSet(storage
, 6);
109 fMoreFragmentFlag
= isBitSet(storage
, 5);
110 fFragmentOffset
= ((storage
& 0b00011111
) << 8) | packet
.get();
112 fTimeToLive
= ConversionHelper
.unsignedByteToInt(packet
.get());
113 fIpDatagramProtocol
= ConversionHelper
.unsignedByteToInt(packet
.get());
114 fHeaderChecksum
= ConversionHelper
.unsignedShortToInt(packet
.getShort());
116 fSourceIpAddress
= new byte[IPv4Values
.IP_ADDRESS_SIZE
];
117 fDestinationIpAddress
= new byte[IPv4Values
.IP_ADDRESS_SIZE
];
118 packet
.get(fSourceIpAddress
);
119 packet
.get(fDestinationIpAddress
);
121 // Get options if there are any
122 if (fInternetHeaderLength
> IPv4Values
.DEFAULT_HEADER_LENGTH
) {
123 fOptions
= new byte[(fInternetHeaderLength
- IPv4Values
.DEFAULT_HEADER_LENGTH
) * IPv4Values
.BLOCK_SIZE
];
124 packet
.get(fOptions
);
129 // Get payload if any.
130 if (packet
.array().length
- packet
.position() > 0) {
131 byte[] array
= new byte[packet
.array().length
- packet
.position()];
133 ByteBuffer payload
= ByteBuffer
.wrap(array
);
134 payload
.order(ByteOrder
.BIG_ENDIAN
);
142 fChildPacket
= findChildPacket();
147 public @Nullable Packet
getChildPacket() {
152 public @Nullable ByteBuffer
getPayload() {
159 * See http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
162 protected @Nullable Packet
findChildPacket() throws BadPacketException
{
163 // TODO Implement more protocols
164 ByteBuffer payload
= fPayload
;
165 if (payload
== null) {
169 switch (fIpDatagramProtocol
) {
170 case IPProtocolNumberHelper
.PROTOCOL_NUMBER_TCP
:
171 return new TCPPacket(getPcapFile(), this, payload
);
172 case IPProtocolNumberHelper
.PROTOCOL_NUMBER_UDP
:
173 return new UDPPacket(getPcapFile(), this, payload
);
175 return new UnknownPacket(getPcapFile(), this, payload
);
181 public String
toString() {
182 // Generate flagString
183 // This is very ugly.
184 String flagString
= null;
186 if (fReservedFlag
&& fDontFragmentFlag
&& fMoreFragmentFlag
) { // 111
187 flagString
= "Flags: 0x07 (Invalid)"; //$NON-NLS-1$
188 } else if (fReservedFlag
&& fDontFragmentFlag
&& !fMoreFragmentFlag
) { // 110
189 flagString
= "Flags: 0x06 (Invalid)"; //$NON-NLS-1$
190 } else if (fReservedFlag
&& !fDontFragmentFlag
&& fMoreFragmentFlag
) { // 101
191 flagString
= "Flags: 0x05 (Invalid)"; //$NON-NLS-1$
192 } else if (fReservedFlag
&& !fDontFragmentFlag
&& !fMoreFragmentFlag
) { // 100
193 flagString
= "Flags: 0x04 (Invalid)"; //$NON-NLS-1$
194 } else if (!fReservedFlag
&& fDontFragmentFlag
&& fMoreFragmentFlag
) { // 011
195 flagString
= "Flags: 0x03 (Invalid)"; //$NON-NLS-1$
196 } else if (!fReservedFlag
&& fDontFragmentFlag
&& !fMoreFragmentFlag
) { // 010
197 flagString
= "Flags: 0x02 (Don't fragment)"; //$NON-NLS-1$
198 } else if (!fReservedFlag
&& !fDontFragmentFlag
&& fMoreFragmentFlag
) { // 001
199 flagString
= "Flags: 0x01 (More fragments)"; //$NON-NLS-1$
200 } else if (!fReservedFlag
&& !fDontFragmentFlag
&& !fMoreFragmentFlag
) { // 000
201 flagString
= "Flags: 0x00 (Don't have more fragments)"; //$NON-NLS-1$
204 flagString
+= ", Fragment Offset: " + fFragmentOffset
; //$NON-NLS-1$
206 // Generate checksum string
207 // TODO calculate the expected checksum from packet
208 String checksumString
= "Header Checksum: " + String
.format("%s%04x", "0x", fHeaderChecksum
); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
210 String string
= getProtocol().getName() + ", Source: " + ConversionHelper
.toIpAddress(fSourceIpAddress
) + ", Destination: " + ConversionHelper
.toIpAddress(fDestinationIpAddress
) + //$NON-NLS-1$ //$NON-NLS-2$
211 "\nVersion: " + fVersion
+ ", Identification: " + String
.format("%s%04x", "0x", fIdentification
) + ", Header Length: " + getHeaderLength() + " bytes, Total Length: " + getTotalLength() + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
212 " bytes\nDifferentiated Services Code Point: " + String
.format("%s%02x", "0x", fDSCP
) + "; Explicit Congestion Notification: " + String
.format("%s%02x", "0x", fExplicitCongestionNotification
) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
213 + "\n" + flagString
+ "\nTime to live: " + fTimeToLive
+ //$NON-NLS-1$ //$NON-NLS-2$
214 "\nProtocol: " + fIpDatagramProtocol
+ "\n" //$NON-NLS-1$ //$NON-NLS-2$
215 + checksumString
+ "\n"; //$NON-NLS-1$
216 final Packet child
= fChildPacket
;
218 return string
+ child
.toString();
224 * Getter method that returns the version of the IP protocol used. This
225 * should always be set to 4 as IPv6 has its own class.
227 * @return The version of the IP used.
229 public int getVersion() {
234 * Getter method that returns the header length in bytes. In the IPv4
235 * packet, this is specified in 4-bytes data block. By default, this method
236 * returns 20 if there are no options present. Otherwise, it will return a
239 * @return The header length in bytes.
241 public int getHeaderLength() {
242 return fInternetHeaderLength
* IPv4Values
.BLOCK_SIZE
;
246 * Getter method that returns the Differentiated Services Code Point (a.k.a.
247 * the Type of Service). This is useful for some technologies that require
248 * real-time data exchange.
252 public int getDSCP() {
257 * Getter method that returns the Explicit Congestion Notification (ECN).
258 * This allows end-to-end communication without dropping packets.
262 public int getExplicitCongestionNotification() {
263 return fExplicitCongestionNotification
;
267 * Getter method to retrieve the length of the entire packet, in bytes. This
268 * number is according to the packet, and might not be true if the packet is
271 * @return The total length (packet and payload) in bytes.
273 public int getTotalLength() {
278 * Getter method to retrieve the Identification. This is a field that is
279 * used to uniquely identify the packets, thus allowing the reconstruction
280 * of fragmented IP packets.
282 * @return The packet identification.
284 public int getIdentification() {
285 return fIdentification
;
289 * Getter method that returns the state of the Reserved flag. This must
292 * @return The state of the Reserved flag.
294 public boolean getReservedFlag() {
295 return fReservedFlag
;
299 * Getter method that indicates if the packet can be fragmented or not.
301 * @return Whether the packet can be fragmented or not.
303 public boolean getDontFragmentFlag() {
304 return fDontFragmentFlag
;
308 * Getter method that indicates if the packet has more fragments or not.
310 * @return Whether the packet has more fragments or not.
312 public boolean getHasMoreFragment() {
313 return fMoreFragmentFlag
;
317 * Getter method that specify the offset of a particular fragment relative
318 * to the original unfragmented packet, in 8-bytes blocks. *
320 * @return The fragment offset.
322 public int getFragmentOffset() {
323 return fFragmentOffset
;
327 * Getter method that returns the time to live in seconds. In practice, this
328 * is a hop count. This is used to prevent packets from persisting.
330 * @return The time left to live for the packet.
332 public int getTimeToLive() {
337 * Getter method that returns the encapsulated protocol.
339 * See http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
341 * @return The encapsulated protocol.
343 public int getIpDatagramProtocol() {
344 return fIpDatagramProtocol
;
348 * Getter method that returns the checksum, according to the packet. This
349 * checksum might be wrong if the packet is erroneous.
351 * @return The header checksum.
353 public int getHeaderChecksum() {
354 return fHeaderChecksum
;
358 * Getter method that returns the source IP address.
360 * @return The source IP address, as a byte array in big-endian.
362 public byte[] getSourceIpAddress() {
363 return fSourceIpAddress
;
367 * Getter method that returns the destination IP address.
369 * @return The destination IP address, as a byte array in big-endian.
371 public byte[] getDestinationIpAddress() {
372 return fDestinationIpAddress
;
376 * Getter method that returns the options. This method returns null if no
377 * options are present.
379 * @return The options of the packet.
381 public @Nullable byte[] getOptions() {
382 final byte[] options
= fOptions
;
383 if (options
== null) {
386 return Arrays
.copyOf(options
, options
.length
);
390 public boolean validate() {
391 // Not yet implemented. ATM, we consider that all packets are valid.
392 // This is the case for all packets.
393 // TODO Implement it.
398 public IPv4Endpoint
getSourceEndpoint() {
400 IPv4Endpoint endpoint
= fSourceEndpoint
;
401 if (endpoint
== null) {
402 endpoint
= new IPv4Endpoint(this, true);
404 fSourceEndpoint
= endpoint
;
405 return fSourceEndpoint
;
409 public IPv4Endpoint
getDestinationEndpoint() {
411 IPv4Endpoint endpoint
= fDestinationEndpoint
;
413 if (endpoint
== null) {
414 endpoint
= new IPv4Endpoint(this, false);
416 fDestinationEndpoint
= endpoint
;
417 return fDestinationEndpoint
;
421 public Map
<String
, String
> getFields() {
422 ImmutableMap
<String
, String
> map
= fFields
;
424 Builder
<String
, String
> builder
= ImmutableMap
.<String
, String
> builder()
425 .put("Version", String
.valueOf(fVersion
)) //$NON-NLS-1$
426 .put("Header Length", String
.valueOf(getHeaderLength()) + " bytes") //$NON-NLS-1$ //$NON-NLS-2$
427 .put("Differentiated Services Field", String
.format("%s%02x", "0x", fDSCP
)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
428 .put("Explicit Congestion Notification", String
.format("%s%02x", "0x", fExplicitCongestionNotification
)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
429 .put("Total Length", String
.valueOf(fTotalLength
) + " bytes") //$NON-NLS-1$ //$NON-NLS-2$
430 .put("Identification", String
.format("%s%04x", "0x", fIdentification
)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
431 .put("Don't Fragment Flag", String
.valueOf(fDontFragmentFlag
)) //$NON-NLS-1$
432 .put("More Fragment Flag", String
.valueOf(fMoreFragmentFlag
)) //$NON-NLS-1$
433 .put("Fragment Offset", String
.valueOf(fFragmentOffset
)) //$NON-NLS-1$
434 .put("Time to live", String
.valueOf(fTimeToLive
)) //$NON-NLS-1$
435 .put("Protocol", IPProtocolNumberHelper
.toString(fIpDatagramProtocol
) + " (" + String
.valueOf(fIpDatagramProtocol
) + ")") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
436 .put("Checksum", String
.format("%s%04x", "0x", fHeaderChecksum
)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
437 .put("Source IP Address", ConversionHelper
.toIpAddress(fSourceIpAddress
)) //$NON-NLS-1$
438 .put("Destination IP Address", ConversionHelper
.toIpAddress(fDestinationIpAddress
)); //$NON-NLS-1$
439 byte[] options
= fOptions
;
440 if (options
== null) {
441 builder
.put("Options", EMPTY_STRING
); //$NON-NLS-1$
443 builder
.put("Options", ConversionHelper
.bytesToHex(options
, true)); //$NON-NLS-1$
446 @SuppressWarnings("null")
448 ImmutableMap
<String
, String
> newMap
= builder
.build();
456 public String
getLocalSummaryString() {
457 return "Src: " + ConversionHelper
.toIpAddress(fSourceIpAddress
) + " , Dst: " + ConversionHelper
.toIpAddress(fDestinationIpAddress
); //$NON-NLS-1$ //$NON-NLS-2$
461 protected String
getSignificationString() {
462 StringBuilder sb
= new StringBuilder();
463 sb
.append(ConversionHelper
.toIpAddress(fSourceIpAddress
))
464 .append(" > ") //$NON-NLS-1$
465 .append(ConversionHelper
.toIpAddress(fDestinationIpAddress
));
467 String flags
= generateFlagString();
468 if (!(flags
.equals(""))) { //$NON-NLS-1$
474 sb
.append(" Id=") //$NON-NLS-1$
475 .append(fIdentification
);
477 final ByteBuffer payload
= fPayload
;
478 if (payload
!= null) {
479 sb
.append(" Len=") //$NON-NLS-1$
480 .append(payload
.array().length
);
482 sb
.append(" Len=0"); //$NON-NLS-1$
484 String string
= sb
.toString();
485 if (string
== null) {
491 private String
generateFlagString() {
492 StringBuilder sb
= new StringBuilder();
493 boolean start
= true;
495 if (fDontFragmentFlag
) {
497 sb
.append(", "); //$NON-NLS-1$
499 sb
.append("DF"); //$NON-NLS-1$
502 if (fMoreFragmentFlag
) {
504 sb
.append(", "); //$NON-NLS-1$
506 sb
.append("MF"); //$NON-NLS-1$
509 String string
= sb
.toString();
510 if (string
== null) {
517 public int hashCode() {
518 final int prime
= 31;
520 final Packet child
= fChildPacket
;
522 result
= prime
* result
+ child
.hashCode();
524 result
= prime
* result
;
526 result
= prime
* result
+ fDSCP
;
527 result
= prime
* result
+ Arrays
.hashCode(fDestinationIpAddress
);
528 result
= prime
* result
+ (fDontFragmentFlag ?
1231 : 1237);
529 result
= prime
* result
+ fExplicitCongestionNotification
;
530 result
= prime
* result
+ fFragmentOffset
;
531 result
= prime
* result
+ fHeaderChecksum
;
532 result
= prime
* result
+ fIdentification
;
533 result
= prime
* result
+ fInternetHeaderLength
;
534 result
= prime
* result
+ fIpDatagramProtocol
;
535 result
= prime
* result
+ (fMoreFragmentFlag ?
1231 : 1237);
536 result
= prime
* result
+ Arrays
.hashCode(fOptions
);
537 final ByteBuffer payload
= fPayload
;
538 if (payload
!= null) {
539 result
= prime
* result
+ payload
.hashCode();
541 result
= prime
* result
;
543 result
= prime
* result
+ (fReservedFlag ?
1231 : 1237);
544 result
= prime
* result
+ Arrays
.hashCode(fSourceIpAddress
);
545 result
= prime
* result
+ fTimeToLive
;
546 result
= prime
* result
+ fTotalLength
;
547 result
= prime
* result
+ fVersion
;
552 public boolean equals(@Nullable Object obj
) {
559 if (getClass() != obj
.getClass()) {
562 IPv4Packet other
= (IPv4Packet
) obj
;
563 final Packet child
= fChildPacket
;
565 if (!child
.equals(other
.fChildPacket
)) {
569 if (other
.fChildPacket
!= null) {
574 if (fDSCP
!= other
.fDSCP
) {
577 if (!Arrays
.equals(fDestinationIpAddress
, other
.fDestinationIpAddress
)) {
580 if (fDontFragmentFlag
!= other
.fDontFragmentFlag
) {
583 if (fExplicitCongestionNotification
!= other
.fExplicitCongestionNotification
) {
586 if (fFragmentOffset
!= other
.fFragmentOffset
) {
589 if (fHeaderChecksum
!= other
.fHeaderChecksum
) {
592 if (fIdentification
!= other
.fIdentification
) {
595 if (fInternetHeaderLength
!= other
.fInternetHeaderLength
) {
598 if (fIpDatagramProtocol
!= other
.fIpDatagramProtocol
) {
601 if (fMoreFragmentFlag
!= other
.fMoreFragmentFlag
) {
604 if (!Arrays
.equals(fOptions
, other
.fOptions
)) {
607 final ByteBuffer payload
= fPayload
;
608 if (payload
!= null) {
609 if (!payload
.equals(other
.fPayload
)) {
613 if (other
.fPayload
!= null) {
617 if (fReservedFlag
!= other
.fReservedFlag
) {
620 if (!Arrays
.equals(fSourceIpAddress
, other
.fSourceIpAddress
)) {
623 if (fTimeToLive
!= other
.fTimeToLive
) {
626 if (fTotalLength
!= other
.fTotalLength
) {
629 if (fVersion
!= other
.fVersion
) {