pcap: Move plugins to the Trace Compass namespace
[deliverable/tracecompass.git] / org.eclipse.tracecompass.pcap.core / src / org / eclipse / linuxtools / internal / pcap / core / protocol / ipv4 / IPv4Packet.java
1 /*******************************************************************************
2 * Copyright (c) 2014 Ericsson
3 *
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
8 *
9 * Contributors:
10 * Vincent Perot - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.linuxtools.internal.pcap.core.protocol.ipv4;
14
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;
21 import java.util.Map;
22
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;
34
35 import com.google.common.collect.ImmutableMap;
36 import com.google.common.collect.ImmutableMap.Builder;
37
38 /**
39 * Class that represents an Ethernet II packet.
40 *
41 * @author Vincent Perot
42 */
43 public class IPv4Packet extends Packet {
44
45 private final @Nullable Packet fChildPacket;
46 private final @Nullable ByteBuffer fPayload;
47
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;
64
65 private @Nullable IPv4Endpoint fSourceEndpoint;
66 private @Nullable IPv4Endpoint fDestinationEndpoint;
67
68 private @Nullable ImmutableMap<String, String> fFields;
69
70 // TODO Interpret options. See
71 // http://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml
72
73 /**
74 * Constructor of the IPv4 Packet class.
75 *
76 * @param file
77 * The file that contains this packet.
78 * @param parent
79 * The parent packet of this packet (the encapsulating packet).
80 * @param packet
81 * The entire packet (header and payload).
82 * @throws BadPacketException
83 * Thrown when the packet is erroneous.
84 */
85 public IPv4Packet(PcapFile file, @Nullable Packet parent, ByteBuffer packet) throws BadPacketException {
86 super(file, parent, PcapProtocol.IPV4);
87
88 // The endpoints are lazy loaded. They are defined in the get*Endpoint()
89 // methods.
90 fSourceEndpoint = null;
91 fDestinationEndpoint = null;
92
93 fFields = null;
94
95 packet.order(ByteOrder.BIG_ENDIAN);
96 packet.position(0);
97
98 byte storage = packet.get();
99 fVersion = ((storage & 0xF0) >> 4) & 0x000000FF;
100 fInternetHeaderLength = storage & 0x0F;
101
102 storage = packet.get();
103 fDSCP = ((storage & 0b11111100) >> 2) & 0x000000FF;
104 fExplicitCongestionNotification = storage & 0b00000011;
105
106 fTotalLength = ConversionHelper.unsignedShortToInt(packet.getShort());
107 fIdentification = ConversionHelper.unsignedShortToInt(packet.getShort());
108
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;
116
117 fTimeToLive = ConversionHelper.unsignedByteToInt(packet.get());
118 fIpDatagramProtocol = ConversionHelper.unsignedByteToInt(packet.get());
119 fHeaderChecksum = ConversionHelper.unsignedShortToInt(packet.getShort());
120
121 byte[] source = new byte[IPv4Values.IP_ADDRESS_SIZE];
122 byte[] destination = new byte[IPv4Values.IP_ADDRESS_SIZE];
123 packet.get(source);
124 packet.get(destination);
125
126 try {
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$
135 }
136
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);
141 } else {
142 fOptions = null;
143 }
144
145 // Get payload if any.
146 if (packet.array().length - packet.position() > 0) {
147 byte[] array = new byte[packet.array().length - packet.position()];
148 packet.get(array);
149 ByteBuffer payload = ByteBuffer.wrap(array);
150 payload.order(ByteOrder.BIG_ENDIAN);
151 payload.position(0);
152 fPayload = payload;
153 } else {
154 fPayload = null;
155 }
156
157 // Find child
158 fChildPacket = findChildPacket();
159
160 }
161
162 @Override
163 public @Nullable Packet getChildPacket() {
164 return fChildPacket;
165 }
166
167 @Override
168 public @Nullable ByteBuffer getPayload() {
169 return fPayload;
170 }
171
172 /**
173 * {@inheritDoc}
174 *
175 * See http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
176 */
177 @Override
178 protected @Nullable Packet findChildPacket() throws BadPacketException {
179 // TODO Implement more protocols
180 ByteBuffer payload = fPayload;
181 if (payload == null) {
182 return null;
183 }
184
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);
190 default:
191 return new UnknownPacket(getPcapFile(), this, payload);
192 }
193
194 }
195
196 @Override
197 public String toString() {
198 // Generate flagString
199 // This is very ugly.
200 String flagString = null;
201
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$
218 }
219
220 flagString += ", Fragment Offset: " + fFragmentOffset; //$NON-NLS-1$
221
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$
225
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;
233 if (child != null) {
234 return string + child.toString();
235 }
236 return string;
237 }
238
239 /**
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.
242 *
243 * @return The version of the IP used.
244 */
245 public int getVersion() {
246 return fVersion;
247 }
248
249 /**
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
253 * higher number.
254 *
255 * @return The header length in bytes.
256 */
257 public int getHeaderLength() {
258 return fInternetHeaderLength * IPv4Values.BLOCK_SIZE;
259 }
260
261 /**
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.
265 *
266 * @return The DSCP
267 */
268 public int getDSCP() {
269 return fDSCP;
270 }
271
272 /**
273 * Getter method that returns the Explicit Congestion Notification (ECN).
274 * This allows end-to-end communication without dropping packets.
275 *
276 * @return The ECN.
277 */
278 public int getExplicitCongestionNotification() {
279 return fExplicitCongestionNotification;
280 }
281
282 /**
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
285 * erroneous.
286 *
287 * @return The total length (packet and payload) in bytes.
288 */
289 public int getTotalLength() {
290 return fTotalLength;
291 }
292
293 /**
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.
297 *
298 * @return The packet identification.
299 */
300 public int getIdentification() {
301 return fIdentification;
302 }
303
304 /**
305 * Getter method that returns the state of the Reserved flag. This must
306 * always be zero.
307 *
308 * @return The state of the Reserved flag.
309 */
310 public boolean getReservedFlag() {
311 return fReservedFlag;
312 }
313
314 /**
315 * Getter method that indicates if the packet can be fragmented or not.
316 *
317 * @return Whether the packet can be fragmented or not.
318 */
319 public boolean getDontFragmentFlag() {
320 return fDontFragmentFlag;
321 }
322
323 /**
324 * Getter method that indicates if the packet has more fragments or not.
325 *
326 * @return Whether the packet has more fragments or not.
327 */
328 public boolean getHasMoreFragment() {
329 return fMoreFragmentFlag;
330 }
331
332 /**
333 * Getter method that specify the offset of a particular fragment relative
334 * to the original unfragmented packet, in 8-bytes blocks. *
335 *
336 * @return The fragment offset.
337 */
338 public int getFragmentOffset() {
339 return fFragmentOffset;
340 }
341
342 /**
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.
345 *
346 * @return The time left to live for the packet.
347 */
348 public int getTimeToLive() {
349 return fTimeToLive;
350 }
351
352 /**
353 * Getter method that returns the encapsulated protocol.
354 *
355 * See http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
356 *
357 * @return The encapsulated protocol.
358 */
359 public int getIpDatagramProtocol() {
360 return fIpDatagramProtocol;
361 }
362
363 /**
364 * Getter method that returns the checksum, according to the packet. This
365 * checksum might be wrong if the packet is erroneous.
366 *
367 * @return The header checksum.
368 */
369 public int getHeaderChecksum() {
370 return fHeaderChecksum;
371 }
372
373 /**
374 * Getter method that returns the source IP address.
375 *
376 * @return The source IP address, as a byte array in big-endian.
377 */
378 public Inet4Address getSourceIpAddress() {
379 return fSourceIpAddress;
380 }
381
382 /**
383 * Getter method that returns the destination IP address.
384 *
385 * @return The destination IP address, as a byte array in big-endian.
386 */
387 public Inet4Address getDestinationIpAddress() {
388 return fDestinationIpAddress;
389 }
390
391 /**
392 * Getter method that returns the options. This method returns null if no
393 * options are present.
394 *
395 * @return The options of the packet.
396 */
397 public @Nullable byte[] getOptions() {
398 final byte[] options = fOptions;
399 if (options == null) {
400 return null;
401 }
402 return Arrays.copyOf(options, options.length);
403 }
404
405 @Override
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.
410 return true;
411 }
412
413 @Override
414 public IPv4Endpoint getSourceEndpoint() {
415 @Nullable
416 IPv4Endpoint endpoint = fSourceEndpoint;
417 if (endpoint == null) {
418 endpoint = new IPv4Endpoint(this, true);
419 }
420 fSourceEndpoint = endpoint;
421 return fSourceEndpoint;
422 }
423
424 @Override
425 public IPv4Endpoint getDestinationEndpoint() {
426 @Nullable
427 IPv4Endpoint endpoint = fDestinationEndpoint;
428
429 if (endpoint == null) {
430 endpoint = new IPv4Endpoint(this, false);
431 }
432 fDestinationEndpoint = endpoint;
433 return fDestinationEndpoint;
434 }
435
436 @Override
437 public Map<String, String> getFields() {
438 ImmutableMap<String, String> map = fFields;
439 if (map == null) {
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$
458 } else {
459 builder.put("Options", ConversionHelper.bytesToHex(options, true)); //$NON-NLS-1$
460
461 }
462 @SuppressWarnings("null")
463 @NonNull
464 ImmutableMap<String, String> newMap = builder.build();
465 fFields = newMap;
466 return newMap;
467 }
468 return map;
469 }
470
471 @Override
472 public String getLocalSummaryString() {
473 return "Src: " + fSourceIpAddress.getHostAddress() + " , Dst: " + fDestinationIpAddress.getHostAddress(); //$NON-NLS-1$ //$NON-NLS-2$
474 }
475
476 @Override
477 protected String getSignificationString() {
478 StringBuilder sb = new StringBuilder();
479 sb.append(fSourceIpAddress.getHostAddress())
480 .append(" > ") //$NON-NLS-1$
481 .append(fDestinationIpAddress.getHostAddress());
482
483 String flags = generateFlagString();
484 if (!(flags.equals(""))) { //$NON-NLS-1$
485 sb.append(' ')
486 .append('[')
487 .append(flags)
488 .append(']');
489 }
490 sb.append(" Id=") //$NON-NLS-1$
491 .append(fIdentification);
492
493 final ByteBuffer payload = fPayload;
494 if (payload != null) {
495 sb.append(" Len=") //$NON-NLS-1$
496 .append(payload.array().length);
497 } else {
498 sb.append(" Len=0"); //$NON-NLS-1$
499 }
500 String string = sb.toString();
501 if (string == null) {
502 return EMPTY_STRING;
503 }
504 return string;
505 }
506
507 private String generateFlagString() {
508 StringBuilder sb = new StringBuilder();
509 boolean start = true;
510
511 if (fDontFragmentFlag) {
512 if (!start) {
513 sb.append(", "); //$NON-NLS-1$
514 }
515 sb.append("DF"); //$NON-NLS-1$
516 start = false;
517 }
518 if (fMoreFragmentFlag) {
519 if (!start) {
520 sb.append(", "); //$NON-NLS-1$
521 }
522 sb.append("MF"); //$NON-NLS-1$
523 start = false;
524 }
525 String string = sb.toString();
526 if (string == null) {
527 return EMPTY_STRING;
528 }
529 return string;
530 }
531
532 @Override
533 public int hashCode() {
534 final int prime = 31;
535 int result = 1;
536 final Packet child = fChildPacket;
537 if (child != null) {
538 result = prime * result + child.hashCode();
539 } else {
540 result = prime * result;
541 }
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();
556 } else {
557 result = prime * result;
558 }
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;
564 return result;
565 }
566
567 @Override
568 public boolean equals(@Nullable Object obj) {
569 if (this == obj) {
570 return true;
571 }
572 if (obj == null) {
573 return false;
574 }
575 if (getClass() != obj.getClass()) {
576 return false;
577 }
578 IPv4Packet other = (IPv4Packet) obj;
579 final Packet child = fChildPacket;
580 if (child != null) {
581 if (!child.equals(other.fChildPacket)) {
582 return false;
583 }
584 } else {
585 if (other.fChildPacket != null) {
586 return false;
587 }
588 }
589
590 if (fDSCP != other.fDSCP) {
591 return false;
592 }
593 if (!(fDestinationIpAddress.equals(other.fDestinationIpAddress))) {
594 return false;
595 }
596 if (fDontFragmentFlag != other.fDontFragmentFlag) {
597 return false;
598 }
599 if (fExplicitCongestionNotification != other.fExplicitCongestionNotification) {
600 return false;
601 }
602 if (fFragmentOffset != other.fFragmentOffset) {
603 return false;
604 }
605 if (fHeaderChecksum != other.fHeaderChecksum) {
606 return false;
607 }
608 if (fIdentification != other.fIdentification) {
609 return false;
610 }
611 if (fInternetHeaderLength != other.fInternetHeaderLength) {
612 return false;
613 }
614 if (fIpDatagramProtocol != other.fIpDatagramProtocol) {
615 return false;
616 }
617 if (fMoreFragmentFlag != other.fMoreFragmentFlag) {
618 return false;
619 }
620 if (!Arrays.equals(fOptions, other.fOptions)) {
621 return false;
622 }
623 final ByteBuffer payload = fPayload;
624 if (payload != null) {
625 if (!payload.equals(other.fPayload)) {
626 return false;
627 }
628 } else {
629 if (other.fPayload != null) {
630 return false;
631 }
632 }
633 if (fReservedFlag != other.fReservedFlag) {
634 return false;
635 }
636 if (!(fSourceIpAddress.equals(other.fSourceIpAddress))) {
637 return false;
638 }
639 if (fTimeToLive != other.fTimeToLive) {
640 return false;
641 }
642 if (fTotalLength != other.fTotalLength) {
643 return false;
644 }
645 if (fVersion != other.fVersion) {
646 return false;
647 }
648 return true;
649 }
650
651 }
This page took 0.053725 seconds and 5 git commands to generate.