/*_########################################################################## _## _## Copyright (C) 2013 Kaito Yamada _## _########################################################################## */ package com.github.kaitoy.sneo.network.protocol; import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.pcap4j.packet.IcmpV6CommonPacket; import org.pcap4j.packet.IcmpV6CommonPacket.IpV6NeighborDiscoveryOption; import org.pcap4j.packet.IcmpV6DestinationUnreachablePacket; import org.pcap4j.packet.IcmpV6EchoReplyPacket; import org.pcap4j.packet.IcmpV6EchoRequestPacket; import org.pcap4j.packet.IcmpV6NeighborAdvertisementPacket; import org.pcap4j.packet.IcmpV6NeighborSolicitationPacket; import org.pcap4j.packet.IcmpV6TimeExceededPacket; import org.pcap4j.packet.IpV6NeighborDiscoverySourceLinkLayerAddressOption; import org.pcap4j.packet.IpV6NeighborDiscoveryTargetLinkLayerAddressOption; import org.pcap4j.packet.IpV6Packet; import org.pcap4j.packet.Packet; import org.pcap4j.packet.SimpleBuilder; import org.pcap4j.packet.namednumber.IcmpV6Code; import org.pcap4j.packet.namednumber.IcmpV6Type; import org.pcap4j.packet.namednumber.IpV6NeighborDiscoveryOptionType; import org.pcap4j.util.MacAddress; import com.github.kaitoy.sneo.network.NetworkInterface; import com.github.kaitoy.sneo.network.NetworkPropertiesLoader; import com.github.kaitoy.sneo.network.NifIpAddress; import com.github.kaitoy.sneo.network.NifIpV6Address; import com.github.kaitoy.sneo.network.Node; import com.github.kaitoy.sneo.network.SendPacketException; public final class IcmpV6Helper extends NeighborDiscoveryHelper { private static NdpRequestSender ndpReqSender = new NdpRequestSender(); private IcmpV6Helper() { throw new AssertionError(); } public static NdpCache newNdpCache(long cacheLife) { return new NdpCache(cacheLife); } public static MacAddress resolveVirtualAddress(InetAddress ipAddress) { return NeighborDiscoveryHelper.resolveVirtualAddress(ipAddress); } public static MacAddress resolveRealAddress( InetAddress targetIpAddr, Node node, NetworkInterface nif, NdpCache ndpCache, long resolveTimeout, TimeUnit unit ) { return resolveRealAddress( ndpReqSender, targetIpAddr, node, nif, ndpCache, resolveTimeout, unit ); } public static void cache(Packet packet, NdpCache ndpCache) { if (packet.contains(IcmpV6NeighborSolicitationPacket.class)) { cacheByNs(packet, ndpCache); } else if (packet.contains(IcmpV6NeighborAdvertisementPacket.class)) { cacheByNa(packet, ndpCache); } } private static void cacheByNs(Packet packet, NdpCache ndpCache) { IpV6Packet ipv6Packet = packet.get(IpV6Packet.class); if (ipv6Packet == null) { return; } Inet6Address srcAddr = ipv6Packet.getHeader().getSrcAddr(); if (srcAddr.equals(IpV6Helper.UNSPECIFIED_ADDRESS)) { return; } IcmpV6NeighborSolicitationPacket nsPacket = packet.get(IcmpV6NeighborSolicitationPacket.class); IpV6NeighborDiscoverySourceLinkLayerAddressOption srcLinkOpt = null; for (IpV6NeighborDiscoveryOption opt: nsPacket.getHeader().getOptions()) { if (opt.getType().equals(IpV6NeighborDiscoveryOptionType.SOURCE_LINK_LAYER_ADDRESS)) { srcLinkOpt = (IpV6NeighborDiscoverySourceLinkLayerAddressOption)opt; break; } } if (srcLinkOpt == null) { return; } // if new, isRouter flag of Neighbor Cache is false. // if update, isRouter is not changed. NeighborDiscoveryHelper.cache(ndpCache, srcAddr, srcLinkOpt.getLinkLayerAddressAsMacAddress()); } private static void cacheByNa(Packet packet, NdpCache ndpCache) { IcmpV6NeighborAdvertisementPacket naPacket = packet.get(IcmpV6NeighborAdvertisementPacket.class); InetAddress ipAddr = naPacket.getHeader().getTargetAddress(); MacAddress macAddr = null; for (IpV6NeighborDiscoveryOption opt: naPacket.getHeader().getOptions()) { if (opt.getType().equals(IpV6NeighborDiscoveryOptionType.TARGET_LINK_LAYER_ADDRESS)) { macAddr = ((IpV6NeighborDiscoveryTargetLinkLayerAddressOption)opt).getLinkLayerAddressAsMacAddress(); } } if (macAddr == null) { return; } // According to RFC 2461, If no entry exists, the advertisement SHOULD be silently discarded. // But cache it here with no check for entry existance. NeighborDiscoveryHelper.cache(ndpCache, ipAddr, macAddr); } public static void sendSolicitedNeighborAdvertisement( Packet invokingPacket,Node node, NetworkInterface nif ) { IpV6Packet ipPacket = invokingPacket.get(IpV6Packet.class); IcmpV6NeighborSolicitationPacket nsPacket = invokingPacket.get(IcmpV6NeighborSolicitationPacket.class); if (nsPacket == null || ipPacket == null) { throw new IllegalArgumentException(invokingPacket.toString()); } Inet6Address nsSrcIpAddr = ipPacket.getHeader().getSrcAddr(); Inet6Address nsTargetAddr = nsPacket.getHeader().getTargetAddress(); List<IpV6NeighborDiscoveryOption> opts = new ArrayList<IpV6NeighborDiscoveryOption>(); IpV6NeighborDiscoveryTargetLinkLayerAddressOption.Builder optBuilder = new IpV6NeighborDiscoveryTargetLinkLayerAddressOption.Builder(); optBuilder.linkLayerAddress(nif.getMacAddress().getAddress()) .correctLengthAtBuild(true); opts.add(optBuilder.build()); IcmpV6NeighborAdvertisementPacket.Builder naBuilder = new IcmpV6NeighborAdvertisementPacket.Builder(); naBuilder .routerFlag(true) .overrideFlag(true) .targetAddress(nsTargetAddr) .options(opts); Inet6Address naDstIpAddr; if (nsSrcIpAddr.equals(IpV6Helper.UNSPECIFIED_ADDRESS)) { naBuilder.solicitedFlag(false); naDstIpAddr = IpV6Helper.LINK_LOCAL_ALL_NODES_ADDRESS; } else { naBuilder.solicitedFlag(true); naDstIpAddr = nsSrcIpAddr; } IcmpV6CommonPacket.Builder icmpV6Common = new IcmpV6CommonPacket.Builder(); icmpV6Common.type(IcmpV6Type.NEIGHBOR_ADVERTISEMENT) .code(IcmpV6Code.NO_CODE) .payloadBuilder(naBuilder) .srcAddr(nsTargetAddr) .dstAddr(naDstIpAddr) .correctChecksumAtBuild(true); try { node.sendL4Packet( icmpV6Common.build(), nsTargetAddr, naDstIpAddr, nif, 255 ); } catch (SendPacketException e) { // TODO handle the exception. e.printStackTrace(); } } public static void sendEchoReply(Packet packet, Node node, NetworkInterface nif) { IcmpV6EchoRequestPacket echo = packet.get(IcmpV6EchoRequestPacket.class); Packet.Builder outer = packet.getBuilder().getOuterOf(IcmpV6EchoRequestPacket.Builder.class); IpV6Packet ipv6 = packet.get(IpV6Packet.class); if ( echo == null || ipv6 == null || outer == null || !(outer instanceof IcmpV6CommonPacket.Builder) ) { throw new IllegalArgumentException(packet.toString()); } IcmpV6EchoReplyPacket.Builder repb = new IcmpV6EchoReplyPacket.Builder(); repb.identifier(echo.getHeader().getIdentifier()) .sequenceNumber(echo.getHeader().getSequenceNumber()) .payloadBuilder(new SimpleBuilder(echo.getPayload())); Inet6Address repSrcAddr = ipv6.getHeader().getDstAddr(); Inet6Address repDstAddr = ipv6.getHeader().getSrcAddr(); ((IcmpV6CommonPacket.Builder)outer) .type(IcmpV6Type.ECHO_REPLY) .srcAddr(repSrcAddr) .dstAddr(repDstAddr) .payloadBuilder(repb) .correctChecksumAtBuild(true); try { node.sendL4Packet( outer.build(), repSrcAddr, repDstAddr, nif ); } catch (SendPacketException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } } public static void sendErrorMessage( IcmpV6Type type, IcmpV6Code code, Packet packet, Node node, NetworkInterface nif ) { IpV6Packet ipv6Packet = packet.get(IpV6Packet.class); if (ipv6Packet == null) { throw new IllegalArgumentException(packet.toString()); } Packet.Builder icmpV6Inet; int size = NetworkPropertiesLoader.getMtu() - 40 - 8; if (type.equals(IcmpV6Type.DESTINATION_UNREACHABLE)) { icmpV6Inet = new IcmpV6DestinationUnreachablePacket.Builder() .payload( org.pcap4j.util.IcmpV6Helper .makePacketForInvokingPacketField(ipv6Packet, size) ); } else if (type.equals(IcmpV6Type.TIME_EXCEEDED)) { icmpV6Inet = new IcmpV6TimeExceededPacket.Builder() .payload( org.pcap4j.util.IcmpV6Helper .makePacketForInvokingPacketField(ipv6Packet, size) ); } else { throw new IllegalArgumentException(packet.toString()); } Inet6Address errDstAddr = ipv6Packet.getHeader().getSrcAddr(); Inet6Address errSrcAddr = null; for (NifIpAddress nifIp: nif.getIpAddresses()) { if (nifIp instanceof NifIpV6Address) { errSrcAddr = ((NifIpV6Address)nifIp).getIpAddr(); } } if (errSrcAddr == null) { throw new IllegalArgumentException(nif.getName() + " doesn't have IPv6 addresses."); } IcmpV6CommonPacket.Builder icmpV6Common = new IcmpV6CommonPacket.Builder(); icmpV6Common.type(type) .code(code) .srcAddr(errSrcAddr) .dstAddr(errDstAddr) .payloadBuilder(icmpV6Inet) .correctChecksumAtBuild(true); try { node.sendL4Packet( icmpV6Common.build(), errSrcAddr, errDstAddr, nif ); } catch (SendPacketException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } } private static class NdpRequestSender implements RequestSender { public void sendRequest( InetAddress targetIpAddr, Node node, NetworkInterface nif ) { InetAddress srcIpAddr = null; for (NifIpAddress addr: nif.getIpAddresses()) { if (addr.getIpAddr() instanceof Inet6Address) { srcIpAddr = addr.getIpAddr(); } } if (srcIpAddr == null) { throw new IllegalArgumentException("No IPv6 address is found in " + nif); } List<IpV6NeighborDiscoveryOption> opts = new ArrayList<IpV6NeighborDiscoveryOption>(); IpV6NeighborDiscoverySourceLinkLayerAddressOption.Builder optBuilder = new IpV6NeighborDiscoverySourceLinkLayerAddressOption.Builder(); optBuilder.linkLayerAddress(nif.getMacAddress().getAddress()) .correctLengthAtBuild(true); opts.add(optBuilder.build()); IcmpV6NeighborSolicitationPacket.Builder nsBuilder = new IcmpV6NeighborSolicitationPacket.Builder() .targetAddress((Inet6Address)targetIpAddr) .options(opts); Inet6Address nsDstAddr = IpV6Helper.generateSolicitedNodeMulticastAddress((Inet6Address)targetIpAddr); IcmpV6CommonPacket.Builder commonBuilder = new IcmpV6CommonPacket.Builder(); commonBuilder .type(IcmpV6Type.NEIGHBOR_SOLICITATION) .code(IcmpV6Code.NO_CODE) .srcAddr((Inet6Address)srcIpAddr) .dstAddr(nsDstAddr) .correctChecksumAtBuild(true) .payloadBuilder(nsBuilder); try { node.sendL4Packet( commonBuilder.build(), srcIpAddr, nsDstAddr, nif, 255 ); } catch (SendPacketException e) { // TODO handle the exception. e.printStackTrace(); } } } public static class NdpCache extends NeighborCache { private NdpCache(long cacheLife) { super(cacheLife); } } }