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