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
.internal
.pcap
.core
.protocol
.ipv4
;
15 import java
.net
.Inet4Address
;
16 import java
.net
.InetAddress
;
17 import java
.net
.UnknownHostException
;
18 import java
.nio
.ByteBuffer
;
19 import java
.nio
.ByteOrder
;
20 import java
.util
.Arrays
;
23 import org
.eclipse
.jdt
.annotation
.NonNull
;
24 import org
.eclipse
.jdt
.annotation
.Nullable
;
25 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.packet
.BadPacketException
;
26 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.packet
.Packet
;
27 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.protocol
.PcapProtocol
;
28 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.protocol
.tcp
.TCPPacket
;
29 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.protocol
.udp
.UDPPacket
;
30 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.protocol
.unknown
.UnknownPacket
;
31 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.trace
.PcapFile
;
32 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.util
.ConversionHelper
;
33 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.util
.IPProtocolNumberHelper
;
35 import com
.google
.common
.collect
.ImmutableMap
;
36 import com
.google
.common
.collect
.ImmutableMap
.Builder
;
39 * Class that represents an Ethernet II packet.
41 * @author Vincent Perot
43 public class IPv4Packet
extends Packet
{
45 private final @Nullable Packet fChildPacket
;
46 private final @Nullable ByteBuffer fPayload
;
48 private final int fVersion
;
49 private final int fInternetHeaderLength
; // in 4 bytes blocks
50 private final int fDSCP
;
51 private final int fExplicitCongestionNotification
;
52 private final int fTotalLength
; // in bytes
53 private final int fIdentification
;
54 private final boolean fReservedFlag
;
55 private final boolean fDontFragmentFlag
;
56 private final boolean fMoreFragmentFlag
;
57 private final int fFragmentOffset
;
58 private final int fTimeToLive
;
59 private final int fIpDatagramProtocol
;
60 private final int fHeaderChecksum
;
61 private final Inet4Address fSourceIpAddress
;
62 private final Inet4Address fDestinationIpAddress
;
63 private final @Nullable byte[] fOptions
;
65 private @Nullable IPv4Endpoint fSourceEndpoint
;
66 private @Nullable IPv4Endpoint fDestinationEndpoint
;
68 private @Nullable ImmutableMap
<String
, String
> fFields
;
70 // TODO Interpret options. See
71 // http://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml
74 * Constructor of the IPv4 Packet class.
77 * The file that contains this packet.
79 * The parent packet of this packet (the encapsulating packet).
81 * The entire packet (header and payload).
82 * @throws BadPacketException
83 * Thrown when the packet is erroneous.
85 public IPv4Packet(PcapFile file
, @Nullable Packet parent
, ByteBuffer packet
) throws BadPacketException
{
86 super(file
, parent
, PcapProtocol
.IPV4
);
88 // The endpoints are lazy loaded. They are defined in the get*Endpoint()
90 fSourceEndpoint
= null;
91 fDestinationEndpoint
= null;
95 packet
.order(ByteOrder
.BIG_ENDIAN
);
98 byte storage
= packet
.get();
99 fVersion
= ((storage
& 0xF0) >> 4) & 0x000000FF;
100 fInternetHeaderLength
= storage
& 0x0F;
102 storage
= packet
.get();
103 fDSCP
= ((storage
& 0b11111100
) >> 2) & 0x000000FF;
104 fExplicitCongestionNotification
= storage
& 0b00000011
;
106 fTotalLength
= ConversionHelper
.unsignedShortToInt(packet
.getShort());
107 fIdentification
= ConversionHelper
.unsignedShortToInt(packet
.getShort());
109 storage
= packet
.get();
110 fReservedFlag
= isBitSet(storage
, 7);
111 fDontFragmentFlag
= isBitSet(storage
, 6);
112 fMoreFragmentFlag
= isBitSet(storage
, 5);
113 int msb
= ((storage
& 0b00011111
) << 8);
114 int lsb
= ConversionHelper
.unsignedByteToInt(packet
.get());
115 fFragmentOffset
= msb
+ lsb
;
117 fTimeToLive
= ConversionHelper
.unsignedByteToInt(packet
.get());
118 fIpDatagramProtocol
= ConversionHelper
.unsignedByteToInt(packet
.get());
119 fHeaderChecksum
= ConversionHelper
.unsignedShortToInt(packet
.getShort());
121 byte[] source
= new byte[IPv4Values
.IP_ADDRESS_SIZE
];
122 byte[] destination
= new byte[IPv4Values
.IP_ADDRESS_SIZE
];
124 packet
.get(destination
);
127 @SuppressWarnings("null")
128 @NonNull Inet4Address sourceIP
= (Inet4Address
) InetAddress
.getByAddress(source
);
129 @SuppressWarnings("null")
130 @NonNull Inet4Address destinationIP
= (Inet4Address
) InetAddress
.getByAddress(destination
);
131 fSourceIpAddress
= sourceIP
;
132 fDestinationIpAddress
= destinationIP
;
133 } catch (UnknownHostException e
) {
134 throw new BadPacketException("The IP Address size is not valid!"); //$NON-NLS-1$
137 // Get options if there are any
138 if (fInternetHeaderLength
> IPv4Values
.DEFAULT_HEADER_LENGTH
) {
139 fOptions
= new byte[(fInternetHeaderLength
- IPv4Values
.DEFAULT_HEADER_LENGTH
) * IPv4Values
.BLOCK_SIZE
];
140 packet
.get(fOptions
);
145 // Get payload if any.
146 if (packet
.array().length
- packet
.position() > 0) {
147 byte[] array
= new byte[packet
.array().length
- packet
.position()];
149 ByteBuffer payload
= ByteBuffer
.wrap(array
);
150 payload
.order(ByteOrder
.BIG_ENDIAN
);
158 fChildPacket
= findChildPacket();
163 public @Nullable Packet
getChildPacket() {
168 public @Nullable ByteBuffer
getPayload() {
175 * See http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
178 protected @Nullable Packet
findChildPacket() throws BadPacketException
{
179 // TODO Implement more protocols
180 ByteBuffer payload
= fPayload
;
181 if (payload
== null) {
185 switch (fIpDatagramProtocol
) {
186 case IPProtocolNumberHelper
.PROTOCOL_NUMBER_TCP
:
187 return new TCPPacket(getPcapFile(), this, payload
);
188 case IPProtocolNumberHelper
.PROTOCOL_NUMBER_UDP
:
189 return new UDPPacket(getPcapFile(), this, payload
);
191 return new UnknownPacket(getPcapFile(), this, payload
);
197 public String
toString() {
198 // Generate flagString
199 // This is very ugly.
200 String flagString
= null;
202 if (fReservedFlag
&& fDontFragmentFlag
&& fMoreFragmentFlag
) { // 111
203 flagString
= "Flags: 0x07 (Invalid)"; //$NON-NLS-1$
204 } else if (fReservedFlag
&& fDontFragmentFlag
&& !fMoreFragmentFlag
) { // 110
205 flagString
= "Flags: 0x06 (Invalid)"; //$NON-NLS-1$
206 } else if (fReservedFlag
&& !fDontFragmentFlag
&& fMoreFragmentFlag
) { // 101
207 flagString
= "Flags: 0x05 (Invalid)"; //$NON-NLS-1$
208 } else if (fReservedFlag
&& !fDontFragmentFlag
&& !fMoreFragmentFlag
) { // 100
209 flagString
= "Flags: 0x04 (Invalid)"; //$NON-NLS-1$
210 } else if (!fReservedFlag
&& fDontFragmentFlag
&& fMoreFragmentFlag
) { // 011
211 flagString
= "Flags: 0x03 (Invalid)"; //$NON-NLS-1$
212 } else if (!fReservedFlag
&& fDontFragmentFlag
&& !fMoreFragmentFlag
) { // 010
213 flagString
= "Flags: 0x02 (Don't fragment)"; //$NON-NLS-1$
214 } else if (!fReservedFlag
&& !fDontFragmentFlag
&& fMoreFragmentFlag
) { // 001
215 flagString
= "Flags: 0x01 (More fragments)"; //$NON-NLS-1$
216 } else if (!fReservedFlag
&& !fDontFragmentFlag
&& !fMoreFragmentFlag
) { // 000
217 flagString
= "Flags: 0x00 (Don't have more fragments)"; //$NON-NLS-1$
220 flagString
+= ", Fragment Offset: " + fFragmentOffset
; //$NON-NLS-1$
222 // Generate checksum string
223 // TODO calculate the expected checksum from packet
224 String checksumString
= "Header Checksum: " + String
.format("%s%04x", "0x", fHeaderChecksum
); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
226 String string
= getProtocol().getName() + ", Source: " + fSourceIpAddress
.getHostAddress() + ", Destination: " + fDestinationIpAddress
.getHostAddress() + //$NON-NLS-1$ //$NON-NLS-2$
227 "\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$
228 " 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$
229 + "\n" + flagString
+ "\nTime to live: " + fTimeToLive
+ //$NON-NLS-1$ //$NON-NLS-2$
230 "\nProtocol: " + fIpDatagramProtocol
+ "\n" //$NON-NLS-1$ //$NON-NLS-2$
231 + checksumString
+ "\n"; //$NON-NLS-1$
232 final Packet child
= fChildPacket
;
234 return string
+ child
.toString();
240 * Getter method that returns the version of the IP protocol used. This
241 * should always be set to 4 as IPv6 has its own class.
243 * @return The version of the IP used.
245 public int getVersion() {
250 * Getter method that returns the header length in bytes. In the IPv4
251 * packet, this is specified in 4-bytes data block. By default, this method
252 * returns 20 if there are no options present. Otherwise, it will return a
255 * @return The header length in bytes.
257 public int getHeaderLength() {
258 return fInternetHeaderLength
* IPv4Values
.BLOCK_SIZE
;
262 * Getter method that returns the Differentiated Services Code Point (a.k.a.
263 * the Type of Service). This is useful for some technologies that require
264 * real-time data exchange.
268 public int getDSCP() {
273 * Getter method that returns the Explicit Congestion Notification (ECN).
274 * This allows end-to-end communication without dropping packets.
278 public int getExplicitCongestionNotification() {
279 return fExplicitCongestionNotification
;
283 * Getter method to retrieve the length of the entire packet, in bytes. This
284 * number is according to the packet, and might not be true if the packet is
287 * @return The total length (packet and payload) in bytes.
289 public int getTotalLength() {
294 * Getter method to retrieve the Identification. This is a field that is
295 * used to uniquely identify the packets, thus allowing the reconstruction
296 * of fragmented IP packets.
298 * @return The packet identification.
300 public int getIdentification() {
301 return fIdentification
;
305 * Getter method that returns the state of the Reserved flag. This must
308 * @return The state of the Reserved flag.
310 public boolean getReservedFlag() {
311 return fReservedFlag
;
315 * Getter method that indicates if the packet can be fragmented or not.
317 * @return Whether the packet can be fragmented or not.
319 public boolean getDontFragmentFlag() {
320 return fDontFragmentFlag
;
324 * Getter method that indicates if the packet has more fragments or not.
326 * @return Whether the packet has more fragments or not.
328 public boolean getHasMoreFragment() {
329 return fMoreFragmentFlag
;
333 * Getter method that specify the offset of a particular fragment relative
334 * to the original unfragmented packet, in 8-bytes blocks. *
336 * @return The fragment offset.
338 public int getFragmentOffset() {
339 return fFragmentOffset
;
343 * Getter method that returns the time to live in seconds. In practice, this
344 * is a hop count. This is used to prevent packets from persisting.
346 * @return The time left to live for the packet.
348 public int getTimeToLive() {
353 * Getter method that returns the encapsulated protocol.
355 * See http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
357 * @return The encapsulated protocol.
359 public int getIpDatagramProtocol() {
360 return fIpDatagramProtocol
;
364 * Getter method that returns the checksum, according to the packet. This
365 * checksum might be wrong if the packet is erroneous.
367 * @return The header checksum.
369 public int getHeaderChecksum() {
370 return fHeaderChecksum
;
374 * Getter method that returns the source IP address.
376 * @return The source IP address, as a byte array in big-endian.
378 public Inet4Address
getSourceIpAddress() {
379 return fSourceIpAddress
;
383 * Getter method that returns the destination IP address.
385 * @return The destination IP address, as a byte array in big-endian.
387 public Inet4Address
getDestinationIpAddress() {
388 return fDestinationIpAddress
;
392 * Getter method that returns the options. This method returns null if no
393 * options are present.
395 * @return The options of the packet.
397 public @Nullable byte[] getOptions() {
398 final byte[] options
= fOptions
;
399 if (options
== null) {
402 return Arrays
.copyOf(options
, options
.length
);
406 public boolean validate() {
407 // Not yet implemented. ATM, we consider that all packets are valid.
408 // This is the case for all packets.
409 // TODO Implement it.
414 public IPv4Endpoint
getSourceEndpoint() {
416 IPv4Endpoint endpoint
= fSourceEndpoint
;
417 if (endpoint
== null) {
418 endpoint
= new IPv4Endpoint(this, true);
420 fSourceEndpoint
= endpoint
;
421 return fSourceEndpoint
;
425 public IPv4Endpoint
getDestinationEndpoint() {
427 IPv4Endpoint endpoint
= fDestinationEndpoint
;
429 if (endpoint
== null) {
430 endpoint
= new IPv4Endpoint(this, false);
432 fDestinationEndpoint
= endpoint
;
433 return fDestinationEndpoint
;
437 public Map
<String
, String
> getFields() {
438 ImmutableMap
<String
, String
> map
= fFields
;
440 Builder
<String
, String
> builder
= ImmutableMap
.<String
, String
> builder()
441 .put("Version", String
.valueOf(fVersion
)) //$NON-NLS-1$
442 .put("Header Length", String
.valueOf(getHeaderLength()) + " bytes") //$NON-NLS-1$ //$NON-NLS-2$
443 .put("Differentiated Services Field", String
.format("%s%02x", "0x", fDSCP
)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
444 .put("Explicit Congestion Notification", String
.format("%s%02x", "0x", fExplicitCongestionNotification
)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
445 .put("Total Length", String
.valueOf(fTotalLength
) + " bytes") //$NON-NLS-1$ //$NON-NLS-2$
446 .put("Identification", String
.format("%s%04x", "0x", fIdentification
)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
447 .put("Don't Fragment Flag", String
.valueOf(fDontFragmentFlag
)) //$NON-NLS-1$
448 .put("More Fragment Flag", String
.valueOf(fMoreFragmentFlag
)) //$NON-NLS-1$
449 .put("Fragment Offset", String
.valueOf(fFragmentOffset
)) //$NON-NLS-1$
450 .put("Time to live", String
.valueOf(fTimeToLive
)) //$NON-NLS-1$
451 .put("Protocol", IPProtocolNumberHelper
.toString(fIpDatagramProtocol
) + " (" + String
.valueOf(fIpDatagramProtocol
) + ")") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
452 .put("Checksum", String
.format("%s%04x", "0x", fHeaderChecksum
)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
453 .put("Source IP Address", fSourceIpAddress
.getHostAddress()) //$NON-NLS-1$
454 .put("Destination IP Address", fDestinationIpAddress
.getHostAddress()); //$NON-NLS-1$
455 byte[] options
= fOptions
;
456 if (options
== null) {
457 builder
.put("Options", EMPTY_STRING
); //$NON-NLS-1$
459 builder
.put("Options", ConversionHelper
.bytesToHex(options
, true)); //$NON-NLS-1$
462 @SuppressWarnings("null")
464 ImmutableMap
<String
, String
> newMap
= builder
.build();
472 public String
getLocalSummaryString() {
473 return "Src: " + fSourceIpAddress
.getHostAddress() + " , Dst: " + fDestinationIpAddress
.getHostAddress(); //$NON-NLS-1$ //$NON-NLS-2$
477 protected String
getSignificationString() {
478 StringBuilder sb
= new StringBuilder();
479 sb
.append(fSourceIpAddress
.getHostAddress())
480 .append(" > ") //$NON-NLS-1$
481 .append(fDestinationIpAddress
.getHostAddress());
483 String flags
= generateFlagString();
484 if (!(flags
.equals(""))) { //$NON-NLS-1$
490 sb
.append(" Id=") //$NON-NLS-1$
491 .append(fIdentification
);
493 final ByteBuffer payload
= fPayload
;
494 if (payload
!= null) {
495 sb
.append(" Len=") //$NON-NLS-1$
496 .append(payload
.array().length
);
498 sb
.append(" Len=0"); //$NON-NLS-1$
500 String string
= sb
.toString();
501 if (string
== null) {
507 private String
generateFlagString() {
508 StringBuilder sb
= new StringBuilder();
509 boolean start
= true;
511 if (fDontFragmentFlag
) {
513 sb
.append(", "); //$NON-NLS-1$
515 sb
.append("DF"); //$NON-NLS-1$
518 if (fMoreFragmentFlag
) {
520 sb
.append(", "); //$NON-NLS-1$
522 sb
.append("MF"); //$NON-NLS-1$
525 String string
= sb
.toString();
526 if (string
== null) {
533 public int hashCode() {
534 final int prime
= 31;
536 final Packet child
= fChildPacket
;
538 result
= prime
* result
+ child
.hashCode();
540 result
= prime
* result
;
542 result
= prime
* result
+ fDSCP
;
543 result
= prime
* result
+ fDestinationIpAddress
.hashCode();
544 result
= prime
* result
+ (fDontFragmentFlag ?
1231 : 1237);
545 result
= prime
* result
+ fExplicitCongestionNotification
;
546 result
= prime
* result
+ fFragmentOffset
;
547 result
= prime
* result
+ fHeaderChecksum
;
548 result
= prime
* result
+ fIdentification
;
549 result
= prime
* result
+ fInternetHeaderLength
;
550 result
= prime
* result
+ fIpDatagramProtocol
;
551 result
= prime
* result
+ (fMoreFragmentFlag ?
1231 : 1237);
552 result
= prime
* result
+ Arrays
.hashCode(fOptions
);
553 final ByteBuffer payload
= fPayload
;
554 if (payload
!= null) {
555 result
= prime
* result
+ payload
.hashCode();
557 result
= prime
* result
;
559 result
= prime
* result
+ (fReservedFlag ?
1231 : 1237);
560 result
= prime
* result
+ fSourceIpAddress
.hashCode();
561 result
= prime
* result
+ fTimeToLive
;
562 result
= prime
* result
+ fTotalLength
;
563 result
= prime
* result
+ fVersion
;
568 public boolean equals(@Nullable Object obj
) {
575 if (getClass() != obj
.getClass()) {
578 IPv4Packet other
= (IPv4Packet
) obj
;
579 final Packet child
= fChildPacket
;
581 if (!child
.equals(other
.fChildPacket
)) {
585 if (other
.fChildPacket
!= null) {
590 if (fDSCP
!= other
.fDSCP
) {
593 if (!(fDestinationIpAddress
.equals(other
.fDestinationIpAddress
))) {
596 if (fDontFragmentFlag
!= other
.fDontFragmentFlag
) {
599 if (fExplicitCongestionNotification
!= other
.fExplicitCongestionNotification
) {
602 if (fFragmentOffset
!= other
.fFragmentOffset
) {
605 if (fHeaderChecksum
!= other
.fHeaderChecksum
) {
608 if (fIdentification
!= other
.fIdentification
) {
611 if (fInternetHeaderLength
!= other
.fInternetHeaderLength
) {
614 if (fIpDatagramProtocol
!= other
.fIpDatagramProtocol
) {
617 if (fMoreFragmentFlag
!= other
.fMoreFragmentFlag
) {
620 if (!Arrays
.equals(fOptions
, other
.fOptions
)) {
623 final ByteBuffer payload
= fPayload
;
624 if (payload
!= null) {
625 if (!payload
.equals(other
.fPayload
)) {
629 if (other
.fPayload
!= null) {
633 if (fReservedFlag
!= other
.fReservedFlag
) {
636 if (!(fSourceIpAddress
.equals(other
.fSourceIpAddress
))) {
639 if (fTimeToLive
!= other
.fTimeToLive
) {
642 if (fTotalLength
!= other
.fTotalLength
) {
645 if (fVersion
!= other
.fVersion
) {