All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cisco.trex.stateless.IPv6NeighborDiscoveryService Maven / Gradle / Ivy

package com.cisco.trex.stateless;

import static org.pcap4j.util.ByteArrays.BYTE_SIZE_IN_BYTES;

import com.cisco.trex.stateless.exception.ServiceModeRequiredException;
import com.cisco.trex.stateless.model.Ipv6Node;
import com.cisco.trex.stateless.model.PortStatus;
import com.cisco.trex.stateless.model.StreamMode;
import com.cisco.trex.stateless.model.StreamModeRate;
import com.cisco.trex.stateless.model.StreamVM;
import com.cisco.trex.stateless.model.TRexClientResult;
import com.cisco.trex.stateless.model.port.PortVlan;
import com.cisco.trex.stateless.model.vm.VMInstruction;
import com.google.common.collect.Lists;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.pcap4j.packet.Dot1qVlanTagPacket;
import org.pcap4j.packet.EthernetPacket;
import org.pcap4j.packet.IcmpV6CommonPacket;
import org.pcap4j.packet.IcmpV6CommonPacket.IpV6NeighborDiscoveryOption;
import org.pcap4j.packet.IcmpV6EchoReplyPacket;
import org.pcap4j.packet.IcmpV6EchoRequestPacket;
import org.pcap4j.packet.IcmpV6NeighborAdvertisementPacket;
import org.pcap4j.packet.IcmpV6NeighborAdvertisementPacket.IcmpV6NeighborAdvertisementHeader;
import org.pcap4j.packet.IcmpV6NeighborSolicitationPacket;
import org.pcap4j.packet.IpV6NeighborDiscoverySourceLinkLayerAddressOption;
import org.pcap4j.packet.IpV6NeighborDiscoveryTargetLinkLayerAddressOption;
import org.pcap4j.packet.IpV6Packet;
import org.pcap4j.packet.IpV6SimpleFlowLabel;
import org.pcap4j.packet.IpV6SimpleTrafficClass;
import org.pcap4j.packet.Packet;
import org.pcap4j.packet.namednumber.EtherType;
import org.pcap4j.packet.namednumber.IcmpV6Code;
import org.pcap4j.packet.namednumber.IcmpV6Type;
import org.pcap4j.packet.namednumber.IpNumber;
import org.pcap4j.packet.namednumber.IpVersion;
import org.pcap4j.util.ByteArrays;
import org.pcap4j.util.MacAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class IPv6NeighborDiscoveryService {

  private static final EtherType QInQ =
      new EtherType((short) 0x88a8, "802.1Q Provider Bridge (Q-in-Q)");
  private static final Logger LOGGER = LoggerFactory.getLogger(IPv6NeighborDiscoveryService.class);
  private TRexClient tRexClient;

  public IPv6NeighborDiscoveryService(TRexClient tRexClient) {
    this.tRexClient = tRexClient;
  }

  public Map scan(int portIdx, int timeDuration, String dstIP, String srcIP)
      throws ServiceModeRequiredException {
    String broadcastIP = "ff02::1";

    long endTimeSec = System.currentTimeMillis() + timeDuration / 2 * 1000;
    TRexClientResult portStatusResult = tRexClient.getPortStatus(portIdx);
    PortStatus portStatus = portStatusResult.get();

    if (!portStatus.getService()) {
      throw new ServiceModeRequiredException();
    }

    String srcMac = portStatus.getAttr().getLayerConiguration().getL2Configuration().getSrc();
    PortVlan vlan = portStatus.getAttr().getVlan();

    Packet pingPkt =
        buildICMPV6EchoReq(
            srcIP,
            srcMac,
            multicastMacFromIPv6(broadcastIP).toString(),
            expandIPv6Address(broadcastIP));
    tRexClient.startStreamsIntermediate(portIdx, Collections.singletonList(buildStream(pingPkt)));

    List nsNaStreams = new ArrayList<>();
    Predicate ipV6NSPktFilter =
        etherPkt ->
            etherPkt.contains(IcmpV6NeighborSolicitationPacket.class)
                || etherPkt.contains(IcmpV6NeighborAdvertisementPacket.class);

    while (endTimeSec > System.currentTimeMillis()) {
      tRexClient
          .getRxQueue(portIdx, ipV6NSPktFilter)
          .forEach(
              pkt -> {
                IpV6Packet ipV6Packet = pkt.get(IpV6Packet.class);
                String nodeIp = ipV6Packet.getHeader().getSrcAddr().toString().substring(1);
                String nodeMac = getLinkLayerAddress(ipV6Packet);

                nsNaStreams.add(
                    buildStream(buildICMPV6NSPkt(vlan, srcMac, nodeMac, nodeIp, srcIP)));
                nsNaStreams.add(
                    buildStream(buildICMPV6NAPkt(vlan, srcMac, nodeMac, nodeIp, srcIP)));
              });
    }

    tRexClient.startStreamsIntermediate(portIdx, nsNaStreams);

    List icmpNAReplies = new ArrayList<>();

    Predicate ipV6NAPktFilter =
        etherPkt -> etherPkt.contains(IcmpV6NeighborAdvertisementPacket.class);
    endTimeSec = System.currentTimeMillis() + timeDuration / 2 * 1000;
    while (endTimeSec > System.currentTimeMillis()) {
      icmpNAReplies.addAll(tRexClient.getRxQueue(portIdx, ipV6NAPktFilter));
    }
    tRexClient.removeRxQueue(portIdx);
    return icmpNAReplies.stream()
        .map(this::toIpv6Node)
        .distinct()
        .filter(
            ipv6Node -> {
              if (dstIP != null) {
                try {
                  return InetAddress.getAllByName(dstIP)
                      .equals(InetAddress.getAllByName(ipv6Node.getIp()));
                } catch (UnknownHostException e) {
                  return false;
                }
              }
              return true;
            })
        .collect(Collectors.toMap(Ipv6Node::getIp, node -> node));
  }

  private Ipv6Node toIpv6Node(EthernetPacket ethernetPacket) {
    IcmpV6NeighborAdvertisementHeader icmpV6NaHdr =
        ethernetPacket.get(IcmpV6NeighborAdvertisementPacket.class).getHeader();
    boolean isRouter = icmpV6NaHdr.getRouterFlag();
    String nodeIp = icmpV6NaHdr.getTargetAddress().toString().substring(1);
    String nodeMac = ethernetPacket.getHeader().getSrcAddr().toString();
    return new Ipv6Node(nodeMac, nodeIp, isRouter);
  }

  public EthernetPacket sendIcmpV6Echo(
      int portIdx, String dstIp, int icmpId, int icmpSeq, int timeOut)
      throws ServiceModeRequiredException {
    PortStatus portStatus = tRexClient.getPortStatus(portIdx).get();
    if (!portStatus.getService()) {
      throw new ServiceModeRequiredException();
    }
    String srcMac = portStatus.getAttr().getLayerConiguration().getL2Configuration().getSrc();
    return sendIcmpV6Echo(portIdx, srcMac, dstIp, icmpId, icmpSeq, timeOut);
  }

  public EthernetPacket sendIcmpV6Echo(
      int portIdx, String srcMac, String dstIp, int icmpId, int icmpSeq, int timeOut) {
    Map stringEthernetPacketMap =
        sendNSandIcmpV6Req(portIdx, timeOut, srcMac, dstIp);

    Optional> icmpMulticastResponse =
        stringEthernetPacketMap.entrySet().stream().findFirst();

    EthernetPacket icmpUnicastReply = null;

    if (icmpMulticastResponse.isPresent()) {
      EthernetPacket etherPkt = icmpMulticastResponse.get().getValue();
      String nodeMac = etherPkt.getHeader().getSrcAddr().toString();
      Packet pingPkt = buildICMPV6EchoReq(null, srcMac, nodeMac, dstIp, icmpId, icmpSeq);
      tRexClient.startStreamsIntermediate(portIdx, Arrays.asList(buildStream(pingPkt)));
      long endTimeSec = System.currentTimeMillis() + timeOut * 1000 / 2;

      while (endTimeSec > System.currentTimeMillis()) {
        List rxQueue =
            tRexClient.getRxQueue(portIdx, pkt -> pkt.contains(IcmpV6EchoReplyPacket.class));
        if (!rxQueue.isEmpty()) {
          icmpUnicastReply = rxQueue.get(0);
        }
      }
    }

    tRexClient.removeRxQueue(portIdx);
    if (tRexClient.getPortStatus(portIdx).get().getState().equals("TX")) {
      tRexClient.stopTraffic(portIdx);
    }
    tRexClient.removeAllStreams(portIdx);
    return icmpUnicastReply;
  }

  public EthernetPacket sendNeighborSolicitation(int portIdx, int timeout, String dstIp)
      throws ServiceModeRequiredException {
    PortStatus portStatus = tRexClient.getPortStatus(portIdx).get();
    if (!portStatus.getService()) {
      throw new ServiceModeRequiredException();
    }
    String srcMac = portStatus.getAttr().getLayerConiguration().getL2Configuration().getSrc();
    PortVlan vlan = portStatus.getAttr().getVlan();
    return sendNeighborSolicitation(vlan, portIdx, timeout, srcMac, null, null, dstIp);
  }

  public EthernetPacket sendNeighborSolicitation(
      PortVlan vlan,
      int portIdx,
      int timeout,
      String srcMac,
      String dstMac,
      String srcIp,
      String dstIp) {
    long endTs = System.currentTimeMillis() + timeout * 1000;

    final String multicastMac = dstMac != null ? dstMac : multicastMacFromIPv6(dstIp).toString();
    final String specifiedSrcIP = srcIp != null ? srcIp : generateIPv6AddrFromMAC(srcMac);

    Packet icmpv6NSPkt = buildICMPV6NSPkt(vlan, srcMac, multicastMac, dstIp, specifiedSrcIP);
    LOGGER.trace("Sending IPv6 Neighbor Solicitation packet:\n{}", icmpv6NSPkt);
    tRexClient.startStreamsIntermediate(portIdx, Arrays.asList(buildStream(icmpv6NSPkt)));

    Predicate ipV6NAPktFilter =
        etherPkt -> {
          if (!etherPkt.contains(IcmpV6NeighborAdvertisementPacket.class)) {
            return false;
          }
          IcmpV6NeighborAdvertisementHeader icmpV6NaHdr =
              etherPkt.get(IcmpV6NeighborAdvertisementPacket.class).getHeader();

          String nodeIp = icmpV6NaHdr.getTargetAddress().toString().substring(1);

          IpV6Packet.IpV6Header ipV6Header = etherPkt.get(IpV6Packet.class).getHeader();
          String dstAddr = ipV6Header.getDstAddr().toString().substring(1);

          try {
            Inet6Address dstIPv6Addr = (Inet6Address) InetAddress.getByName(dstAddr);
            Inet6Address srcIPv6Addr = (Inet6Address) InetAddress.getByName(specifiedSrcIP);

            Inet6Address nodeIpv6 = (Inet6Address) InetAddress.getByName(nodeIp);
            Inet6Address targetIpv6inNS = (Inet6Address) InetAddress.getByName(dstIp);
            return icmpV6NaHdr.getSolicitedFlag()
                && nodeIpv6.equals(targetIpv6inNS)
                && dstIPv6Addr.equals(srcIPv6Addr);
          } catch (UnknownHostException e) {
            throw new IllegalArgumentException("Invalid address", e);
          }
        };

    EthernetPacket na = null;
    while (endTs > System.currentTimeMillis() && na == null) {
      List pkts = tRexClient.getRxQueue(portIdx, ipV6NAPktFilter);
      if (!pkts.isEmpty()) {
        na = pkts.get(0);
      }
    }
    tRexClient.removeRxQueue(portIdx);
    if (tRexClient.getPortStatus(portIdx).get().getState().equals("TX")) {
      tRexClient.stopTraffic(portIdx);
    }
    tRexClient.removeAllStreams(portIdx);
    return na;
  }

  private static AbstractMap.SimpleEntry buildVlan(
      IpV6Packet.Builder ipv6Builder, PortVlan vlan) {
    Queue vlanTags = new LinkedList<>(Lists.reverse(vlan.getTags()));
    Packet.Builder resultPayloadBuilder = ipv6Builder;
    EtherType resultEtherType = EtherType.IPV6;

    if (vlanTags.peek() != null) {
      Dot1qVlanTagPacket.Builder vlanInsideBuilder = new Dot1qVlanTagPacket.Builder();
      vlanInsideBuilder
          .type(EtherType.IPV6)
          .vid(vlanTags.poll().shortValue())
          .payloadBuilder(ipv6Builder);

      resultPayloadBuilder = vlanInsideBuilder;
      resultEtherType = EtherType.DOT1Q_VLAN_TAGGED_FRAMES;

      if (vlanTags.peek() != null) {
        Dot1qVlanTagPacket.Builder vlanOutsideBuilder = new Dot1qVlanTagPacket.Builder();
        vlanOutsideBuilder
            .type(EtherType.DOT1Q_VLAN_TAGGED_FRAMES)
            .vid(vlanTags.poll().shortValue())
            .payloadBuilder(vlanInsideBuilder);
        resultPayloadBuilder = vlanOutsideBuilder;
        resultEtherType = QInQ;
      }
    }

    return new AbstractMap.SimpleEntry<>(resultEtherType, resultPayloadBuilder);
  }

  private Map sendNSandIcmpV6Req(
      int portIdx, int timeDuration, String srcMac, String dstIp) {
    long endTs = System.currentTimeMillis() + timeDuration * 1000;
    TRexClientResult portStatusResult = tRexClient.getPortStatus(portIdx);
    PortVlan vlan = portStatusResult.get().getAttr().getVlan();

    Packet pingPkt = buildICMPV6EchoReq(null, srcMac, null, dstIp);
    Packet icmpv6NSPkt =
        buildICMPV6NSPkt(vlan, srcMac, multicastMacFromIPv6(dstIp).toString(), dstIp, null);

    List stlStreams =
        Stream.of(buildStream(pingPkt), buildStream(icmpv6NSPkt)).collect(Collectors.toList());

    tRexClient.startStreamsIntermediate(portIdx, stlStreams);

    Map naIncomingRequests = new HashMap<>();

    Predicate ipV6NAPktFilter =
        etherPkt -> {
          if (!etherPkt.contains(IcmpV6NeighborAdvertisementPacket.class)) {
            return false;
          }
          IcmpV6NeighborAdvertisementHeader icmpV6NaHdr =
              etherPkt.get(IcmpV6NeighborAdvertisementPacket.class).getHeader();

          String nodeIp = icmpV6NaHdr.getTargetAddress().toString().substring(1);

          IpV6Packet.IpV6Header ipV6Header = etherPkt.get(IpV6Packet.class).getHeader();
          String dstAddr = ipV6Header.getDstAddr().toString().substring(1);

          try {
            Inet6Address dstIPv6Addr = (Inet6Address) InetAddress.getByName(dstAddr);
            Inet6Address srcIPv6Addr =
                (Inet6Address) InetAddress.getByName(generateIPv6AddrFromMAC(srcMac));
            return !naIncomingRequests.containsKey(nodeIp) && dstIPv6Addr.equals(srcIPv6Addr);
          } catch (UnknownHostException e) {
            throw new IllegalArgumentException("Invalid address", e);
          }
        };

    while (endTs > System.currentTimeMillis()) {
      tRexClient
          .getRxQueue(portIdx, ipV6NAPktFilter)
          .forEach(
              pkt -> {
                IcmpV6NeighborAdvertisementHeader icmpV6NaHdr =
                    pkt.get(IcmpV6NeighborAdvertisementPacket.class).getHeader();
                String nodeIp = icmpV6NaHdr.getTargetAddress().toString().substring(1);
                naIncomingRequests.put(nodeIp, pkt);
              });
    }
    tRexClient.removeRxQueue(portIdx);
    return naIncomingRequests;
  }

  private static com.cisco.trex.stateless.model.Stream buildStream(Packet pkt) {
    return buildStream(pkt, Collections.emptyList());
  }

  private static com.cisco.trex.stateless.model.Stream buildStream(
      Packet pkt, List instructions) {
    int streamId = (int) (Math.random() * 1000);
    return new com.cisco.trex.stateless.model.Stream(
        streamId,
        true,
        3,
        0.0,
        new StreamMode(
            10,
            10,
            5,
            1.0,
            new StreamModeRate(StreamModeRate.Type.percentage, 100.0),
            StreamMode.Type.single_burst),
        -1,
        pkt,
        new StreamVM("", instructions),
        true,
        false,
        null);
  }

  static Packet buildICMPV6NSPkt(
      PortVlan vlan, String srcMac, String dstMac, String dstIp, String srcIp) {
    EthernetPacket.Builder ethBuilder = new EthernetPacket.Builder();
    try {

      IpV6NeighborDiscoverySourceLinkLayerAddressOption sourceLLAddr =
          new IpV6NeighborDiscoverySourceLinkLayerAddressOption.Builder()
              .correctLengthAtBuild(true)
              .linkLayerAddress(hexStringToByteArray(srcMac.replace(":", "")))
              .build();

      IcmpV6NeighborSolicitationPacket.Builder ipv6NSBuilder =
          new IcmpV6NeighborSolicitationPacket.Builder();
      ipv6NSBuilder
          .options(Arrays.asList(sourceLLAddr))
          .targetAddress((Inet6Address) InetAddress.getByName(dstIp));

      final String specifiedSrcIP = srcIp != null ? srcIp : generateIPv6AddrFromMAC(srcMac);

      // Calculate the Solicited-Node multicast address, RFC 4291 chapter 2.7.1
      String[] destIpParts = dstIp.split(":");
      String multicastIp =
          String.format(
              "FF02::1:FF%s:%s", destIpParts[6].substring(2, 4), destIpParts[7].substring(0, 4));

      IcmpV6CommonPacket.Builder icmpCommonPktBuilder = new IcmpV6CommonPacket.Builder();
      icmpCommonPktBuilder
          .srcAddr((Inet6Address) InetAddress.getByName(specifiedSrcIP))
          .dstAddr((Inet6Address) InetAddress.getByName(multicastIp))
          .type(IcmpV6Type.NEIGHBOR_SOLICITATION)
          .code(IcmpV6Code.NO_CODE)
          .correctChecksumAtBuild(true)
          .payloadBuilder(ipv6NSBuilder);

      IpV6Packet.Builder ipV6Builder = new IpV6Packet.Builder();
      ipV6Builder
          .srcAddr((Inet6Address) InetAddress.getByName(specifiedSrcIP))
          .dstAddr((Inet6Address) InetAddress.getByName(multicastIp))
          .version(IpVersion.IPV6)
          .hopLimit((byte) -1)
          .trafficClass(IpV6SimpleTrafficClass.newInstance((byte) 0))
          .flowLabel(IpV6SimpleFlowLabel.newInstance(0))
          .nextHeader(IpNumber.ICMPV6)
          .payloadBuilder(icmpCommonPktBuilder)
          .correctLengthAtBuild(true);

      AbstractMap.SimpleEntry payload =
          new AbstractMap.SimpleEntry<>(EtherType.IPV6, ipV6Builder);
      if (!vlan.getTags().isEmpty()) {
        payload = buildVlan(ipV6Builder, vlan);
      }

      ethBuilder
          .srcAddr(MacAddress.getByName(srcMac))
          .dstAddr(MacAddress.getByName(dstMac))
          .type(payload.getKey())
          .payloadBuilder(payload.getValue())
          .paddingAtBuild(true);

    } catch (UnknownHostException e) {
      throw new IllegalArgumentException("Invalid address", e);
    }
    return ethBuilder.build();
  }

  /**
   * IPv6 Neighbor Discovery Source Link Layer Address header
   *
   * 

0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type | Length | Link-Layer * Address ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ private static String getLinkLayerAddress(IpV6Packet pkt) { final int TYPE_OFFSET = 0; final int TYPE_SIZE = BYTE_SIZE_IN_BYTES; final int LENGTH_OFFSET = TYPE_OFFSET + TYPE_SIZE; final int LENGTH_SIZE = BYTE_SIZE_IN_BYTES; final int LINK_LAYER_ADDRESS_OFFSET = LENGTH_OFFSET + LENGTH_SIZE; final int LINK_LAYER_ADDRESS_LENGTH = 6; // MAC address IcmpV6NeighborSolicitationPacket nsPkt = pkt.get(IcmpV6NeighborSolicitationPacket.class); IpV6NeighborDiscoveryOption linkLayerAddressOption = nsPkt.getHeader().getOptions().get(0); byte[] linkLayerAddress = ByteArrays.getSubArray( linkLayerAddressOption.getRawData(), LINK_LAYER_ADDRESS_OFFSET, LINK_LAYER_ADDRESS_LENGTH); return ByteArrays.toHexString(linkLayerAddress, ":"); } private static Packet buildICMPV6NAPkt( PortVlan vlan, String srcMac, String dstMac, String dstIp, String srcIP) { final String specifiedSrcIP = srcIP != null ? srcIP : generateIPv6AddrFromMAC(srcMac); EthernetPacket.Builder ethBuilder = new EthernetPacket.Builder(); try { IpV6NeighborDiscoveryTargetLinkLayerAddressOption tLLAddr = new IpV6NeighborDiscoveryTargetLinkLayerAddressOption.Builder() .correctLengthAtBuild(true) .linkLayerAddress(hexStringToByteArray(dstMac.replace(":", ""))) .build(); IcmpV6NeighborAdvertisementPacket.Builder ipv6NABuilder = new IcmpV6NeighborAdvertisementPacket.Builder(); ipv6NABuilder .routerFlag(false) .options(Arrays.asList(tLLAddr)) .solicitedFlag(true) .overrideFlag(true) .targetAddress((Inet6Address) InetAddress.getByName(specifiedSrcIP)); IcmpV6CommonPacket.Builder icmpCommonPktBuilder = new IcmpV6CommonPacket.Builder(); icmpCommonPktBuilder .srcAddr((Inet6Address) InetAddress.getByName(specifiedSrcIP)) .dstAddr((Inet6Address) InetAddress.getByName(dstIp)) .type(IcmpV6Type.NEIGHBOR_ADVERTISEMENT) .code(IcmpV6Code.NO_CODE) .correctChecksumAtBuild(true) .payloadBuilder(ipv6NABuilder); IpV6Packet.Builder ipV6Builder = new IpV6Packet.Builder(); ipV6Builder .srcAddr((Inet6Address) InetAddress.getByName(specifiedSrcIP)) .dstAddr((Inet6Address) InetAddress.getByName(dstIp)) .version(IpVersion.IPV6) .trafficClass(IpV6SimpleTrafficClass.newInstance((byte) 0)) .flowLabel(IpV6SimpleFlowLabel.newInstance(0)) .nextHeader(IpNumber.ICMPV6) .hopLimit((byte) 1) .payloadBuilder(icmpCommonPktBuilder) .correctLengthAtBuild(true); AbstractMap.SimpleEntry payload = new AbstractMap.SimpleEntry<>(EtherType.IPV6, ipV6Builder); if (!vlan.getTags().isEmpty()) { payload = buildVlan(ipV6Builder, vlan); } ethBuilder .srcAddr(MacAddress.getByName(srcMac)) .dstAddr(MacAddress.getByName("33:33:00:00:00:01")) .type(payload.getKey()) .payloadBuilder(payload.getValue()) .paddingAtBuild(true); } catch (UnknownHostException e) { throw new IllegalArgumentException("Invalid address", e); } return ethBuilder.build(); } public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); } return data; } public static EthernetPacket buildICMPV6EchoReq( String srcIp, String srcMacString, String dstMacString, String dstIp, int icmpId, int icmpSeq) { PortVlan vlan = new PortVlan(); vlan.setTags(new ArrayList()); return buildICMPV6EchoReq(vlan, srcIp, srcMacString, dstMacString, dstIp, icmpId, icmpSeq); } public static EthernetPacket buildICMPV6EchoReq( PortVlan vlan, String srcIp, String srcMacString, String dstMacString, String dstIp, int icmpId, int icmpSeq) { /* * * mld_pkt = (Ether(src = self.src_mac, dst = self.dst_mld_mac) / IPv6(src = self.src_ip, dst = * self.dst_mld_ip, hlim = 1) / IPv6ExtHdrHopByHop(options = [RouterAlert(), PadN()]) / * ICMPv6MLReportV2() / MLDv2Addr(type = 4, len = 0, multicast_addr = 'ff02::2')) ping_pkt = * (Ether(src = self.src_mac, dst = dst_mac) / IPv6(src = self.src_ip, dst = self.dst_ip, hlim = * 1) / ICMPv6EchoRequest()) return [self.vlan.embed(mld_pkt), self.vlan.embed(ping_pkt)] */ final String specifiedSrcIP = srcIp != null ? srcIp : generateIPv6AddrFromMAC(srcMacString); IcmpV6EchoRequestPacket.Builder icmpV6ERBuilder = new IcmpV6EchoRequestPacket.Builder(); icmpV6ERBuilder.identifier((short) icmpId).sequenceNumber((short) icmpSeq); IcmpV6CommonPacket.Builder icmpCommonPktBuilder = new IcmpV6CommonPacket.Builder(); try { icmpCommonPktBuilder .srcAddr((Inet6Address) InetAddress.getByName(specifiedSrcIP)) .dstAddr( (Inet6Address) InetAddress.getByName(dstIp != null ? dstIp : "ff02:0:0:0:0:0:0:1")) .type(IcmpV6Type.ECHO_REQUEST) .code(IcmpV6Code.NO_CODE) .correctChecksumAtBuild(true) .payloadBuilder(icmpV6ERBuilder); IpV6Packet.Builder ipV6Builder = new IpV6Packet.Builder(); ipV6Builder .srcAddr((Inet6Address) InetAddress.getByName(specifiedSrcIP)) .dstAddr( (Inet6Address) InetAddress.getByName(dstIp != null ? dstIp : "ff02:0:0:0:0:0:0:1")) .version(IpVersion.IPV6) .trafficClass(IpV6SimpleTrafficClass.newInstance((byte) 0)) .flowLabel(IpV6SimpleFlowLabel.newInstance(0)) .nextHeader(IpNumber.ICMPV6) .hopLimit((byte) 64) .payloadBuilder(icmpCommonPktBuilder) .correctLengthAtBuild(true); MacAddress dstMac; if (dstMacString != null) { dstMac = MacAddress.getByName(dstMacString); } else { dstMac = dstIp == null ? MacAddress.getByName("33:33:00:00:00:01") : multicastMacFromIPv6(dstIp); } EthernetPacket.Builder ethBuilder = new EthernetPacket.Builder(); AbstractMap.SimpleEntry payload = new AbstractMap.SimpleEntry<>(EtherType.IPV6, ipV6Builder); if (!vlan.getTags().isEmpty()) { payload = buildVlan(ipV6Builder, vlan); } ethBuilder .srcAddr(MacAddress.getByName(srcMacString)) .dstAddr(dstMac) .dstAddr(dstMac) .type(payload.getKey()) .payloadBuilder(payload.getValue()) .paddingAtBuild(true); return ethBuilder.build(); } catch (UnknownHostException e) { throw new IllegalArgumentException("Invalid address", e); } } public static EthernetPacket buildICMPV6EchoReq( String srcIp, String srcMacString, String dstMacString, String dstIp) { return buildICMPV6EchoReq(srcIp, srcMacString, dstMacString, dstIp, 0, 0); } /** * Convert to solicitated-node multicast described in RFC 2624 section 7 * * @param ipV6 * @return */ static MacAddress multicastMacFromIPv6(String ipV6) { String expandedIPv6 = expandIPv6Address(ipV6); List ipv6Octets = Arrays.stream(expandedIPv6.split(":")) .map(octet -> Long.parseLong(octet, 16)) .collect(Collectors.toList()); int lastIdx = ipv6Octets.size() - 1; int preLastIdx = ipv6Octets.size() - 2; String macAddressStr = String.format( "33:33:ff:%02x:%02x:%02x", divMod(ipv6Octets.get(preLastIdx), 256)[1], divMod(ipv6Octets.get(lastIdx), 256)[0], divMod(ipv6Octets.get(lastIdx), 256)[1]); return MacAddress.getByName(macAddressStr); } private static long[] divMod(long a, long b) { long[] result = new long[2]; result[1] = a % b; result[0] = (a - result[1]) / b; return result; } private static String expandIPv6Address(String shortAddress) { String[] addressArray = shortAddress.split(":"); if (shortAddress.startsWith(":")) { addressArray[0] = "0"; } else if (shortAddress.endsWith(":")) { addressArray[addressArray.length - 1] = "0"; } for (int i = 0; i < addressArray.length; i++) { if (addressArray[i] == null || addressArray[i].isEmpty()) { StringBuilder sb = new StringBuilder(); int leftSize = i + 1; String[] left = new String[i + 1]; System.arraycopy(addressArray, 0, left, 0, leftSize); sb.append(Arrays.stream(left).collect(Collectors.joining(":"))); String[] expanded = Stream.generate(() -> "0").limit(9 - addressArray.length).toArray(String[]::new); sb.append(Arrays.stream(expanded).collect(Collectors.joining(":"))); sb.append(":"); int rightSize = addressArray.length - i - 1; String[] right = new String[rightSize]; System.arraycopy(addressArray, i + 1, right, 0, rightSize); sb.append(Arrays.stream(right).collect(Collectors.joining(":"))); return sb.toString(); } } return Arrays.stream(addressArray).collect(Collectors.joining(":")); } static String generateIPv6AddrFromMAC(String mac) { String prefix = "fe80"; List macOctets = Arrays.stream(mac.split(":")) .map(octet -> Integer.parseInt(octet, 16)) .collect(Collectors.toList()); // insert ff fe in the middle macOctets.add(3, 0xfe); macOctets.add(3, 0xff); // invert second bind macOctets.set(0, macOctets.get(0) ^ 0b1 << 1); List strOctets = new ArrayList<>(); for (int i = 0; i < macOctets.size(); i += 2) { strOctets.add( String.format( "%s%s", StringUtils.leftPad(Integer.toHexString(macOctets.get(i)), 2, "0"), StringUtils.leftPad(Integer.toHexString(macOctets.get(i + 1)), 2, "0"))); } return String.format("%s::%s", prefix, String.join(":", strOctets)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy