/** * Sends an APR request for the target IP address to all ports except in-port. * * @param deviceId Switch device ID * @param targetAddress target IP address for ARP * @param inPort in-port */ public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) { byte[] senderMacAddress; byte[] senderIpAddress; try { senderMacAddress = config.getDeviceMac(deviceId).toBytes(); senderIpAddress = config.getRouterIp(deviceId).toOctets(); } catch (DeviceConfigNotFoundException e) { log.warn(e.getMessage() + " Aborting sendArpRequest."); return; } ARP arpRequest = new ARP(); arpRequest .setHardwareType(ARP.HW_TYPE_ETHERNET) .setProtocolType(ARP.PROTO_TYPE_IP) .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH) .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH) .setOpCode(ARP.OP_REQUEST) .setSenderHardwareAddress(senderMacAddress) .setTargetHardwareAddress(MacAddress.ZERO.toBytes()) .setSenderProtocolAddress(senderIpAddress) .setTargetProtocolAddress(targetAddress.toOctets()); Ethernet eth = new Ethernet(); eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes()) .setSourceMACAddress(senderMacAddress) .setEtherType(Ethernet.TYPE_ARP) .setPayload(arpRequest); removeVlanAndFlood(eth, inPort); }
/** * Monitors hosts on the dataplane to detect changes in host data. * * <p>The HostMonitor can monitor hosts that have already been detected for changes. At an * application's request, it can also monitor and actively probe for hosts that have not yet been * detected (specified by IP address). */ public class HostMonitor implements TimerTask { private PacketService packetService; private HostManager hostManager; private InterfaceService interfaceService; private final Set<IpAddress> monitoredAddresses; private final ConcurrentMap<ProviderId, HostProvider> hostProviders; private static final long DEFAULT_PROBE_RATE = 30000; // milliseconds private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes(); private long probeRate = DEFAULT_PROBE_RATE; private Timeout timeout; /** * Creates a new host monitor. * * @param packetService packet service used to send packets on the data plane * @param hostManager host manager used to look up host information and probe existing hosts * @param interfaceService interface service for interface information */ public HostMonitor( PacketService packetService, HostManager hostManager, InterfaceService interfaceService) { this.packetService = packetService; this.hostManager = hostManager; this.interfaceService = interfaceService; monitoredAddresses = Collections.newSetFromMap(new ConcurrentHashMap<>()); hostProviders = new ConcurrentHashMap<>(); } /** * Adds an IP address to be monitored by the host monitor. The monitor will periodically probe the * host to detect changes. * * @param ip IP address of the host to monitor */ void addMonitoringFor(IpAddress ip) { monitoredAddresses.add(ip); } /** * Stops monitoring the given IP address. * * @param ip IP address to stop monitoring on */ void stopMonitoring(IpAddress ip) { monitoredAddresses.remove(ip); } /** Starts the host monitor. Does nothing if the monitor is already running. */ void start() { synchronized (this) { if (timeout == null) { timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS); } } } /** Stops the host monitor. */ void shutdown() { synchronized (this) { timeout.cancel(); timeout = null; } } /** * Registers a host provider with the host monitor. The monitor can use the provider to probe * hosts. * * @param provider the host provider to register */ void registerHostProvider(HostProvider provider) { hostProviders.put(provider.id(), provider); } @Override public void run(Timeout timeout) throws Exception { for (IpAddress ip : monitoredAddresses) { Set<Host> hosts = hostManager.getHostsByIp(ip); if (hosts.isEmpty()) { sendArpNdpRequest(ip); } else { for (Host host : hosts) { HostProvider provider = hostProviders.get(host.providerId()); if (provider == null) { hostProviders.remove(host.providerId(), null); } else { provider.triggerProbe(host); } } } } this.timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS); } /** * Sends an ARP or Neighbor Discovery Protocol request for the given IP address. * * @param targetIp IP address to send the request for */ private void sendArpNdpRequest(IpAddress targetIp) { Interface intf = interfaceService.getMatchingInterface(targetIp); if (intf == null) { return; } for (InterfaceIpAddress ia : intf.ipAddresses()) { if (ia.subnetAddress().contains(targetIp)) { sendArpNdpProbe(intf.connectPoint(), targetIp, ia.ipAddress(), intf.mac(), intf.vlan()); } } } private void sendArpNdpProbe( ConnectPoint connectPoint, IpAddress targetIp, IpAddress sourceIp, MacAddress sourceMac, VlanId vlan) { Ethernet probePacket = null; if (targetIp.isIp4()) { // IPv4: Use ARP probePacket = buildArpRequest(targetIp, sourceIp, sourceMac, vlan); } else { // IPv6: Use Neighbor Discovery probePacket = buildNdpRequest(targetIp, sourceIp, sourceMac, vlan); } List<Instruction> instructions = new ArrayList<>(); instructions.add(Instructions.createOutput(connectPoint.port())); TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build(); OutboundPacket outboundPacket = new DefaultOutboundPacket( connectPoint.deviceId(), treatment, ByteBuffer.wrap(probePacket.serialize())); packetService.emit(outboundPacket); } private Ethernet buildArpRequest( IpAddress targetIp, IpAddress sourceIp, MacAddress sourceMac, VlanId vlan) { ARP arp = new ARP(); arp.setHardwareType(ARP.HW_TYPE_ETHERNET) .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH) .setProtocolType(ARP.PROTO_TYPE_IP) .setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH) .setOpCode(ARP.OP_REQUEST); arp.setSenderHardwareAddress(sourceMac.toBytes()) .setSenderProtocolAddress(sourceIp.toOctets()) .setTargetHardwareAddress(ZERO_MAC_ADDRESS) .setTargetProtocolAddress(targetIp.toOctets()); Ethernet ethernet = new Ethernet(); ethernet .setEtherType(Ethernet.TYPE_ARP) .setDestinationMACAddress(MacAddress.BROADCAST) .setSourceMACAddress(sourceMac) .setPayload(arp); if (!vlan.equals(VlanId.NONE)) { ethernet.setVlanID(vlan.toShort()); } ethernet.setPad(true); return ethernet; } private Ethernet buildNdpRequest( IpAddress targetIp, IpAddress sourceIp, MacAddress sourceMac, VlanId vlan) { // Create the Ethernet packet Ethernet ethernet = new Ethernet(); ethernet .setEtherType(Ethernet.TYPE_IPV6) .setDestinationMACAddress(MacAddress.BROADCAST) .setSourceMACAddress(sourceMac); if (!vlan.equals(VlanId.NONE)) { ethernet.setVlanID(vlan.toShort()); } // // Create the IPv6 packet // // TODO: The destination IP address should be the // solicited-node multicast address IPv6 ipv6 = new IPv6(); ipv6.setSourceAddress(sourceIp.toOctets()); ipv6.setDestinationAddress(targetIp.toOctets()); ipv6.setHopLimit((byte) 255); // Create the ICMPv6 packet ICMP6 icmp6 = new ICMP6(); icmp6.setIcmpType(ICMP6.NEIGHBOR_SOLICITATION); icmp6.setIcmpCode((byte) 0); // Create the Neighbor Solication packet NeighborSolicitation ns = new NeighborSolicitation(); ns.setTargetAddress(targetIp.toOctets()); ns.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS, sourceMac.toBytes()); icmp6.setPayload(ns); ipv6.setPayload(icmp6); ethernet.setPayload(ipv6); return ethernet; } }