diff --git a/pcap4j-core/src/main/java/org/pcap4j/util/IpV6Helper.java b/pcap4j-core/src/main/java/org/pcap4j/util/IpV6Helper.java new file mode 100644 index 000000000..1ba32e9ff --- /dev/null +++ b/pcap4j-core/src/main/java/org/pcap4j/util/IpV6Helper.java @@ -0,0 +1,131 @@ +/*_########################################################################## + _## + _## Copyright (C) 2012 Pcap4J.org + _## + _########################################################################## +*/ + +package org.pcap4j.util; + +import java.util.Comparator; +import java.util.List; +import org.pcap4j.packet.*; +import org.pcap4j.packet.factory.PacketFactories; +import org.pcap4j.packet.namednumber.IpNumber; + +public final class IpV6Helper { + private static final Comparator comparator = new ComparatorImpl(); + + private IpV6Helper() {} + + /** + * @param ipV6Packet fragmented ivp6 packet + * @return payload length of the fragmented packet + */ + private static int computePayloadLength(IpV6Packet ipV6Packet) { + return ipV6Packet.getHeader().getPayloadLength() - computeExtHeaderSizes(ipV6Packet); + } + + private static int computeExtHeaderSizes(Packet packet) { + Packet payload = packet.getPayload(); + if (payload instanceof IpV6ExtOptionsPacket) { + IpV6ExtOptionsPacket ipV6ExtOptionsPacket = (IpV6ExtOptionsPacket) payload; + return ((ipV6ExtOptionsPacket.getHeader().getHdrExtLenAsInt() + 1) * 8) + + computeExtHeaderSizes(payload); + } else if (payload instanceof IpV6ExtFragmentPacket) { + IpV6ExtFragmentPacket ipV6ExtFragmentPacket = (IpV6ExtFragmentPacket) payload; + return (ipV6ExtFragmentPacket.getHeader().length()); + } else if (payload instanceof IpV6ExtRoutingPacket) { + IpV6ExtRoutingPacket ipV6ExtRoutingPacket = (IpV6ExtRoutingPacket) payload; + return ((ipV6ExtRoutingPacket.getHeader().getHdrExtLenAsInt() + 1) * 8) + + computeExtHeaderSizes(payload); + } else { + throw new IllegalArgumentException("Can't find IpV6 fragment packet: " + packet); + } + } + + /** + * @param outerOfFragment builder of packet which is previous for ext. fragment header + * @param ipNumber new ip number for the packet + */ + private static void fixIpNumber(Packet.Builder outerOfFragment, IpNumber ipNumber) { + if (outerOfFragment instanceof IpV6Packet.Builder) { + IpV6Packet.Builder builder = (IpV6Packet.Builder) outerOfFragment; + builder.nextHeader(ipNumber); + } else if (outerOfFragment instanceof IpV6ExtOptionsPacket.Builder) { + IpV6ExtOptionsPacket.Builder builder = (IpV6ExtOptionsPacket.Builder) outerOfFragment; + builder.nextHeader(ipNumber); + } else if (outerOfFragment instanceof IpV6ExtRoutingPacket.Builder) { + IpV6ExtRoutingPacket.Builder builder = (IpV6ExtRoutingPacket.Builder) outerOfFragment; + builder.nextHeader(ipNumber); + } else { + throw new IllegalArgumentException("Can't defragment, unexpected header: " + outerOfFragment); + } + } + + /** + * @param list list + * @return a defragmented packet. + */ + public static IpV6Packet defragment(List list) { + list.sort(comparator); + IpV6Packet lastPacket = list.get(list.size() - 1); + IpV6ExtFragmentPacket ipV6LastFragmentPacket = lastPacket.get(IpV6ExtFragmentPacket.class); + + int payloadTotalLength = + ipV6LastFragmentPacket.getHeader().getFragmentOffset() * 8 + + computePayloadLength(lastPacket); + + if (payloadTotalLength <= 0) { + throw new IllegalArgumentException("Can't defragment: " + list); + } + + final byte[] defragmentedPayload = new byte[payloadTotalLength]; + int destPos = 0; + try { + for (IpV6Packet p : list) { + IpV6ExtFragmentPacket fragmentPacket = p.get(IpV6ExtFragmentPacket.class); + byte[] rawPayload = fragmentPacket.getPayload().getRawData(); + System.arraycopy(rawPayload, 0, defragmentedPayload, destPos, rawPayload.length); + destPos += rawPayload.length; + } + } catch (Throwable e) { + throw new IllegalArgumentException("Can't defragment: " + list, e); + } + + IpV6Packet firstPacket = list.get(0); + IpV6ExtFragmentPacket ipV6FirstFragmentPacket = firstPacket.get(IpV6ExtFragmentPacket.class); + IpV6Packet.Builder builder = firstPacket.getBuilder(); + Packet.Builder outerOfFragmentb = builder.getOuterOf(IpV6ExtFragmentPacket.Builder.class); + outerOfFragmentb.payloadBuilder( + new SimpleBuilder( + PacketFactories.getFactory(Packet.class, IpNumber.class) + .newInstance( + defragmentedPayload, + 0, + defragmentedPayload.length, + ipV6FirstFragmentPacket.getHeader().getNextHeader()))); + fixIpNumber(outerOfFragmentb, ipV6FirstFragmentPacket.getHeader().getNextHeader()); + builder.correctLengthAtBuild(true); + return builder.build(); + } + + private static final class ComparatorImpl implements Comparator { + + @Override + public int compare(IpV6Packet p1, IpV6Packet p2) { + IpV6ExtFragmentPacket fp1 = p1.get(IpV6ExtFragmentPacket.class); + IpV6ExtFragmentPacket fp2 = p2.get(IpV6ExtFragmentPacket.class); + + if (fp1 == null) { + throw new IllegalArgumentException("Can't defragment: " + p1); + } + + if (fp2 == null) { + throw new IllegalArgumentException("Can't defragment: " + p2); + } + + return fp1.getHeader().getFragmentOffset() - fp2.getHeader().getFragmentOffset(); + } + } +} diff --git a/pcap4j-packetfactory-propertiesbased/src/test/resources/org/pcap4j/test/core/IpV6HelperTest.pcap b/pcap4j-packetfactory-propertiesbased/src/test/resources/org/pcap4j/test/core/IpV6HelperTest.pcap new file mode 100644 index 000000000..87845811d Binary files /dev/null and b/pcap4j-packetfactory-propertiesbased/src/test/resources/org/pcap4j/test/core/IpV6HelperTest.pcap differ diff --git a/pcap4j-packetfactory-static/src/test/resources/org/pcap4j/test/core/IpV6HelperTest.pcap b/pcap4j-packetfactory-static/src/test/resources/org/pcap4j/test/core/IpV6HelperTest.pcap new file mode 100644 index 000000000..87845811d Binary files /dev/null and b/pcap4j-packetfactory-static/src/test/resources/org/pcap4j/test/core/IpV6HelperTest.pcap differ diff --git a/pcap4j-packettest/src/test/java/org/pcap4j/test/util/IpV6HelperTest.java b/pcap4j-packettest/src/test/java/org/pcap4j/test/util/IpV6HelperTest.java new file mode 100644 index 000000000..6653e37ca --- /dev/null +++ b/pcap4j-packettest/src/test/java/org/pcap4j/test/util/IpV6HelperTest.java @@ -0,0 +1,228 @@ +/*_########################################################################## + _## + _## Copyright (C) 2012 Pcap4J.org + _## + _########################################################################## +*/ + +package org.pcap4j.test.util; + +import static org.junit.Assert.*; + +import java.io.EOFException; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.*; +import java.util.concurrent.TimeoutException; +import org.junit.Test; +import org.pcap4j.core.PcapHandle; +import org.pcap4j.core.Pcaps; +import org.pcap4j.packet.*; +import org.pcap4j.packet.namednumber.*; +import org.pcap4j.util.IpV6Helper; +import org.pcap4j.util.MacAddress; + +@SuppressWarnings("javadoc") +public class IpV6HelperTest { + private static final String PCAP_FILE_KEY = IpV6HelperTest.class.getName() + ".pcapFile"; + private static final String PCAP_FILE = + System.getProperty( + PCAP_FILE_KEY, + "src/test/resources/org/pcap4j/test/core/" + + IpV6HelperTest.class.getSimpleName() + + ".pcap"); + + enum ExtHeader { + DESTINATION, + ROUTING, + NO_HEADER + }; + + private EthernetPacket getPacket(boolean fragmented) throws UnknownHostException { + UnknownPacket.Builder unknownb = new UnknownPacket.Builder(); + unknownb.rawData(new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}); + + Inet6Address srcIp = (Inet6Address) InetAddress.getByName("2001:db8::3:2:1"); + Inet6Address dstIp = (Inet6Address) InetAddress.getByName("2001:db8::3:2:2"); + + UdpPacket.Builder udpb = new UdpPacket.Builder(); + udpb.srcPort(UdpPort.SNMP_TRAP) + .dstPort(UdpPort.getInstance((short) 0)) + .payloadBuilder(unknownb) + .correctChecksumAtBuild(true) + .correctLengthAtBuild(true) + .srcAddr(srcIp) + .dstAddr(dstIp); + + IpV6ExtFragmentPacket.Builder fragHeadb = new IpV6ExtFragmentPacket.Builder(); + fragHeadb + .fragmentOffset((short) 0) + .identification(123) + .nextHeader(IpNumber.UDP) + .payloadBuilder(udpb); + + IpV6Packet.Builder ipv6b = new IpV6Packet.Builder(); + ipv6b + .version(IpVersion.IPV6) + .hopLimit((byte) 100) + .trafficClass(IpV6SimpleTrafficClass.newInstance((byte) 0x12)) + .flowLabel(IpV6SimpleFlowLabel.newInstance(0x12345)) + .nextHeader(fragmented ? IpNumber.IPV6_FRAG : IpNumber.UDP) + .srcAddr(srcIp) + .dstAddr(dstIp) + .payloadBuilder(fragmented ? fragHeadb : udpb) + .correctLengthAtBuild(true); + + EthernetPacket.Builder eb = new EthernetPacket.Builder(); + eb.dstAddr(MacAddress.getByName("fe:00:00:00:00:02")) + .srcAddr(MacAddress.getByName("fe:00:00:00:00:01")) + .type(EtherType.IPV6) + .payloadBuilder(ipv6b) + .paddingAtBuild(true); + + return eb.build(); + } + + private Packet addExtRoutingHeader(Packet packet, boolean isFragmented) + throws IllegalRawDataException, UnknownHostException { + + IpV6ExtRoutingPacket.Builder ipV6ExtRoutingPacketb = new IpV6ExtRoutingPacket.Builder(); + ipV6ExtRoutingPacketb + .nextHeader(isFragmented ? IpNumber.IPV6_FRAG : IpNumber.UDP) + .correctLengthAtBuild(true) + .routingType(IpV6RoutingType.NIMROD) + .segmentsLeft((byte) 2) + .data( + new IpV6RoutingSourceRouteData( + 0, + new ArrayList( + Arrays.asList( + new Inet6Address[] { + (Inet6Address) InetAddress.getByName("2200::210:2:0:0:4") + })))) + .payloadBuilder( + isFragmented + ? packet.get(IpV6ExtFragmentPacket.class).getBuilder() + : packet.get(UdpPacket.class).getBuilder()); + + Packet.Builder packetb = packet.getBuilder(); + packetb + .get(IpV6Packet.Builder.class) + .correctLengthAtBuild(true) + .payloadBuilder(ipV6ExtRoutingPacketb) + .nextHeader(IpNumber.IPV6_ROUTE); + + return packetb.build(); + } + + private Packet addExtDestinationHeader(Packet packet, boolean isFragmented) + throws IllegalRawDataException, UnknownHostException { + IpV6ExtDestinationOptionsPacket.Builder ipV6ExtDestinationOptionsPacketb = + new IpV6ExtDestinationOptionsPacket.Builder(); + ipV6ExtDestinationOptionsPacketb + .nextHeader(isFragmented ? IpNumber.IPV6_FRAG : IpNumber.UDP) + .correctLengthAtBuild(true) + .options( + new ArrayList( + Arrays.asList( + new IpV6ExtOptionsPacket.IpV6Option[] { + new IpV6PadNOption.Builder() + .correctLengthAtBuild(true) + .data(new byte[] {0, 0, 0, 0}) + .build() + }))) + .payloadBuilder( + isFragmented + ? packet.get(IpV6ExtFragmentPacket.class).getBuilder() + : packet.get(UdpPacket.class).getBuilder()); + + Packet.Builder packetb = packet.getBuilder(); + packetb + .get(IpV6Packet.Builder.class) + .correctLengthAtBuild(true) + .payloadBuilder(ipV6ExtDestinationOptionsPacketb) + .nextHeader(IpNumber.IPV6_ROUTE); + + return packetb.build(); + } + + @Test(expected = IllegalArgumentException.class) + public void twoNoFragmentedPackets() throws UnknownHostException { + List ipV6Packets = new ArrayList(); + IpV6Packet ipV6Packet = getPacket(false).get(IpV6Packet.class); + ipV6Packets.add(ipV6Packet); + ipV6Packets.add(ipV6Packet); + IpV6Helper.defragment(ipV6Packets); + } + + @Test(expected = IllegalArgumentException.class) + public void oneNoFragmentedPacket() throws UnknownHostException { + List ipV6Packets = new ArrayList(); + ipV6Packets.add(getPacket(true).get(IpV6Packet.class)); + ipV6Packets.add(getPacket(false).get(IpV6Packet.class)); + IpV6Helper.defragment(ipV6Packets); + } + + private void testDefragment(ExtHeader extHeader) throws Exception { + EthernetPacket expectedPacket; + if (extHeader == ExtHeader.ROUTING) { + expectedPacket = (EthernetPacket) addExtRoutingHeader(getPacket(false), false); + } else if (extHeader == ExtHeader.DESTINATION) { + expectedPacket = (EthernetPacket) addExtDestinationHeader(getPacket(false), false); + } else { + expectedPacket = getPacket(false); + } + + PcapHandle handle = Pcaps.openOffline(PCAP_FILE); + List ipV6Packets = new ArrayList(); + Packet firstPacket = null; + + while (true) { + try { + Packet packet; + if (extHeader == ExtHeader.ROUTING) { + packet = addExtRoutingHeader(handle.getNextPacketEx(), true); + } else if (extHeader == ExtHeader.DESTINATION) { + packet = addExtDestinationHeader(handle.getNextPacketEx(), true); + } else { + packet = handle.getNextPacketEx(); + } + if (firstPacket == null) { + firstPacket = packet; + } + ipV6Packets.add(packet.get(IpV6Packet.class)); + } catch (TimeoutException e) { + continue; + } catch (EOFException e) { + break; + } + } + handle.close(); + + Collections.shuffle(ipV6Packets); + + IpV6Packet defragmentedIpV6Packet = IpV6Helper.defragment(ipV6Packets); + Packet.Builder actualb = firstPacket.getBuilder(); + actualb + .getOuterOf(IpV6Packet.Builder.class) + .payloadBuilder(new SimpleBuilder(defragmentedIpV6Packet)); + + assertEquals(expectedPacket, actualb.build()); + } + + @Test + public void testDefragmentWithRoutingHeader() throws Exception { + testDefragment(ExtHeader.ROUTING); + } + + @Test + public void testDefragmentWithoutExtHeaders() throws Exception { + testDefragment(ExtHeader.NO_HEADER); + } + + @Test + public void testDefragmentWithDestinationExtHeaders() throws Exception { + testDefragment(ExtHeader.DESTINATION); + } +}