3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
13 package org.opendaylight.controller.arphandler.internal;
15 import java.net.InetAddress;
16 import java.net.UnknownHostException;
17 import java.util.Arrays;
18 import java.util.Collections;
19 import java.util.HashSet;
21 import java.util.Timer;
22 import java.util.TimerTask;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentMap;
26 import org.opendaylight.controller.hosttracker.IfHostListener;
27 import org.opendaylight.controller.hosttracker.IfIptoHost;
28 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
29 import org.opendaylight.controller.hosttracker.hostAware.IHostFinder;
30 import org.opendaylight.controller.sal.core.ConstructionException;
31 import org.opendaylight.controller.sal.core.Node;
32 import org.opendaylight.controller.sal.core.NodeConnector;
33 import org.opendaylight.controller.sal.packet.ARP;
34 import org.opendaylight.controller.sal.packet.BitBufferHelper;
35 import org.opendaylight.controller.sal.packet.Ethernet;
36 import org.opendaylight.controller.sal.packet.IDataPacketService;
37 import org.opendaylight.controller.sal.packet.IListenDataPacket;
38 import org.opendaylight.controller.sal.packet.IPv4;
39 import org.opendaylight.controller.sal.packet.Packet;
40 import org.opendaylight.controller.sal.packet.PacketResult;
41 import org.opendaylight.controller.sal.packet.RawPacket;
42 import org.opendaylight.controller.sal.utils.EtherTypes;
43 import org.opendaylight.controller.sal.utils.HexEncode;
44 import org.opendaylight.controller.sal.utils.NetUtils;
45 import org.opendaylight.controller.switchmanager.ISwitchManager;
46 import org.opendaylight.controller.switchmanager.Subnet;
47 import org.opendaylight.controller.topologymanager.ITopologyManager;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
51 public class ArpHandler implements IHostFinder, IListenDataPacket {
52 private static final Logger logger = LoggerFactory
53 .getLogger(ArpHandler.class);
54 private IfIptoHost hostTracker = null;
55 private ISwitchManager switchManager = null;
56 private ITopologyManager topologyManager;
57 private IDataPacketService dataPacketService = null;
58 private Set<IfHostListener> hostListener = Collections
59 .synchronizedSet(new HashSet<IfHostListener>());
60 private ConcurrentMap<InetAddress, Set<HostNodeConnector>> arpRequestors;
61 private ConcurrentMap<InetAddress, Short> countDownTimers;
62 private Timer periodicTimer;
64 void setHostListener(IfHostListener s) {
65 if (this.hostListener != null) {
66 this.hostListener.add(s);
70 void unsetHostListener(IfHostListener s) {
71 if (this.hostListener != null) {
72 this.hostListener.remove(s);
76 void setDataPacketService(IDataPacketService s) {
77 this.dataPacketService = s;
80 void unsetDataPacketService(IDataPacketService s) {
81 if (this.dataPacketService == s) {
82 this.dataPacketService = null;
86 public IfIptoHost getHostTracker() {
90 public void setHostTracker(IfIptoHost hostTracker) {
91 logger.debug("Setting HostTracker");
92 this.hostTracker = hostTracker;
95 public void unsetHostTracker(IfIptoHost s) {
96 logger.debug("UNSetting HostTracker");
97 if (this.hostTracker == s) {
98 this.hostTracker = null;
102 public void setTopologyManager(ITopologyManager tm) {
103 this.topologyManager = tm;
106 public void unsetTopologyManager(ITopologyManager tm) {
107 if (this.topologyManager == tm) {
108 this.topologyManager = null;
112 protected void sendARPReply(NodeConnector p, byte[] sMAC, InetAddress sIP,
113 byte[] tMAC, InetAddress tIP) {
114 byte[] senderIP = sIP.getAddress();
115 byte[] targetIP = tIP.getAddress();
117 arp.setHardwareType(ARP.HW_TYPE_ETHERNET).setProtocolType(
118 EtherTypes.IPv4.shortValue())
119 .setHardwareAddressLength((byte) 6).setProtocolAddressLength(
120 (byte) 4).setOpCode(ARP.REPLY)
121 .setSenderHardwareAddress(sMAC).setSenderProtocolAddress(
122 senderIP).setTargetHardwareAddress(tMAC)
123 .setTargetProtocolAddress(targetIP);
125 Ethernet ethernet = new Ethernet();
126 ethernet.setSourceMACAddress(sMAC).setDestinationMACAddress(tMAC)
127 .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
129 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
130 destPkt.setOutgoingNodeConnector(p);
132 this.dataPacketService.transmitDataPacket(destPkt);
135 private boolean isBroadcastMAC(byte[] mac) {
136 if (BitBufferHelper.toNumber(mac) == 0xffffffffffffL) { //TODO: implement this in our Ethernet
142 private boolean isUnicastMAC(byte[] mac) {
143 if ((BitBufferHelper.toNumber(mac) & 0x010000000000L) == 0) {
149 protected void handleARPPacket(Ethernet eHeader, ARP pkt, NodeConnector p) {
150 if (pkt.getOpCode() == 0x1) {
151 logger.debug("Received ARP REQUEST Packet from NodeConnector: {}",
154 logger.debug("Received ARP REPLY Packet from NodeConnector: {}",
157 InetAddress targetIP = null;
159 targetIP = InetAddress.getByAddress(pkt.getTargetProtocolAddress());
160 } catch (UnknownHostException e1) {
163 InetAddress sourceIP = null;
165 sourceIP = InetAddress.getByAddress(pkt.getSenderProtocolAddress());
166 } catch (UnknownHostException e1) {
169 byte[] targetMAC = eHeader.getDestinationMACAddress();
170 byte[] sourceMAC = eHeader.getSourceMACAddress();
173 * Sanity Check; drop ARP packets originated by the controller itself.
174 * This is to avoid continuous flooding
176 if (Arrays.equals(sourceMAC, getControllerMAC())) {
177 if (logger.isDebugEnabled()) {
179 "Receive the self originated packet (srcMAC {}) --> DROP",
180 HexEncode.bytesToHexString(sourceMAC));
185 Subnet subnet = null;
186 if (switchManager != null) {
187 subnet = switchManager.getSubnetByNetworkAddress(sourceIP);
189 if (subnet == null) {
190 logger.debug("can't find subnet matching {}, drop packet",sourceIP);
193 logger.debug("Found {} matching {}", subnet, sourceIP);
195 * Make sure that the host is a legitimate member of this subnet
197 if (!subnet.hasNodeConnector(p)) {
198 logger.debug("{} showing up on {} does not belong to {}",
199 new Object[] { sourceIP, p, subnet });
203 HostNodeConnector requestor = null;
204 if (isUnicastMAC(sourceMAC)) {
205 // TODO For not this is only OPENFLOW but we need to fix this
206 if (p.getType().equals(
207 NodeConnector.NodeConnectorIDType.OPENFLOW)) {
209 requestor = new HostNodeConnector(sourceMAC, sourceIP, p, subnet
211 } catch (ConstructionException e) {
215 * Learn host from the received ARP REQ/REPLY, inform
218 logger.debug("Inform Host tracker of new host {}", requestor.getNetworkAddress());
219 synchronized (this.hostListener) {
220 for (IfHostListener listener : this.hostListener) {
221 listener.hostListener(requestor);
227 * Gratuitous ARP. If there are hosts (in arpRequestors) waiting for the
228 * ARP reply for this sourceIP, it's time to generate the reply and it
231 if (sourceIP.equals(targetIP)) {
232 generateAndSendReply(sourceIP, sourceMAC);
237 * ARP Reply. If there are hosts (in arpRequesttors) waiting for the ARP
238 * reply for this sourceIP, it's time to generate the reply and it to
241 if (pkt.getOpCode() != ARP.REQUEST) {
242 generateAndSendReply(sourceIP, sourceMAC);
247 * ARP Request Handling:
248 * If targetIP is the IP of the subnet, reply with ARP REPLY
249 * If targetIP is a known host, PROXY ARP (by sending ARP REPLY) on behalf of known target hosts.
250 * For unknown target hosts, generate and send an ARP request to ALL switches/ports using
251 * the IP address defined in the subnet as source address
254 * Send ARP reply if target IP is gateway IP
256 if ((targetIP.equals(subnet.getNetworkAddress()))
257 && (isBroadcastMAC(targetMAC) || Arrays.equals(targetMAC,
258 getControllerMAC()))) {
259 sendARPReply(p, getControllerMAC(), targetIP, pkt
260 .getSenderHardwareAddress(), sourceIP);
265 * unknown host, initiate ARP request
267 HostNodeConnector host = hostTracker.hostQuery(targetIP);
269 // add the requestor to the list so that we can replay the reply
270 // when the host responds
271 if (requestor != null) {
272 Set<HostNodeConnector> requestorSet = arpRequestors
274 if ((requestorSet == null) || requestorSet.isEmpty()) {
275 requestorSet = new HashSet<HostNodeConnector>();
276 countDownTimers.put(targetIP, (short) 2); // set max timeout
279 requestorSet.add(requestor);
280 arpRequestors.put(targetIP, requestorSet);
282 sendBcastARPRequest(targetIP, subnet);
286 * Known target host, send ARP REPLY
287 * make sure that targetMAC matches the host's MAC if it is not broadcastMAC
289 if (isBroadcastMAC(targetMAC)
290 || Arrays.equals(host.getDataLayerAddressBytes(), targetMAC)) {
291 sendARPReply(p, host.getDataLayerAddressBytes(), host
292 .getNetworkAddress(), pkt.getSenderHardwareAddress(),
297 * target target MAC has been changed. For now, discard it.
298 * TODO: We may need to send unicast ARP REQUEST on behalf of the
299 * target back to the sender to trigger the sender to
307 * Send a broadcast ARP Request to the switch/ ports using
308 * the networkAddress of the subnet as sender IP
309 * the controller's MAC as sender MAC
310 * the targetIP as the target Network Address
312 protected void sendBcastARPRequest(InetAddress targetIP, Subnet subnet) {
313 Set<NodeConnector> nodeConnectors;
314 if (subnet.isFlatLayer2()) {
315 nodeConnectors = new HashSet<NodeConnector>();
316 for (Node n : this.switchManager.getNodes()) {
317 nodeConnectors.addAll(this.switchManager
318 .getUpNodeConnectors(n));
321 nodeConnectors = subnet.getNodeConnectors();
323 for (NodeConnector p : nodeConnectors) {
324 if (topologyManager.isInternal(p)) {
328 byte[] senderIP = subnet.getNetworkAddress().getAddress();
329 byte[] targetIPB = targetIP.getAddress();
330 arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
331 .setProtocolType(EtherTypes.IPv4.shortValue())
332 .setHardwareAddressLength((byte) 6)
333 .setProtocolAddressLength((byte) 4)
334 .setOpCode(ARP.REQUEST)
335 .setSenderHardwareAddress(getControllerMAC())
336 .setSenderProtocolAddress(senderIP)
337 .setTargetHardwareAddress(new byte[] { (byte) 0, (byte) 0,
339 (byte) 0, (byte) 0 })
340 .setTargetProtocolAddress(targetIPB);
342 Ethernet ethernet = new Ethernet();
343 ethernet.setSourceMACAddress(getControllerMAC())
344 .setDestinationMACAddress(new byte[] { (byte) -1,
350 .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
352 // TODO For now send port-by-port, see how to optimize to
353 // send to multiple ports at once
354 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
355 destPkt.setOutgoingNodeConnector(p);
357 this.dataPacketService.transmitDataPacket(destPkt);
362 * Send a unicast ARP Request to the known host on a specific switch/port as
363 * defined in the host.
364 * The sender IP is the networkAddress of the subnet
365 * The sender MAC is the controller's MAC
367 protected void sendUcastARPRequest(HostNodeConnector host, Subnet subnet) {
368 //Long swID = host.getnodeconnectornodeId();
369 //Short portID = host.getnodeconnectorportId();
370 //Node n = NodeCreator.createOFNode(swID);
371 Node n = host.getnodeconnectorNode();
373 logger.error("cannot send UcastARP because cannot extract node "
374 + "from HostNodeConnector: {}", host);
377 NodeConnector outPort = host.getnodeConnector();
378 if (outPort == null) {
379 logger.error("cannot send UcastARP because cannot extract "
380 + "outPort from HostNodeConnector: {}", host);
384 byte[] senderIP = subnet.getNetworkAddress().getAddress();
385 byte[] targetIP = host.getNetworkAddress().getAddress();
386 byte[] targetMAC = host.getDataLayerAddressBytes();
388 arp.setHardwareType(ARP.HW_TYPE_ETHERNET).setProtocolType(
389 EtherTypes.IPv4.shortValue())
390 .setHardwareAddressLength((byte) 6).setProtocolAddressLength(
391 (byte) 4).setOpCode(ARP.REQUEST)
392 .setSenderHardwareAddress(getControllerMAC())
393 .setSenderProtocolAddress(senderIP).setTargetHardwareAddress(
394 targetMAC).setTargetProtocolAddress(targetIP);
396 Ethernet ethernet = new Ethernet();
397 ethernet.setSourceMACAddress(getControllerMAC())
398 .setDestinationMACAddress(targetMAC).setEtherType(
399 EtherTypes.ARP.shortValue()).setPayload(arp);
401 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
402 destPkt.setOutgoingNodeConnector(outPort);
404 this.dataPacketService.transmitDataPacket(destPkt);
407 public void find(InetAddress networkAddress) {
408 logger.debug("Received find IP {}", networkAddress);
410 Subnet subnet = null;
411 if (switchManager != null) {
412 subnet = switchManager.getSubnetByNetworkAddress(networkAddress);
414 if (subnet == null) {
415 logger.debug("can't find subnet matching IP {}", networkAddress);
418 logger.debug("found subnet {}", subnet);
420 // send a broadcast ARP Request to this interface
421 sendBcastARPRequest(networkAddress, subnet);
425 * Probe the host by sending a unicast ARP Request to the host
427 public void probe(HostNodeConnector host) {
428 logger.debug("Received probe host {}", host);
430 Subnet subnet = null;
431 if (switchManager != null) {
432 subnet = switchManager.getSubnetByNetworkAddress(host
433 .getNetworkAddress());
435 if (subnet == null) {
436 logger.debug("can't find subnet matching {}", host
437 .getNetworkAddress());
440 sendUcastARPRequest(host, subnet);
444 * An IP packet is punted to the controller, this means that the
445 * destination host is not known to the controller.
446 * Need to discover it by sending a Broadcast ARP Request
448 protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p) {
449 InetAddress dIP = null;
451 dIP = InetAddress.getByAddress(NetUtils.intToByteArray4(pkt
452 .getDestinationAddress()));
453 } catch (UnknownHostException e1) {
457 Subnet subnet = null;
458 if (switchManager != null) {
459 subnet = switchManager.getSubnetByNetworkAddress(dIP);
461 if (subnet == null) {
462 logger.debug("can't find subnet matching {}, drop packet", dIP);
465 logger.debug("Found {} matching {}", subnet, dIP);
467 * unknown destination host, initiate ARP request
469 sendBcastARPRequest(dIP, subnet);
473 public byte[] getControllerMAC() {
474 if (switchManager == null) {
477 return switchManager.getControllerMAC();
481 * Function called by the dependency manager when all the required
482 * dependencies are satisfied
486 arpRequestors = new ConcurrentHashMap<InetAddress, Set<HostNodeConnector>>();
487 countDownTimers = new ConcurrentHashMap<InetAddress, Short>();
491 * Function called by the dependency manager when at least one
492 * dependency become unsatisfied or when the component is shutting
493 * down because for example bundle is being stopped.
500 * Function called by dependency manager after "init ()" is called
501 * and after the services provided by the class are registered in
502 * the service registry
506 startPeriodicTimer();
510 * Function called by the dependency manager before the services
511 * exported by the component are unregistered, this will be
512 * followed by a "destroy ()" calls
516 cancelPeriodicTimer();
519 void setSwitchManager(ISwitchManager s) {
520 logger.debug("SwitchManager set");
521 this.switchManager = s;
524 void unsetSwitchManager(ISwitchManager s) {
525 if (this.switchManager == s) {
526 logger.debug("SwitchManager removed!");
527 this.switchManager = null;
532 public PacketResult receiveDataPacket(RawPacket inPkt) {
534 return PacketResult.IGNORED;
537 .trace("Received a frame of size: {}",
538 inPkt.getPacketData().length);
539 Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
540 if (formattedPak instanceof Ethernet) {
541 Object nextPak = formattedPak.getPayload();
542 if (nextPak instanceof IPv4) {
543 handlePuntedIPPacket((IPv4) nextPak, inPkt
544 .getIncomingNodeConnector());
545 logger.trace("Handled IP packet");
547 if (nextPak instanceof ARP) {
548 handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt
549 .getIncomingNodeConnector());
550 logger.trace("Handled ARP packet");
553 return PacketResult.IGNORED;
556 private void startPeriodicTimer() {
557 this.periodicTimer = new Timer("ArpHandler Periodic Timer");
558 this.periodicTimer.scheduleAtFixedRate(new TimerTask() {
561 Set<InetAddress> targetIPs = countDownTimers.keySet();
562 Set<InetAddress> expiredTargets = new HashSet<InetAddress>();
563 for (InetAddress t : targetIPs) {
564 short tick = countDownTimers.get(t);
567 expiredTargets.add(t);
569 countDownTimers.replace(t, tick);
572 for (InetAddress t : expiredTargets) {
573 countDownTimers.remove(t);
574 // remove the requestor(s) who have been waited for the ARP
575 // reply from this target for more than 1sec
576 arpRequestors.remove(t);
577 logger.debug("{} didn't respond to ARP request", t);
583 private void cancelPeriodicTimer() {
584 if (this.periodicTimer != null) {
585 this.periodicTimer.cancel();
589 private void generateAndSendReply(InetAddress sourceIP, byte[] sourceMAC) {
590 Set<HostNodeConnector> hosts = arpRequestors.remove(sourceIP);
591 if ((hosts == null) || hosts.isEmpty()) {
594 countDownTimers.remove(sourceIP);
595 for (HostNodeConnector host : hosts) {
597 "Sending ARP Reply with src {}/{}, target {}/{}",
598 new Object[] { sourceMAC, sourceIP,
599 host.getDataLayerAddressBytes(),
600 host.getNetworkAddress() });
601 sendARPReply(host.getnodeConnector(), sourceMAC, sourceIP,
602 host.getDataLayerAddressBytes(), host.getNetworkAddress());