Fix some null warnings
[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 import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
17
18 import java.net.Inet4Address;
19 import java.net.InetAddress;
20 import java.net.UnknownHostException;
21 import java.nio.ByteBuffer;
22 import java.nio.ByteOrder;
23 import java.util.Arrays;
24 import java.util.Map;
25
26 import org.eclipse.jdt.annotation.NonNull;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.eclipse.tracecompass.common.core.NonNullUtils;
29 import org.eclipse.tracecompass.internal.pcap.core.packet.BadPacketException;
30 import org.eclipse.tracecompass.internal.pcap.core.packet.Packet;
31 import org.eclipse.tracecompass.internal.pcap.core.protocol.PcapProtocol;
32 import org.eclipse.tracecompass.internal.pcap.core.protocol.tcp.TCPPacket;
33 import org.eclipse.tracecompass.internal.pcap.core.protocol.udp.UDPPacket;
34 import org.eclipse.tracecompass.internal.pcap.core.protocol.unknown.UnknownPacket;
35 import org.eclipse.tracecompass.internal.pcap.core.trace.PcapFile;
36 import org.eclipse.tracecompass.internal.pcap.core.util.ConversionHelper;
37 import org.eclipse.tracecompass.internal.pcap.core.util.IPProtocolNumberHelper;
38
39 import com.google.common.collect.ImmutableMap;
40 import com.google.common.collect.ImmutableMap.Builder;
41
42 /**
43 * Class that represents an Ethernet II packet.
44 *
45 * @author Vincent Perot
46 */
47 public class IPv4Packet extends Packet {
48
49 private final @Nullable Packet fChildPacket;
50 private final @Nullable ByteBuffer fPayload;
51
52 private final int fVersion;
53 private final int fInternetHeaderLength; // in 4 bytes blocks
54 private final int fDSCP;
55 private final int fExplicitCongestionNotification;
56 private final int fTotalLength; // in bytes
57 private final int fIdentification;
58 private final boolean fReservedFlag;
59 private final boolean fDontFragmentFlag;
60 private final boolean fMoreFragmentFlag;
61 private final int fFragmentOffset;
62 private final int fTimeToLive;
63 private final int fIpDatagramProtocol;
64 private final int fHeaderChecksum;
65 private final Inet4Address fSourceIpAddress;
66 private final Inet4Address fDestinationIpAddress;
67 private final byte @Nullable [] fOptions;
68
69 private @Nullable IPv4Endpoint fSourceEndpoint;
70 private @Nullable IPv4Endpoint fDestinationEndpoint;
71
72 private @Nullable Map<String, String> fFields;
73
74 // TODO Interpret options. See
75 // http://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml
76
77 /**
78 * Constructor of the IPv4 Packet class.
79 *
80 * @param file
81 * The file that contains this packet.
82 * @param parent
83 * The parent packet of this packet (the encapsulating packet).
84 * @param packet
85 * The entire packet (header and payload).
86 * @throws BadPacketException
87 * Thrown when the packet is erroneous.
88 */
89 public IPv4Packet(PcapFile file, @Nullable Packet parent, ByteBuffer packet) throws BadPacketException {
90 super(file, parent, PcapProtocol.IPV4);
91
92 // The endpoints are lazy loaded. They are defined in the get*Endpoint()
93 // methods.
94 fSourceEndpoint = null;
95 fDestinationEndpoint = null;
96
97 fFields = null;
98
99 packet.order(ByteOrder.BIG_ENDIAN);
100 packet.position(0);
101
102 byte storage = packet.get();
103 fVersion = ((storage & 0xF0) >> 4) & 0x000000FF;
104 fInternetHeaderLength = storage & 0x0F;
105
106 storage = packet.get();
107 fDSCP = ((storage & 0b11111100) >> 2) & 0x000000FF;
108 fExplicitCongestionNotification = storage & 0b00000011;
109
110 fTotalLength = ConversionHelper.unsignedShortToInt(packet.getShort());
111 fIdentification = ConversionHelper.unsignedShortToInt(packet.getShort());
112
113 storage = packet.get();
114 fReservedFlag = isBitSet(storage, 7);
115 fDontFragmentFlag = isBitSet(storage, 6);
116 fMoreFragmentFlag = isBitSet(storage, 5);
117 int msb = ((storage & 0b00011111) << 8);
118 int lsb = ConversionHelper.unsignedByteToInt(packet.get());
119 fFragmentOffset = msb + lsb;
120
121 fTimeToLive = ConversionHelper.unsignedByteToInt(packet.get());
122 fIpDatagramProtocol = ConversionHelper.unsignedByteToInt(packet.get());
123 fHeaderChecksum = ConversionHelper.unsignedShortToInt(packet.getShort());
124
125 byte[] source = new byte[IPv4Values.IP_ADDRESS_SIZE];
126 byte[] destination = new byte[IPv4Values.IP_ADDRESS_SIZE];
127 packet.get(source);
128 packet.get(destination);
129
130 try {
131 fSourceIpAddress = (Inet4Address) checkNotNull(InetAddress.getByAddress(source));
132 fDestinationIpAddress = (Inet4Address) checkNotNull(InetAddress.getByAddress(destination));
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 byte @Nullable [] 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 Map<String, String> map = fFields;
439 if (map == null) {
440 Builder<@NonNull String, @NonNull String> builder = ImmutableMap.<@NonNull String, @NonNull 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", nullToEmptyString(fSourceIpAddress.getHostAddress())) //$NON-NLS-1$
454 .put("Destination IP Address", nullToEmptyString(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 fFields = checkNotNull(builder.build());
463 return fFields;
464 }
465 return map;
466 }
467
468 @Override
469 public String getLocalSummaryString() {
470 return "Src: " + fSourceIpAddress.getHostAddress() + " , Dst: " + fDestinationIpAddress.getHostAddress(); //$NON-NLS-1$ //$NON-NLS-2$
471 }
472
473 @Override
474 protected String getSignificationString() {
475 StringBuilder sb = new StringBuilder();
476 sb.append(fSourceIpAddress.getHostAddress())
477 .append(" > ") //$NON-NLS-1$
478 .append(fDestinationIpAddress.getHostAddress());
479
480 String flags = generateFlagString();
481 if (!(flags.equals(""))) { //$NON-NLS-1$
482 sb.append(' ')
483 .append('[')
484 .append(flags)
485 .append(']');
486 }
487 sb.append(" Id=") //$NON-NLS-1$
488 .append(fIdentification);
489
490 final ByteBuffer payload = fPayload;
491 if (payload != null) {
492 sb.append(" Len=") //$NON-NLS-1$
493 .append(payload.array().length);
494 } else {
495 sb.append(" Len=0"); //$NON-NLS-1$
496 }
497 return NonNullUtils.nullToEmptyString(sb);
498 }
499
500 private String generateFlagString() {
501 StringBuilder sb = new StringBuilder();
502 boolean start = true;
503
504 if (fDontFragmentFlag) {
505 if (!start) {
506 sb.append(", "); //$NON-NLS-1$
507 }
508 sb.append("DF"); //$NON-NLS-1$
509 start = false;
510 }
511 if (fMoreFragmentFlag) {
512 if (!start) {
513 sb.append(", "); //$NON-NLS-1$
514 }
515 sb.append("MF"); //$NON-NLS-1$
516 start = false;
517 }
518 return NonNullUtils.nullToEmptyString(sb);
519 }
520
521 @Override
522 public int hashCode() {
523 final int prime = 31;
524 int result = 1;
525 final Packet child = fChildPacket;
526 if (child != null) {
527 result = prime * result + child.hashCode();
528 } else {
529 result = prime * result;
530 }
531 result = prime * result + fDSCP;
532 result = prime * result + fDestinationIpAddress.hashCode();
533 result = prime * result + (fDontFragmentFlag ? 1231 : 1237);
534 result = prime * result + fExplicitCongestionNotification;
535 result = prime * result + fFragmentOffset;
536 result = prime * result + fHeaderChecksum;
537 result = prime * result + fIdentification;
538 result = prime * result + fInternetHeaderLength;
539 result = prime * result + fIpDatagramProtocol;
540 result = prime * result + (fMoreFragmentFlag ? 1231 : 1237);
541 result = prime * result + Arrays.hashCode(fOptions);
542 final ByteBuffer payload = fPayload;
543 if (payload != null) {
544 result = prime * result + payload.hashCode();
545 } else {
546 result = prime * result;
547 }
548 result = prime * result + (fReservedFlag ? 1231 : 1237);
549 result = prime * result + fSourceIpAddress.hashCode();
550 result = prime * result + fTimeToLive;
551 result = prime * result + fTotalLength;
552 result = prime * result + fVersion;
553 return result;
554 }
555
556 @Override
557 public boolean equals(@Nullable Object obj) {
558 if (this == obj) {
559 return true;
560 }
561 if (obj == null) {
562 return false;
563 }
564 if (getClass() != obj.getClass()) {
565 return false;
566 }
567 IPv4Packet other = (IPv4Packet) obj;
568 if (!NonNullUtils.equalsNullable(fChildPacket, other.fChildPacket)) {
569 return false;
570 }
571 if (fDSCP != other.fDSCP) {
572 return false;
573 }
574 if (!(fDestinationIpAddress.equals(other.fDestinationIpAddress))) {
575 return false;
576 }
577 if (fDontFragmentFlag != other.fDontFragmentFlag) {
578 return false;
579 }
580 if (fExplicitCongestionNotification != other.fExplicitCongestionNotification) {
581 return false;
582 }
583 if (fFragmentOffset != other.fFragmentOffset) {
584 return false;
585 }
586 if (fHeaderChecksum != other.fHeaderChecksum) {
587 return false;
588 }
589 if (fIdentification != other.fIdentification) {
590 return false;
591 }
592 if (fInternetHeaderLength != other.fInternetHeaderLength) {
593 return false;
594 }
595 if (fIpDatagramProtocol != other.fIpDatagramProtocol) {
596 return false;
597 }
598 if (fMoreFragmentFlag != other.fMoreFragmentFlag) {
599 return false;
600 }
601 if (!Arrays.equals(fOptions, other.fOptions)) {
602 return false;
603 }
604 if (!NonNullUtils.equalsNullable(fPayload, other.fPayload)) {
605 return false;
606 }
607 if (fReservedFlag != other.fReservedFlag) {
608 return false;
609 }
610 if (!(fSourceIpAddress.equals(other.fSourceIpAddress))) {
611 return false;
612 }
613 if (fTimeToLive != other.fTimeToLive) {
614 return false;
615 }
616 if (fTotalLength != other.fTotalLength) {
617 return false;
618 }
619 if (fVersion != other.fVersion) {
620 return false;
621 }
622 return true;
623 }
624 }
This page took 0.046151 seconds and 5 git commands to generate.