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
.tracecompass
.internal
.pcap
.core
.protocol
.ipv4
;
15 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
17 import java
.net
.Inet4Address
;
18 import java
.net
.InetAddress
;
19 import java
.net
.UnknownHostException
;
20 import java
.nio
.ByteBuffer
;
21 import java
.nio
.ByteOrder
;
22 import java
.util
.Arrays
;
25 import org
.eclipse
.jdt
.annotation
.Nullable
;
26 import org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
;
27 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.packet
.BadPacketException
;
28 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.packet
.Packet
;
29 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.protocol
.PcapProtocol
;
30 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.protocol
.tcp
.TCPPacket
;
31 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.protocol
.udp
.UDPPacket
;
32 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.protocol
.unknown
.UnknownPacket
;
33 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.trace
.PcapFile
;
34 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.util
.ConversionHelper
;
35 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.util
.IPProtocolNumberHelper
;
37 import com
.google
.common
.collect
.ImmutableMap
;
38 import com
.google
.common
.collect
.ImmutableMap
.Builder
;
41 * Class that represents an Ethernet II packet.
43 * @author Vincent Perot
45 public class IPv4Packet
extends Packet
{
47 private final @Nullable Packet fChildPacket
;
48 private final @Nullable ByteBuffer fPayload
;
50 private final int fVersion
;
51 private final int fInternetHeaderLength
; // in 4 bytes blocks
52 private final int fDSCP
;
53 private final int fExplicitCongestionNotification
;
54 private final int fTotalLength
; // in bytes
55 private final int fIdentification
;
56 private final boolean fReservedFlag
;
57 private final boolean fDontFragmentFlag
;
58 private final boolean fMoreFragmentFlag
;
59 private final int fFragmentOffset
;
60 private final int fTimeToLive
;
61 private final int fIpDatagramProtocol
;
62 private final int fHeaderChecksum
;
63 private final Inet4Address fSourceIpAddress
;
64 private final Inet4Address fDestinationIpAddress
;
65 private final @Nullable byte[] fOptions
;
67 private @Nullable IPv4Endpoint fSourceEndpoint
;
68 private @Nullable IPv4Endpoint fDestinationEndpoint
;
70 private @Nullable Map
<String
, String
> fFields
;
72 // TODO Interpret options. See
73 // http://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml
76 * Constructor of the IPv4 Packet class.
79 * The file that contains this packet.
81 * The parent packet of this packet (the encapsulating packet).
83 * The entire packet (header and payload).
84 * @throws BadPacketException
85 * Thrown when the packet is erroneous.
87 public IPv4Packet(PcapFile file
, @Nullable Packet parent
, ByteBuffer packet
) throws BadPacketException
{
88 super(file
, parent
, PcapProtocol
.IPV4
);
90 // The endpoints are lazy loaded. They are defined in the get*Endpoint()
92 fSourceEndpoint
= null;
93 fDestinationEndpoint
= null;
97 packet
.order(ByteOrder
.BIG_ENDIAN
);
100 byte storage
= packet
.get();
101 fVersion
= ((storage
& 0xF0) >> 4) & 0x000000FF;
102 fInternetHeaderLength
= storage
& 0x0F;
104 storage
= packet
.get();
105 fDSCP
= ((storage
& 0b11111100
) >> 2) & 0x000000FF;
106 fExplicitCongestionNotification
= storage
& 0b00000011
;
108 fTotalLength
= ConversionHelper
.unsignedShortToInt(packet
.getShort());
109 fIdentification
= ConversionHelper
.unsignedShortToInt(packet
.getShort());
111 storage
= packet
.get();
112 fReservedFlag
= isBitSet(storage
, 7);
113 fDontFragmentFlag
= isBitSet(storage
, 6);
114 fMoreFragmentFlag
= isBitSet(storage
, 5);
115 int msb
= ((storage
& 0b00011111
) << 8);
116 int lsb
= ConversionHelper
.unsignedByteToInt(packet
.get());
117 fFragmentOffset
= msb
+ lsb
;
119 fTimeToLive
= ConversionHelper
.unsignedByteToInt(packet
.get());
120 fIpDatagramProtocol
= ConversionHelper
.unsignedByteToInt(packet
.get());
121 fHeaderChecksum
= ConversionHelper
.unsignedShortToInt(packet
.getShort());
123 byte[] source
= new byte[IPv4Values
.IP_ADDRESS_SIZE
];
124 byte[] destination
= new byte[IPv4Values
.IP_ADDRESS_SIZE
];
126 packet
.get(destination
);
129 fSourceIpAddress
= (Inet4Address
) checkNotNull(InetAddress
.getByAddress(source
));
130 fDestinationIpAddress
= (Inet4Address
) checkNotNull(InetAddress
.getByAddress(destination
));
131 } catch (UnknownHostException e
) {
132 throw new BadPacketException("The IP Address size is not valid!"); //$NON-NLS-1$
135 // Get options if there are any
136 if (fInternetHeaderLength
> IPv4Values
.DEFAULT_HEADER_LENGTH
) {
137 fOptions
= new byte[(fInternetHeaderLength
- IPv4Values
.DEFAULT_HEADER_LENGTH
) * IPv4Values
.BLOCK_SIZE
];
138 packet
.get(fOptions
);
143 // Get payload if any.
144 if (packet
.array().length
- packet
.position() > 0) {
145 byte[] array
= new byte[packet
.array().length
- packet
.position()];
147 ByteBuffer payload
= ByteBuffer
.wrap(array
);
148 payload
.order(ByteOrder
.BIG_ENDIAN
);
156 fChildPacket
= findChildPacket();
161 public @Nullable Packet
getChildPacket() {
166 public @Nullable ByteBuffer
getPayload() {
173 * See http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
176 protected @Nullable Packet
findChildPacket() throws BadPacketException
{
177 // TODO Implement more protocols
178 ByteBuffer payload
= fPayload
;
179 if (payload
== null) {
183 switch (fIpDatagramProtocol
) {
184 case IPProtocolNumberHelper
.PROTOCOL_NUMBER_TCP
:
185 return new TCPPacket(getPcapFile(), this, payload
);
186 case IPProtocolNumberHelper
.PROTOCOL_NUMBER_UDP
:
187 return new UDPPacket(getPcapFile(), this, payload
);
189 return new UnknownPacket(getPcapFile(), this, payload
);
195 public String
toString() {
196 // Generate flagString
197 // This is very ugly.
198 String flagString
= null;
200 if (fReservedFlag
&& fDontFragmentFlag
&& fMoreFragmentFlag
) { // 111
201 flagString
= "Flags: 0x07 (Invalid)"; //$NON-NLS-1$
202 } else if (fReservedFlag
&& fDontFragmentFlag
&& !fMoreFragmentFlag
) { // 110
203 flagString
= "Flags: 0x06 (Invalid)"; //$NON-NLS-1$
204 } else if (fReservedFlag
&& !fDontFragmentFlag
&& fMoreFragmentFlag
) { // 101
205 flagString
= "Flags: 0x05 (Invalid)"; //$NON-NLS-1$
206 } else if (fReservedFlag
&& !fDontFragmentFlag
&& !fMoreFragmentFlag
) { // 100
207 flagString
= "Flags: 0x04 (Invalid)"; //$NON-NLS-1$
208 } else if (!fReservedFlag
&& fDontFragmentFlag
&& fMoreFragmentFlag
) { // 011
209 flagString
= "Flags: 0x03 (Invalid)"; //$NON-NLS-1$
210 } else if (!fReservedFlag
&& fDontFragmentFlag
&& !fMoreFragmentFlag
) { // 010
211 flagString
= "Flags: 0x02 (Don't fragment)"; //$NON-NLS-1$
212 } else if (!fReservedFlag
&& !fDontFragmentFlag
&& fMoreFragmentFlag
) { // 001
213 flagString
= "Flags: 0x01 (More fragments)"; //$NON-NLS-1$
214 } else if (!fReservedFlag
&& !fDontFragmentFlag
&& !fMoreFragmentFlag
) { // 000
215 flagString
= "Flags: 0x00 (Don't have more fragments)"; //$NON-NLS-1$
218 flagString
+= ", Fragment Offset: " + fFragmentOffset
; //$NON-NLS-1$
220 // Generate checksum string
221 // TODO calculate the expected checksum from packet
222 String checksumString
= "Header Checksum: " + String
.format("%s%04x", "0x", fHeaderChecksum
); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
224 String string
= getProtocol().getName() + ", Source: " + fSourceIpAddress
.getHostAddress() + ", Destination: " + fDestinationIpAddress
.getHostAddress() + //$NON-NLS-1$ //$NON-NLS-2$
225 "\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$
226 " 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$
227 + "\n" + flagString
+ "\nTime to live: " + fTimeToLive
+ //$NON-NLS-1$ //$NON-NLS-2$
228 "\nProtocol: " + fIpDatagramProtocol
+ "\n" //$NON-NLS-1$ //$NON-NLS-2$
229 + checksumString
+ "\n"; //$NON-NLS-1$
230 final Packet child
= fChildPacket
;
232 return string
+ child
.toString();
238 * Getter method that returns the version of the IP protocol used. This
239 * should always be set to 4 as IPv6 has its own class.
241 * @return The version of the IP used.
243 public int getVersion() {
248 * Getter method that returns the header length in bytes. In the IPv4
249 * packet, this is specified in 4-bytes data block. By default, this method
250 * returns 20 if there are no options present. Otherwise, it will return a
253 * @return The header length in bytes.
255 public int getHeaderLength() {
256 return fInternetHeaderLength
* IPv4Values
.BLOCK_SIZE
;
260 * Getter method that returns the Differentiated Services Code Point (a.k.a.
261 * the Type of Service). This is useful for some technologies that require
262 * real-time data exchange.
266 public int getDSCP() {
271 * Getter method that returns the Explicit Congestion Notification (ECN).
272 * This allows end-to-end communication without dropping packets.
276 public int getExplicitCongestionNotification() {
277 return fExplicitCongestionNotification
;
281 * Getter method to retrieve the length of the entire packet, in bytes. This
282 * number is according to the packet, and might not be true if the packet is
285 * @return The total length (packet and payload) in bytes.
287 public int getTotalLength() {
292 * Getter method to retrieve the Identification. This is a field that is
293 * used to uniquely identify the packets, thus allowing the reconstruction
294 * of fragmented IP packets.
296 * @return The packet identification.
298 public int getIdentification() {
299 return fIdentification
;
303 * Getter method that returns the state of the Reserved flag. This must
306 * @return The state of the Reserved flag.
308 public boolean getReservedFlag() {
309 return fReservedFlag
;
313 * Getter method that indicates if the packet can be fragmented or not.
315 * @return Whether the packet can be fragmented or not.
317 public boolean getDontFragmentFlag() {
318 return fDontFragmentFlag
;
322 * Getter method that indicates if the packet has more fragments or not.
324 * @return Whether the packet has more fragments or not.
326 public boolean getHasMoreFragment() {
327 return fMoreFragmentFlag
;
331 * Getter method that specify the offset of a particular fragment relative
332 * to the original unfragmented packet, in 8-bytes blocks. *
334 * @return The fragment offset.
336 public int getFragmentOffset() {
337 return fFragmentOffset
;
341 * Getter method that returns the time to live in seconds. In practice, this
342 * is a hop count. This is used to prevent packets from persisting.
344 * @return The time left to live for the packet.
346 public int getTimeToLive() {
351 * Getter method that returns the encapsulated protocol.
353 * See http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
355 * @return The encapsulated protocol.
357 public int getIpDatagramProtocol() {
358 return fIpDatagramProtocol
;
362 * Getter method that returns the checksum, according to the packet. This
363 * checksum might be wrong if the packet is erroneous.
365 * @return The header checksum.
367 public int getHeaderChecksum() {
368 return fHeaderChecksum
;
372 * Getter method that returns the source IP address.
374 * @return The source IP address, as a byte array in big-endian.
376 public Inet4Address
getSourceIpAddress() {
377 return fSourceIpAddress
;
381 * Getter method that returns the destination IP address.
383 * @return The destination IP address, as a byte array in big-endian.
385 public Inet4Address
getDestinationIpAddress() {
386 return fDestinationIpAddress
;
390 * Getter method that returns the options. This method returns null if no
391 * options are present.
393 * @return The options of the packet.
395 public @Nullable byte[] getOptions() {
396 final byte[] options
= fOptions
;
397 if (options
== null) {
400 return Arrays
.copyOf(options
, options
.length
);
404 public boolean validate() {
405 // Not yet implemented. ATM, we consider that all packets are valid.
406 // This is the case for all packets.
407 // TODO Implement it.
412 public IPv4Endpoint
getSourceEndpoint() {
414 IPv4Endpoint endpoint
= fSourceEndpoint
;
415 if (endpoint
== null) {
416 endpoint
= new IPv4Endpoint(this, true);
418 fSourceEndpoint
= endpoint
;
419 return fSourceEndpoint
;
423 public IPv4Endpoint
getDestinationEndpoint() {
425 IPv4Endpoint endpoint
= fDestinationEndpoint
;
427 if (endpoint
== null) {
428 endpoint
= new IPv4Endpoint(this, false);
430 fDestinationEndpoint
= endpoint
;
431 return fDestinationEndpoint
;
435 public Map
<String
, String
> getFields() {
436 Map
<String
, String
> map
= fFields
;
438 Builder
<String
, String
> builder
= ImmutableMap
.<String
, String
> builder()
439 .put("Version", String
.valueOf(fVersion
)) //$NON-NLS-1$
440 .put("Header Length", String
.valueOf(getHeaderLength()) + " bytes") //$NON-NLS-1$ //$NON-NLS-2$
441 .put("Differentiated Services Field", String
.format("%s%02x", "0x", fDSCP
)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
442 .put("Explicit Congestion Notification", String
.format("%s%02x", "0x", fExplicitCongestionNotification
)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
443 .put("Total Length", String
.valueOf(fTotalLength
) + " bytes") //$NON-NLS-1$ //$NON-NLS-2$
444 .put("Identification", String
.format("%s%04x", "0x", fIdentification
)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
445 .put("Don't Fragment Flag", String
.valueOf(fDontFragmentFlag
)) //$NON-NLS-1$
446 .put("More Fragment Flag", String
.valueOf(fMoreFragmentFlag
)) //$NON-NLS-1$
447 .put("Fragment Offset", String
.valueOf(fFragmentOffset
)) //$NON-NLS-1$
448 .put("Time to live", String
.valueOf(fTimeToLive
)) //$NON-NLS-1$
449 .put("Protocol", IPProtocolNumberHelper
.toString(fIpDatagramProtocol
) + " (" + String
.valueOf(fIpDatagramProtocol
) + ")") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
450 .put("Checksum", String
.format("%s%04x", "0x", fHeaderChecksum
)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
451 .put("Source IP Address", fSourceIpAddress
.getHostAddress()) //$NON-NLS-1$
452 .put("Destination IP Address", fDestinationIpAddress
.getHostAddress()); //$NON-NLS-1$
453 byte[] options
= fOptions
;
454 if (options
== null) {
455 builder
.put("Options", EMPTY_STRING
); //$NON-NLS-1$
457 builder
.put("Options", ConversionHelper
.bytesToHex(options
, true)); //$NON-NLS-1$
460 fFields
= checkNotNull(builder
.build());
467 public String
getLocalSummaryString() {
468 return "Src: " + fSourceIpAddress
.getHostAddress() + " , Dst: " + fDestinationIpAddress
.getHostAddress(); //$NON-NLS-1$ //$NON-NLS-2$
472 protected String
getSignificationString() {
473 StringBuilder sb
= new StringBuilder();
474 sb
.append(fSourceIpAddress
.getHostAddress())
475 .append(" > ") //$NON-NLS-1$
476 .append(fDestinationIpAddress
.getHostAddress());
478 String flags
= generateFlagString();
479 if (!(flags
.equals(""))) { //$NON-NLS-1$
485 sb
.append(" Id=") //$NON-NLS-1$
486 .append(fIdentification
);
488 final ByteBuffer payload
= fPayload
;
489 if (payload
!= null) {
490 sb
.append(" Len=") //$NON-NLS-1$
491 .append(payload
.array().length
);
493 sb
.append(" Len=0"); //$NON-NLS-1$
495 return NonNullUtils
.nullToEmptyString(sb
);
498 private String
generateFlagString() {
499 StringBuilder sb
= new StringBuilder();
500 boolean start
= true;
502 if (fDontFragmentFlag
) {
504 sb
.append(", "); //$NON-NLS-1$
506 sb
.append("DF"); //$NON-NLS-1$
509 if (fMoreFragmentFlag
) {
511 sb
.append(", "); //$NON-NLS-1$
513 sb
.append("MF"); //$NON-NLS-1$
516 return NonNullUtils
.nullToEmptyString(sb
);
520 public int hashCode() {
521 final int prime
= 31;
523 final Packet child
= fChildPacket
;
525 result
= prime
* result
+ child
.hashCode();
527 result
= prime
* result
;
529 result
= prime
* result
+ fDSCP
;
530 result
= prime
* result
+ fDestinationIpAddress
.hashCode();
531 result
= prime
* result
+ (fDontFragmentFlag ?
1231 : 1237);
532 result
= prime
* result
+ fExplicitCongestionNotification
;
533 result
= prime
* result
+ fFragmentOffset
;
534 result
= prime
* result
+ fHeaderChecksum
;
535 result
= prime
* result
+ fIdentification
;
536 result
= prime
* result
+ fInternetHeaderLength
;
537 result
= prime
* result
+ fIpDatagramProtocol
;
538 result
= prime
* result
+ (fMoreFragmentFlag ?
1231 : 1237);
539 result
= prime
* result
+ Arrays
.hashCode(fOptions
);
540 final ByteBuffer payload
= fPayload
;
541 if (payload
!= null) {
542 result
= prime
* result
+ payload
.hashCode();
544 result
= prime
* result
;
546 result
= prime
* result
+ (fReservedFlag ?
1231 : 1237);
547 result
= prime
* result
+ fSourceIpAddress
.hashCode();
548 result
= prime
* result
+ fTimeToLive
;
549 result
= prime
* result
+ fTotalLength
;
550 result
= prime
* result
+ fVersion
;
555 public boolean equals(@Nullable Object obj
) {
562 if (getClass() != obj
.getClass()) {
565 IPv4Packet other
= (IPv4Packet
) obj
;
566 if (!NonNullUtils
.equalsNullable(fChildPacket
, other
.fChildPacket
)) {
569 if (fDSCP
!= other
.fDSCP
) {
572 if (!(fDestinationIpAddress
.equals(other
.fDestinationIpAddress
))) {
575 if (fDontFragmentFlag
!= other
.fDontFragmentFlag
) {
578 if (fExplicitCongestionNotification
!= other
.fExplicitCongestionNotification
) {
581 if (fFragmentOffset
!= other
.fFragmentOffset
) {
584 if (fHeaderChecksum
!= other
.fHeaderChecksum
) {
587 if (fIdentification
!= other
.fIdentification
) {
590 if (fInternetHeaderLength
!= other
.fInternetHeaderLength
) {
593 if (fIpDatagramProtocol
!= other
.fIpDatagramProtocol
) {
596 if (fMoreFragmentFlag
!= other
.fMoreFragmentFlag
) {
599 if (!Arrays
.equals(fOptions
, other
.fOptions
)) {
602 if (!NonNullUtils
.equalsNullable(fPayload
, other
.fPayload
)) {
605 if (fReservedFlag
!= other
.fReservedFlag
) {
608 if (!(fSourceIpAddress
.equals(other
.fSourceIpAddress
))) {
611 if (fTimeToLive
!= other
.fTimeToLive
) {
614 if (fTotalLength
!= other
.fTotalLength
) {
617 if (fVersion
!= other
.fVersion
) {