2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
12 package org.opendaylight.controller.arphandler.internal;
14 import java.net.InetAddress;
15 import java.net.UnknownHostException;
16 import java.util.Arrays;
17 import java.util.Collections;
18 import java.util.EnumSet;
19 import java.util.HashSet;
21 import java.util.Timer;
22 import java.util.TimerTask;
23 import java.util.concurrent.BlockingQueue;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26 import java.util.concurrent.CopyOnWriteArraySet;
27 import java.util.concurrent.LinkedBlockingQueue;
29 import org.opendaylight.controller.arphandler.ARPCacheEvent;
30 import org.opendaylight.controller.arphandler.ARPEvent;
31 import org.opendaylight.controller.arphandler.ARPReply;
32 import org.opendaylight.controller.arphandler.ARPRequest;
33 import org.opendaylight.controller.clustering.services.CacheConfigException;
34 import org.opendaylight.controller.clustering.services.CacheExistException;
35 import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
36 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
37 import org.opendaylight.controller.clustering.services.IClusterServices;
38 import org.opendaylight.controller.connectionmanager.IConnectionManager;
39 import org.opendaylight.controller.hosttracker.HostIdFactory;
40 import org.opendaylight.controller.hosttracker.IHostId;
41 import org.opendaylight.controller.hosttracker.IfHostListener;
42 import org.opendaylight.controller.hosttracker.IfIptoHost;
43 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
44 import org.opendaylight.controller.hosttracker.hostAware.IHostFinder;
45 import org.opendaylight.controller.sal.connection.ConnectionLocality;
46 import org.opendaylight.controller.sal.core.ConstructionException;
47 import org.opendaylight.controller.sal.core.Node;
48 import org.opendaylight.controller.sal.core.NodeConnector;
49 import org.opendaylight.controller.sal.packet.ARP;
50 import org.opendaylight.controller.sal.packet.Ethernet;
51 import org.opendaylight.controller.sal.packet.IDataPacketService;
52 import org.opendaylight.controller.sal.packet.IEEE8021Q;
53 import org.opendaylight.controller.sal.packet.IListenDataPacket;
54 import org.opendaylight.controller.sal.packet.IPv4;
55 import org.opendaylight.controller.sal.packet.Packet;
56 import org.opendaylight.controller.sal.packet.PacketResult;
57 import org.opendaylight.controller.sal.packet.RawPacket;
58 import org.opendaylight.controller.sal.utils.EtherTypes;
59 import org.opendaylight.controller.sal.utils.HexEncode;
60 import org.opendaylight.controller.sal.utils.NetUtils;
61 import org.opendaylight.controller.switchmanager.ISwitchManager;
62 import org.opendaylight.controller.switchmanager.Subnet;
63 import org.opendaylight.controller.topologymanager.ITopologyManager;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
68 * The ArpHandler offers services to react on ARP requests and replies
69 * sent by network hosts. Moreover it allows for creating ARP messages
70 * by the controller itself.
72 * The ARP Handler on ODL doesn't use the requester MAC address in
73 * order to avoid to have to build a spanning tree where to forward
74 * ARP Requests. The ARP requests are broadcast packets so in order to
75 * reach everywhere need to be flooded, when you flood in a network
76 * that is not a tree (all the networks has some level of redundancy)
77 * that would create forwarding loops without a spanning tree. Given
78 * the need is only to send out the ARP requests toward all the hosts
79 * we actually don't need to implement a flooding mechanism in software
80 * (which would be expensive) we just send out the ARP request toward
81 * all the ports that are suspected to be host ports on all the
82 * switches (from the controller). Now the condition for which a port
83 * is marked as host port could potentially be incorrect so when the
84 * controller sends out the ARP Request that could come back to the
85 * controller and could cause another request not needed. So changing
86 * the source MAC address of the request to be the one of the controller,
87 * controller can protect itself from honoring twice the same request.
88 * This enables an ARP handler resolution, without the need of spanning
89 * tree and limiting software flooding to the minimum required.
92 public class ArpHandler implements IHostFinder, IListenDataPacket, ICacheUpdateAware<ARPEvent, Boolean> {
93 private static final Logger log = LoggerFactory.getLogger(ArpHandler.class);
94 static final String ARP_EVENT_CACHE_NAME = "arphandler.arpRequestReplyEvent";
95 private IfIptoHost hostTracker;
96 private ISwitchManager switchManager;
97 private ITopologyManager topologyManager;
98 private IDataPacketService dataPacketService;
99 private IClusterContainerServices clusterContainerService;
100 private IConnectionManager connectionManager;
101 private Set<IfHostListener> hostListeners = new CopyOnWriteArraySet<IfHostListener>();
102 private ConcurrentMap<InetAddress, Set<HostNodeConnector>> arpRequestors;
103 private ConcurrentMap<InetAddress, Short> countDownTimers;
104 private Timer periodicTimer;
105 private BlockingQueue<ARPCacheEvent> ARPCacheEvents = new LinkedBlockingQueue<ARPCacheEvent>();
106 private Thread cacheEventHandler;
107 private boolean stopping = false;
110 * A cluster allocated cache. Used for synchronizing ARP request/reply
111 * events across all cluster controllers. To raise an event, we put() a
112 * specific event object (as key) and all nodes handle it in the
113 * entryUpdated callback.
115 * In case of ARPReply, we put true value to send replies to any requestors
116 * by calling generateAndSendReply
118 private ConcurrentMap<ARPEvent, Boolean> arpRequestReplyEvent;
120 void setConnectionManager(IConnectionManager cm) {
121 this.connectionManager = cm;
124 void unsetConnectionManager(IConnectionManager cm) {
125 if (this.connectionManager == cm) {
126 connectionManager = null;
130 void setClusterContainerService(IClusterContainerServices s) {
131 this.clusterContainerService = s;
134 void unsetClusterContainerService(IClusterContainerServices s) {
135 if (this.clusterContainerService == s) {
136 this.clusterContainerService = null;
140 void setHostListener(IfHostListener s) {
141 if (this.hostListeners != null) {
142 this.hostListeners.add(s);
146 void unsetHostListener(IfHostListener s) {
147 if (this.hostListeners != null) {
148 this.hostListeners.remove(s);
152 void setDataPacketService(IDataPacketService s) {
153 this.dataPacketService = s;
156 void unsetDataPacketService(IDataPacketService s) {
157 if (this.dataPacketService == s) {
158 this.dataPacketService = null;
162 public void setHostTracker(IfIptoHost hostTracker) {
163 log.debug("Setting HostTracker");
164 this.hostTracker = hostTracker;
167 public void unsetHostTracker(IfIptoHost s) {
168 log.debug("UNSetting HostTracker");
169 if (this.hostTracker == s) {
170 this.hostTracker = null;
174 public void setTopologyManager(ITopologyManager tm) {
175 this.topologyManager = tm;
178 public void unsetTopologyManager(ITopologyManager tm) {
179 if (this.topologyManager == tm) {
180 this.topologyManager = null;
184 protected void sendARPReply(NodeConnector p, byte[] sMAC, InetAddress sIP, byte[] tMAC, InetAddress tIP, short vlan) {
185 byte[] senderIP = sIP.getAddress();
186 byte[] targetIP = tIP.getAddress();
187 ARP arp = createARP(ARP.REPLY, sMAC, senderIP, tMAC, targetIP);
189 if(log.isTraceEnabled()) {
190 log.trace("Sending Arp Reply with srcMac {} - srcIp {} - dstMac {} - dstIp {} - outport {}",
191 HexEncode.bytesToHexString(sMAC),
192 sIP, HexEncode.bytesToHexString(tMAC), tIP, p);
195 Ethernet ethernet = createEthernet(sMAC, tMAC, arp, vlan);
197 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
198 destPkt.setOutgoingNodeConnector(p);
200 this.dataPacketService.transmitDataPacket(destPkt);
203 private void logArpPacket(ARP pkt, NodeConnector p, short vlan) {
205 log.trace("Received Arp {} with srcMac {} - srcIp {} - dstMac {} - dstIp {} - inport {} {}",
206 ((pkt.getOpCode() == ARP.REQUEST) ? "Request" : "Reply"),
207 HexEncode.bytesToHexString(pkt.getSenderHardwareAddress()),
208 InetAddress.getByAddress(pkt.getSenderProtocolAddress()),
209 HexEncode.bytesToHexString(pkt.getTargetHardwareAddress()),
210 InetAddress.getByAddress(pkt.getTargetProtocolAddress()), p, (vlan != 0 ? "on vlan " + vlan : ""));
212 } catch (UnknownHostException e) {
213 log.warn("Illegal Ip Address in the ARP packet", e);
217 protected void handleARPPacket(Ethernet eHeader, ARP pkt, NodeConnector p, short vlan) {
219 if(log.isTraceEnabled()) {
220 logArpPacket(pkt, p, vlan);
223 byte[] sourceMAC = eHeader.getSourceMACAddress();
224 byte[] targetMAC = eHeader.getDestinationMACAddress();
226 * Sanity Check; drop ARP packets originated by the controller itself.
227 * This is to avoid continuous flooding
229 if (Arrays.equals(sourceMAC, getControllerMAC())) {
230 if (log.isDebugEnabled()) {
231 log.debug("Receive a self originated ARP pkt (srcMAC {}) --> DROP",
232 HexEncode.bytesToHexString(sourceMAC));
237 InetAddress targetIP, sourceIP;
239 targetIP = InetAddress.getByAddress(pkt.getTargetProtocolAddress());
240 sourceIP = InetAddress.getByAddress(pkt.getSenderProtocolAddress());
241 } catch (UnknownHostException e1) {
242 log.debug("Invalid host in ARP packet: {}", e1.getMessage());
246 Subnet subnet = null;
247 if (switchManager != null) {
248 subnet = switchManager.getSubnetByNetworkAddress(sourceIP);
250 if (subnet == null) {
251 log.debug("ARPHandler: can't find subnet matching {}, drop packet", sourceIP);
255 // Make sure that the host is a legitimate member of this subnet
256 if (!subnet.hasNodeConnector(p)) {
257 log.debug("{} showing up on {} does not belong to {}", new Object[] { sourceIP, p, subnet });
261 HostNodeConnector requestor = null;
262 if (NetUtils.isUnicastMACAddr(sourceMAC) && p.getNode() != null) {
264 requestor = new HostNodeConnector(sourceMAC, sourceIP, p, vlan);
265 } catch (ConstructionException e) {
266 log.debug("Received ARP packet with invalid MAC: {}", HexEncode.bytesToHexString(sourceMAC));
270 * Learn host from the received ARP REQ/REPLY, inform Host Tracker
272 log.trace("Inform Host tracker of new host {}", requestor.getNetworkAddress());
273 for (IfHostListener listener : this.hostListeners) {
274 listener.hostListener(requestor);
279 * OpCode != request -> ARP Reply. If there are hosts (in arpRequestors)
280 * waiting for the ARP reply for this sourceIP, it's time to generate
281 * the reply and send it to these hosts.
283 * If sourceIP==targetIP, it is a Gratuitous ARP. If there are hosts (in
284 * arpRequestors) waiting for the ARP reply for this sourceIP, it's time
285 * to generate the reply and send it to these hosts
288 if (pkt.getOpCode() != ARP.REQUEST || sourceIP.equals(targetIP)) {
289 // Raise a reply event so that any waiting requestors will be sent a
291 // the true value indicates we should generate replies to requestors
292 // across the cluster
293 log.trace("Received ARP reply packet from {}, reply to all requestors.", sourceIP);
294 arpRequestReplyEvent.put(new ARPReply(sourceIP, sourceMAC, vlan), true);
299 * ARP Request Handling: If targetIP is the IP of the subnet, reply with
300 * ARP REPLY If targetIP is a known host, PROXY ARP (by sending ARP
301 * REPLY) on behalf of known target hosts. For unknown target hosts,
302 * generate and send an ARP request to ALL switches/ports using the IP
303 * address defined in the subnet as source address
306 * If target IP is gateway IP, Send ARP reply
308 if ((targetIP.equals(subnet.getNetworkAddress()))
309 && (NetUtils.isBroadcastMACAddr(targetMAC) || Arrays.equals(targetMAC, getControllerMAC()))) {
310 if (connectionManager.getLocalityStatus(p.getNode()) == ConnectionLocality.LOCAL) {
311 if (log.isTraceEnabled()) {
312 log.trace("Received local ARP req. for default gateway. Replying with controller MAC: {}",
313 HexEncode.bytesToHexString(getControllerMAC()));
315 sendARPReply(p, getControllerMAC(), targetIP, pkt.getSenderHardwareAddress(), sourceIP, vlan);
317 log.trace("Received non-local ARP req. for default gateway. Raising reply event");
318 arpRequestReplyEvent.put(
319 new ARPReply(p, targetIP, getControllerMAC(), sourceIP, pkt.getSenderHardwareAddress(), vlan), false);
324 // Hosttracker hosts db key implementation
325 IHostId id = HostIdFactory.create(targetIP, null);
326 HostNodeConnector host = hostTracker.hostQuery(id);
327 // unknown host, initiate ARP request
329 // add the requestor to the list so that we can replay the reply
330 // when the host responds
331 if (requestor != null) {
332 Set<HostNodeConnector> requestorSet = arpRequestors.get(targetIP);
333 if (requestorSet == null) {
334 requestorSet = Collections.newSetFromMap(new ConcurrentHashMap<HostNodeConnector, Boolean>());
335 arpRequestors.put(targetIP, requestorSet);
337 requestorSet.add(requestor);
338 countDownTimers.put(targetIP, (short) 2); // reset timeout to
341 // Raise a bcast request event, all controllers need to send one
342 log.trace("Sending a bcast ARP request for {}", targetIP);
343 arpRequestReplyEvent.put(new ARPRequest(targetIP, subnet), false);
346 * Target host known (across the cluster), send ARP REPLY make sure
347 * that targetMAC matches the host's MAC if it is not broadcastMAC
349 if (NetUtils.isBroadcastMACAddr(targetMAC) || Arrays.equals(host.getDataLayerAddressBytes(), targetMAC)) {
350 log.trace("Received ARP req. for known host {}, sending reply...", targetIP);
351 if (connectionManager.getLocalityStatus(p.getNode()) == ConnectionLocality.LOCAL) {
352 sendARPReply(p, host.getDataLayerAddressBytes(), host.getNetworkAddress(),
353 pkt.getSenderHardwareAddress(), sourceIP, vlan);
355 arpRequestReplyEvent.put(new ARPReply(p, host.getNetworkAddress(), host.getDataLayerAddressBytes(),
356 sourceIP, pkt.getSenderHardwareAddress(), vlan), false);
360 * Target MAC has been changed. For now, discard it. TODO: We
361 * may need to send unicast ARP REQUEST on behalf of the target
362 * back to the sender to trigger the sender to update its table
369 * Send a broadcast ARP Request to the switch/ ports using the
370 * networkAddress of the subnet as sender IP the controller's MAC as sender
371 * MAC the targetIP as the target Network Address
373 protected void sendBcastARPRequest(InetAddress targetIP, Subnet subnet) {
374 log.trace("sendBcatARPRequest targetIP:{} subnet:{}", targetIP, subnet);
375 Set<NodeConnector> nodeConnectors;
376 if (subnet.isFlatLayer2()) {
377 nodeConnectors = new HashSet<NodeConnector>();
378 for (Node n : this.switchManager.getNodes()) {
379 nodeConnectors.addAll(this.switchManager.getUpNodeConnectors(n));
382 nodeConnectors = subnet.getNodeConnectors();
384 byte[] targetHardwareAddress = new byte[] { (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0 };
386 // TODO: should use IBroadcastHandler instead
387 for (NodeConnector p : nodeConnectors) {
388 // filter out any non-local or internal ports
389 if (!(connectionManager.getLocalityStatus(p.getNode()) == ConnectionLocality.LOCAL)
390 || topologyManager.isInternal(p)) {
393 log.trace("Sending toward nodeConnector:{}", p);
394 byte[] senderIP = subnet.getNetworkAddress().getAddress();
395 byte[] targetIPByte = targetIP.getAddress();
396 ARP arp = createARP(ARP.REQUEST, getControllerMAC(), senderIP, targetHardwareAddress, targetIPByte);
398 if(log.isTraceEnabled()) {
399 log.trace("Sending Broadcast Arp Request with srcMac {} - srcIp {} - dstMac {} - dstIp {} - outport {}", HexEncode.bytesToHexString(getControllerMAC()),
400 subnet.getNetworkAddress(), HexEncode.bytesToHexString(targetHardwareAddress), targetIP, p);
403 byte[] destMACAddress = NetUtils.getBroadcastMACAddr();
404 Ethernet ethernet = createEthernet(getControllerMAC(), destMACAddress, arp, (short)0);
406 // TODO For now send port-by-port, see how to optimize to
407 // send to multiple ports at once
408 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
409 destPkt.setOutgoingNodeConnector(p);
411 this.dataPacketService.transmitDataPacket(destPkt);
416 * Send a unicast ARP Request to the known host on specific (switch/port,
417 * vlan) as defined in the host. The sender IP is the networkAddress of the
418 * subnet The sender MAC is the controller's MAC
420 protected void sendUcastARPRequest(HostNodeConnector host, Subnet subnet) {
421 log.trace("sendUcastARPRequest host:{} subnet:{}", host, subnet);
422 NodeConnector outPort = host.getnodeConnector();
423 if (outPort == null) {
424 log.error("Failed sending UcastARP because cannot extract output port from Host: {}", host);
428 byte[] senderIP = subnet.getNetworkAddress().getAddress();
429 byte[] targetIP = host.getNetworkAddress().getAddress();
430 byte[] targetMAC = host.getDataLayerAddressBytes();
431 ARP arp = createARP(ARP.REQUEST, getControllerMAC(), senderIP, targetMAC, targetIP);
433 if(log.isTraceEnabled()) {
434 log.trace("Sending Unicast Arp Request with srcMac {} - srcIp {} - dstMac {} - dstIp {} - outport {}",
435 HexEncode.bytesToHexString(getControllerMAC()),
436 subnet.getNetworkAddress(), HexEncode.bytesToHexString(targetMAC), host.getNetworkAddress(),
440 Ethernet ethernet = createEthernet(getControllerMAC(), targetMAC, arp, host.getVlan());
442 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
443 destPkt.setOutgoingNodeConnector(outPort);
445 this.dataPacketService.transmitDataPacket(destPkt);
449 public void find(InetAddress networkAddress) {
450 log.trace("Received find IP {}", networkAddress);
452 Subnet subnet = null;
453 if (switchManager != null) {
454 subnet = switchManager.getSubnetByNetworkAddress(networkAddress);
456 if (subnet == null) {
457 log.debug("Can't find subnet matching IP {}", networkAddress);
461 // send a broadcast ARP Request to this IP
462 arpRequestReplyEvent.put(new ARPRequest(networkAddress, subnet), false);
466 * Probe the host by sending a unicast ARP Request to the host
469 public void probe(HostNodeConnector host) {
470 log.trace("Received probe host {}", host);
472 Subnet subnet = null;
473 if (switchManager != null) {
474 subnet = switchManager.getSubnetByNetworkAddress(host.getNetworkAddress());
476 if (subnet == null) {
477 log.debug("can't find subnet matching {}", host.getNetworkAddress());
481 if (connectionManager.getLocalityStatus(host.getnodeconnectorNode()) == ConnectionLocality.LOCAL) {
482 log.trace("Send a ucast ARP req. to: {}", host);
483 sendUcastARPRequest(host, subnet);
485 log.trace("Raise a ucast ARP req. event to: {}", host);
486 arpRequestReplyEvent.put(new ARPRequest(host, subnet), false);
491 * An IP packet is punted to the controller, this means that the destination
492 * host is not known to the controller. Need to discover it by sending a
493 * Broadcast ARP Request
498 protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p, short vlan) {
500 InetAddress dIP = NetUtils.getInetAddress(pkt.getDestinationAddress());
505 // try to find a matching subnet
506 Subnet subnet = null;
507 if (switchManager != null) {
508 subnet = switchManager.getSubnetByNetworkAddress(dIP);
510 if (subnet == null) {
511 log.debug("Can't find subnet matching {}, drop packet", dIP);
514 // If packet is sent to the default gw (us), ignore it for now
515 if (subnet.getNetworkAddress().equals(dIP)) {
516 log.trace("Ignore IP packet destined to default gw");
520 // see if we know about the host
521 // Hosttracker hosts db key implementation
522 HostNodeConnector host = hostTracker.hostFind(dIP);
525 // if we don't know about the host, try to find it
526 log.trace("Punted IP pkt to {}, sending bcast ARP event...", dIP);
528 * unknown destination host, initiate bcast ARP request
530 arpRequestReplyEvent.put(new ARPRequest(dIP, subnet), false);
533 log.trace("Ignoring punted IP pkt to known host: {} (received on: {})", dIP, p);
537 public byte[] getControllerMAC() {
538 if (switchManager == null) {
541 return switchManager.getControllerMAC();
545 * Function called by the dependency manager when all the required
546 * dependencies are satisfied
550 arpRequestors = new ConcurrentHashMap<InetAddress, Set<HostNodeConnector>>();
551 countDownTimers = new ConcurrentHashMap<InetAddress, Short>();
552 cacheEventHandler = new Thread(new ARPCacheEventHandler(), "ARPCacheEventHandler Thread");
559 @SuppressWarnings({ "unchecked" })
560 private void retrieveCaches() {
561 ConcurrentMap<?, ?> map;
563 if (this.clusterContainerService == null) {
564 log.error("Cluster service unavailable, can't retieve ARPHandler caches!");
568 map = clusterContainerService.getCache(ARP_EVENT_CACHE_NAME);
570 this.arpRequestReplyEvent = (ConcurrentMap<ARPEvent, Boolean>) map;
572 log.error("Cache allocation failed for {}", ARP_EVENT_CACHE_NAME);
576 private void allocateCaches() {
577 if (clusterContainerService == null) {
578 nonClusterObjectCreate();
579 log.error("Clustering service unavailable. Allocated non-cluster caches for ARPHandler.");
584 clusterContainerService.createCache(ARP_EVENT_CACHE_NAME,
585 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
586 } catch (CacheConfigException e) {
587 log.error("ARPHandler cache configuration invalid!");
588 } catch (CacheExistException e) {
589 log.debug("ARPHandler cache exists, skipped allocation.");
594 private void nonClusterObjectCreate() {
595 arpRequestReplyEvent = new ConcurrentHashMap<ARPEvent, Boolean>();
599 * Function called by the dependency manager when at least one dependency
600 * become unsatisfied or when the component is shutting down because for
601 * example bundle is being stopped.
605 cacheEventHandler.interrupt();
609 * Function called by dependency manager after "init ()" is called and after
610 * the services provided by the class are registered in the service registry
615 startPeriodicTimer();
616 cacheEventHandler.start();
620 * Function called by the dependency manager before the services exported by
621 * the component are unregistered, this will be followed by a "destroy ()"
630 cancelPeriodicTimer();
633 void setSwitchManager(ISwitchManager s) {
634 log.debug("SwitchManager service set.");
635 this.switchManager = s;
638 void unsetSwitchManager(ISwitchManager s) {
639 if (this.switchManager == s) {
640 log.debug("SwitchManager service UNset.");
641 this.switchManager = null;
646 public PacketResult receiveDataPacket(RawPacket inPkt) {
648 return PacketResult.IGNORED;
650 log.trace("Received a frame of size: {}", inPkt.getPacketData().length);
651 Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
652 if (formattedPak instanceof Ethernet) {
653 Packet nextPak = formattedPak.getPayload();
655 if (nextPak instanceof IEEE8021Q) {
656 vlan = ((IEEE8021Q) nextPak).getVid();
657 log.trace("Moved after the dot1Q header");
658 nextPak = ((IEEE8021Q) nextPak).getPayload();
660 if (nextPak instanceof IPv4) {
661 log.trace("Handle IP packet: {}", formattedPak);
662 handlePuntedIPPacket((IPv4) nextPak, inPkt.getIncomingNodeConnector(), vlan);
663 } else if (nextPak instanceof ARP) {
664 log.trace("Handle ARP packet: {}", formattedPak);
665 handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt.getIncomingNodeConnector(), vlan);
668 return PacketResult.IGNORED;
671 private ARP createARP(short opCode, byte[] senderMacAddress, byte[] senderIP, byte[] targetMacAddress,
674 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
675 arp.setProtocolType(EtherTypes.IPv4.shortValue());
676 arp.setHardwareAddressLength((byte) 6);
677 arp.setProtocolAddressLength((byte) 4);
678 arp.setOpCode(opCode);
679 arp.setSenderHardwareAddress(senderMacAddress);
680 arp.setSenderProtocolAddress(senderIP);
681 arp.setTargetHardwareAddress(targetMacAddress);
682 arp.setTargetProtocolAddress(targetIP);
686 private Ethernet createEthernet(byte[] sourceMAC, byte[] targetMAC, ARP arp, short vlan) {
687 Ethernet ethernet = new Ethernet();
688 ethernet.setSourceMACAddress(sourceMAC);
689 ethernet.setDestinationMACAddress(targetMAC);
691 ethernet.setEtherType(EtherTypes.ARP.shortValue());
692 ethernet.setPayload(arp);
694 IEEE8021Q dot1q = new IEEE8021Q();
696 dot1q.setEtherType(EtherTypes.ARP.shortValue());
697 dot1q.setPayload(arp);
698 dot1q.setCfi((byte)0);
699 dot1q.setPcp((byte)0);
700 ethernet.setEtherType(EtherTypes.VLANTAGGED.shortValue());
701 ethernet.setPayload(dot1q);
706 private void startPeriodicTimer() {
707 this.periodicTimer = new Timer("ArpHandler Periodic Timer");
708 this.periodicTimer.scheduleAtFixedRate(new TimerTask() {
711 Set<InetAddress> targetIPs = countDownTimers.keySet();
712 Set<InetAddress> expiredTargets = new HashSet<InetAddress>();
713 for (InetAddress t : targetIPs) {
714 short tick = countDownTimers.get(t);
717 expiredTargets.add(t);
719 countDownTimers.replace(t, tick);
722 for (InetAddress tIP : expiredTargets) {
723 countDownTimers.remove(tIP);
724 // Remove the requestor(s) who have been waiting for the ARP
725 // reply from this target for more than 1sec
726 arpRequestors.remove(tIP);
727 log.debug("ARP reply was not received from {}", tIP);
730 // Clean up ARP event cache
732 if (clusterContainerService.amICoordinator() && !arpRequestReplyEvent.isEmpty()) {
733 arpRequestReplyEvent.clear();
735 } catch (Exception e) {
736 log.warn("ARPHandler: A cluster member failed to clear event cache.");
742 private void cancelPeriodicTimer() {
743 if (this.periodicTimer != null) {
744 this.periodicTimer.cancel();
748 private void generateAndSendReply(InetAddress sourceIP, byte[] sourceMAC, short vlan) {
749 if (log.isTraceEnabled()) {
750 log.trace("generateAndSendReply called with params sourceIP:{} sourceMAC:{}", sourceIP,
751 HexEncode.bytesToHexString(sourceMAC));
753 Set<HostNodeConnector> hosts = arpRequestors.remove(sourceIP);
754 if ((hosts == null) || hosts.isEmpty()) {
755 log.trace("Bailing out no requestors Hosts");
758 countDownTimers.remove(sourceIP);
759 for (HostNodeConnector host : hosts) {
760 if (log.isTraceEnabled()) {
762 "Sending ARP Reply with src {}/{}, target {}/{} {}",
763 new Object[] { HexEncode.bytesToHexString(sourceMAC), sourceIP,
764 HexEncode.bytesToHexString(host.getDataLayerAddressBytes()), host.getNetworkAddress(),
765 (vlan != 0 ? "on vlan " + vlan : "") });
767 if (connectionManager.getLocalityStatus(host.getnodeconnectorNode()) == ConnectionLocality.LOCAL) {
768 sendARPReply(host.getnodeConnector(), sourceMAC, sourceIP, host.getDataLayerAddressBytes(),
769 host.getNetworkAddress(), vlan);
772 * In the remote event a requestor moved to another controller
773 * it may turn out it now we need to send the ARP reply from a
774 * different controller, this cover the case
776 arpRequestReplyEvent.put(
777 new ARPReply(host.getnodeConnector(), sourceIP, sourceMAC, host.getNetworkAddress(), host
778 .getDataLayerAddressBytes(), vlan), false);
784 public void entryUpdated(ARPEvent key, Boolean new_value, String cacheName, boolean originLocal) {
785 log.trace("Got and entryUpdated for cacheName {} key {} isNew {}", cacheName, key, new_value);
786 enqueueARPCacheEvent(key, new_value);
790 public void entryCreated(ARPEvent key, String cacheName, boolean originLocal) {
795 public void entryDeleted(ARPEvent key, String cacheName, boolean originLocal) {
799 private void enqueueARPCacheEvent(ARPEvent event, boolean new_value) {
801 ARPCacheEvent cacheEvent = new ARPCacheEvent(event, new_value);
802 if (!ARPCacheEvents.contains(cacheEvent)) {
803 this.ARPCacheEvents.add(cacheEvent);
804 log.trace("Enqueued {}", event);
806 } catch (Exception e) {
807 log.debug("enqueueARPCacheEvent caught Interrupt Exception for event {}", event);
812 * this thread monitors the connectionEvent queue for new incoming events
815 private class ARPCacheEventHandler implements Runnable {
820 ARPCacheEvent ev = ARPCacheEvents.take();
821 ARPEvent event = ev.getEvent();
822 if (event instanceof ARPRequest) {
823 ARPRequest req = (ARPRequest) event;
824 // If broadcast request
825 if (req.getHost() == null) {
826 log.trace("Trigger and ARP Broadcast Request upon receipt of {}", req);
827 sendBcastARPRequest(req.getTargetIP(), req.getSubnet());
829 // If unicast and local, send reply
830 } else if (connectionManager.getLocalityStatus(req.getHost().getnodeconnectorNode()) == ConnectionLocality.LOCAL) {
831 log.trace("ARPCacheEventHandler - sendUcatARPRequest upon receipt of {}", req);
832 sendUcastARPRequest(req.getHost(), req.getSubnet());
834 } else if (event instanceof ARPReply) {
835 ARPReply rep = (ARPReply) event;
836 // New reply received by controller, notify all awaiting
837 // requestors across the cluster
838 if (ev.isNewReply()) {
839 log.trace("Trigger a generateAndSendReply in response to {}", rep);
840 generateAndSendReply(rep.getTargetIP(), rep.getTargetMac(), rep.getVlan());
841 // Otherwise, a specific reply. If local, send out.
842 } else if (connectionManager.getLocalityStatus(rep.getPort().getNode()) == ConnectionLocality.LOCAL) {
843 log.trace("ARPCacheEventHandler - sendUcatARPReply locally in response to {}", rep);
844 sendARPReply(rep.getPort(), rep.getSourceMac(), rep.getSourceIP(), rep.getTargetMac(),
845 rep.getTargetIP(), rep.getVlan());
848 } catch (InterruptedException e) {
849 ARPCacheEvents.clear();