HA - Cache synch for Topology Manager
[controller.git] / opendaylight / arphandler / src / main / java / org / opendaylight / controller / arphandler / internal / ArpHandler.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
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
8  */
9
10 /*
11  *
12  */
13 package org.opendaylight.controller.arphandler.internal;
14
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;
20 import java.util.Set;
21 import java.util.Timer;
22 import java.util.TimerTask;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentMap;
25
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;
50
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;
63
64     void setHostListener(IfHostListener s) {
65         if (this.hostListener != null) {
66             this.hostListener.add(s);
67         }
68     }
69
70     void unsetHostListener(IfHostListener s) {
71         if (this.hostListener != null) {
72             this.hostListener.remove(s);
73         }
74     }
75
76     void setDataPacketService(IDataPacketService s) {
77         this.dataPacketService = s;
78     }
79
80     void unsetDataPacketService(IDataPacketService s) {
81         if (this.dataPacketService == s) {
82             this.dataPacketService = null;
83         }
84     }
85
86     public IfIptoHost getHostTracker() {
87         return hostTracker;
88     }
89
90     public void setHostTracker(IfIptoHost hostTracker) {
91         logger.debug("Setting HostTracker");
92         this.hostTracker = hostTracker;
93     }
94
95     public void unsetHostTracker(IfIptoHost s) {
96         logger.debug("UNSetting HostTracker");
97         if (this.hostTracker == s) {
98             this.hostTracker = null;
99         }
100     }
101
102     public void setTopologyManager(ITopologyManager tm) {
103         this.topologyManager = tm;
104     }
105
106     public void unsetTopologyManager(ITopologyManager tm) {
107         if (this.topologyManager == tm) {
108             this.topologyManager = null;
109         }
110     }
111
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();
116         ARP arp = new ARP();
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);
124
125         Ethernet ethernet = new Ethernet();
126         ethernet.setSourceMACAddress(sMAC).setDestinationMACAddress(tMAC)
127                 .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
128
129         RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
130         destPkt.setOutgoingNodeConnector(p);
131
132         this.dataPacketService.transmitDataPacket(destPkt);
133     }
134
135     private boolean isBroadcastMAC(byte[] mac) {
136         if (BitBufferHelper.toNumber(mac) == 0xffffffffffffL) { //TODO: implement this in our Ethernet
137             return true;
138         }
139         return false;
140     }
141
142     private boolean isUnicastMAC(byte[] mac) {
143         if ((BitBufferHelper.toNumber(mac) & 0x010000000000L) == 0) {
144             return true;
145         }
146         return false;
147     }
148
149     protected void handleARPPacket(Ethernet eHeader, ARP pkt, NodeConnector p) {
150         if (pkt.getOpCode() == 0x1) {
151             logger.debug("Received ARP REQUEST Packet from NodeConnector: {}",
152                          p);
153         } else {
154             logger.debug("Received ARP REPLY Packet from NodeConnector: {}",
155                          p);
156         }
157         InetAddress targetIP = null;
158         try {
159             targetIP = InetAddress.getByAddress(pkt.getTargetProtocolAddress());
160         } catch (UnknownHostException e1) {
161             return;
162         }
163         InetAddress sourceIP = null;
164         try {
165             sourceIP = InetAddress.getByAddress(pkt.getSenderProtocolAddress());
166         } catch (UnknownHostException e1) {
167             return;
168         }
169         byte[] targetMAC = eHeader.getDestinationMACAddress();
170         byte[] sourceMAC = eHeader.getSourceMACAddress();
171
172         /*
173          * Sanity Check; drop ARP packets originated by the controller itself.
174          * This is to avoid continuous flooding
175          */
176         if (Arrays.equals(sourceMAC, getControllerMAC())) {
177             if (logger.isDebugEnabled()) {
178               logger.debug(
179                     "Receive the self originated packet (srcMAC {}) --> DROP",
180                     HexEncode.bytesToHexString(sourceMAC));
181             }
182             return;
183         }
184
185         Subnet subnet = null;
186         if (switchManager != null) {
187             subnet = switchManager.getSubnetByNetworkAddress(sourceIP);
188         }
189         if (subnet == null) {
190             logger.debug("can't find subnet matching {}, drop packet",sourceIP);
191             return;
192         }
193         logger.debug("Found {} matching {}", subnet, sourceIP);
194         /*
195          * Make sure that the host is a legitimate member of this subnet
196          */
197         if (!subnet.hasNodeConnector(p)) {
198             logger.debug("{} showing up on {} does not belong to {}",
199                     new Object[] { sourceIP, p, subnet });
200             return;
201         }
202
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)) {
208                 try {
209                     requestor = new HostNodeConnector(sourceMAC, sourceIP, p, subnet
210                             .getVlan());
211                 } catch (ConstructionException e) {
212                     return;
213                 }
214                 /*
215                  * Learn host from the received ARP REQ/REPLY, inform
216                  * Host Tracker
217                  */
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);
222                     }
223                 }
224             }
225         }
226         /*
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
229          * to these hosts
230          */
231         if (sourceIP.equals(targetIP)) {
232             generateAndSendReply(sourceIP, sourceMAC);
233             return;
234         }
235
236         /*
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
239          * these hosts
240          */
241         if (pkt.getOpCode() != ARP.REQUEST) {
242             generateAndSendReply(sourceIP, sourceMAC);
243             return;
244         }
245
246         /*
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
252          */
253         /*
254          * Send ARP reply if target IP is gateway IP
255          */
256         if ((targetIP.equals(subnet.getNetworkAddress()))
257                 && (isBroadcastMAC(targetMAC) || Arrays.equals(targetMAC,
258                         getControllerMAC()))) {
259             sendARPReply(p, getControllerMAC(), targetIP, pkt
260                     .getSenderHardwareAddress(), sourceIP);
261             return;
262         }
263
264         /*
265          * unknown host, initiate ARP request
266          */
267         HostNodeConnector host = hostTracker.hostQuery(targetIP);
268         if (host == null) {
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
273                         .get(targetIP);
274                 if ((requestorSet == null) || requestorSet.isEmpty()) {
275                     requestorSet = new HashSet<HostNodeConnector>();
276                     countDownTimers.put(targetIP, (short) 2); // set max timeout
277                                                               // to 2sec
278                 }
279                 requestorSet.add(requestor);
280                 arpRequestors.put(targetIP, requestorSet);
281             }
282             sendBcastARPRequest(targetIP, subnet);
283             return;
284         }
285         /*
286          * Known target host, send ARP REPLY
287          * make sure that targetMAC matches the host's MAC if it is not broadcastMAC
288          */
289         if (isBroadcastMAC(targetMAC)
290                 || Arrays.equals(host.getDataLayerAddressBytes(), targetMAC)) {
291             sendARPReply(p, host.getDataLayerAddressBytes(), host
292                     .getNetworkAddress(), pkt.getSenderHardwareAddress(),
293                     sourceIP);
294             return;
295         } else {
296             /*
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
300              * update its table
301              */
302             return;
303         }
304     }
305
306     /*
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
311      */
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));
319             }
320         } else {
321             nodeConnectors = subnet.getNodeConnectors();
322         }
323         for (NodeConnector p : nodeConnectors) {
324             if (topologyManager.isInternal(p)) {
325                 continue;
326             }
327             ARP arp = new ARP();
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,
338                                                      (byte) 0, (byte) 0,
339                                                      (byte) 0, (byte) 0 })
340                .setTargetProtocolAddress(targetIPB);
341
342             Ethernet ethernet = new Ethernet();
343             ethernet.setSourceMACAddress(getControllerMAC())
344                     .setDestinationMACAddress(new byte[] { (byte) -1,
345                                                           (byte) -1,
346                                                           (byte) -1,
347                                                           (byte) -1,
348                                                           (byte) -1,
349                                                           (byte) -1 })
350                     .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
351
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);
356
357             this.dataPacketService.transmitDataPacket(destPkt);
358         }
359     }
360
361     /*
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
366      */
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();
372         if (n == null) {
373             logger.error("cannot send UcastARP because cannot extract node "
374                     + "from HostNodeConnector: {}", host);
375             return;
376         }
377         NodeConnector outPort = host.getnodeConnector();
378         if (outPort == null) {
379             logger.error("cannot send UcastARP because cannot extract "
380                     + "outPort from HostNodeConnector: {}", host);
381             return;
382         }
383
384         byte[] senderIP = subnet.getNetworkAddress().getAddress();
385         byte[] targetIP = host.getNetworkAddress().getAddress();
386         byte[] targetMAC = host.getDataLayerAddressBytes();
387         ARP arp = new ARP();
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);
395
396         Ethernet ethernet = new Ethernet();
397         ethernet.setSourceMACAddress(getControllerMAC())
398                 .setDestinationMACAddress(targetMAC).setEtherType(
399                         EtherTypes.ARP.shortValue()).setPayload(arp);
400
401         RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
402         destPkt.setOutgoingNodeConnector(outPort);
403
404         this.dataPacketService.transmitDataPacket(destPkt);
405     }
406
407     public void find(InetAddress networkAddress) {
408         logger.debug("Received find IP {}", networkAddress);
409
410         Subnet subnet = null;
411         if (switchManager != null) {
412             subnet = switchManager.getSubnetByNetworkAddress(networkAddress);
413         }
414         if (subnet == null) {
415             logger.debug("can't find subnet matching IP {}", networkAddress);
416             return;
417         }
418         logger.debug("found subnet {}", subnet);
419
420         // send a broadcast ARP Request to this interface
421         sendBcastARPRequest(networkAddress, subnet);
422     }
423
424     /*
425      * Probe the host by sending a unicast ARP Request to the host
426      */
427     public void probe(HostNodeConnector host) {
428         logger.debug("Received probe host {}", host);
429
430         Subnet subnet = null;
431         if (switchManager != null) {
432             subnet = switchManager.getSubnetByNetworkAddress(host
433                     .getNetworkAddress());
434         }
435         if (subnet == null) {
436             logger.debug("can't find subnet matching {}", host
437                     .getNetworkAddress());
438             return;
439         }
440         sendUcastARPRequest(host, subnet);
441     }
442
443     /*
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
447      */
448     protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p) {
449         InetAddress dIP = null;
450         try {
451             dIP = InetAddress.getByAddress(NetUtils.intToByteArray4(pkt
452                     .getDestinationAddress()));
453         } catch (UnknownHostException e1) {
454             return;
455         }
456
457         Subnet subnet = null;
458         if (switchManager != null) {
459             subnet = switchManager.getSubnetByNetworkAddress(dIP);
460         }
461         if (subnet == null) {
462             logger.debug("can't find subnet matching {}, drop packet", dIP);
463             return;
464         }
465         logger.debug("Found {} matching {}", subnet, dIP);
466         /*
467          * unknown destination host, initiate ARP request
468          */
469         sendBcastARPRequest(dIP, subnet);
470         return;
471     }
472
473     public byte[] getControllerMAC() {
474         if (switchManager == null) {
475             return null;
476         }
477         return switchManager.getControllerMAC();
478     }
479
480     /**
481      * Function called by the dependency manager when all the required
482      * dependencies are satisfied
483      *
484      */
485     void init() {
486         arpRequestors = new ConcurrentHashMap<InetAddress, Set<HostNodeConnector>>();
487         countDownTimers = new ConcurrentHashMap<InetAddress, Short>();
488     }
489
490     /**
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.
494      *
495      */
496     void destroy() {
497     }
498
499     /**
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
503      *
504      */
505     void start() {
506         startPeriodicTimer();
507     }
508
509     /**
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
513      *
514      */
515     void stop() {
516         cancelPeriodicTimer();
517     }
518
519     void setSwitchManager(ISwitchManager s) {
520         logger.debug("SwitchManager set");
521         this.switchManager = s;
522     }
523
524     void unsetSwitchManager(ISwitchManager s) {
525         if (this.switchManager == s) {
526             logger.debug("SwitchManager removed!");
527             this.switchManager = null;
528         }
529     }
530
531     @Override
532     public PacketResult receiveDataPacket(RawPacket inPkt) {
533         if (inPkt == null) {
534             return PacketResult.IGNORED;
535         }
536         logger
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");
546             }
547             if (nextPak instanceof ARP) {
548                 handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt
549                         .getIncomingNodeConnector());
550                 logger.trace("Handled ARP packet");
551             }
552         }
553         return PacketResult.IGNORED;
554     }
555
556     private void startPeriodicTimer() {
557         this.periodicTimer = new Timer("ArpHandler Periodic Timer");
558         this.periodicTimer.scheduleAtFixedRate(new TimerTask() {
559             @Override
560             public void run() {
561                 Set<InetAddress> targetIPs = countDownTimers.keySet();
562                 Set<InetAddress> expiredTargets = new HashSet<InetAddress>();
563                 for (InetAddress t : targetIPs) {
564                     short tick = countDownTimers.get(t);
565                     tick--;
566                     if (tick <= 0) {
567                         expiredTargets.add(t);
568                     } else {
569                         countDownTimers.replace(t, tick);
570                     }
571                 }
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);
578                 }
579             }
580         }, 0, 1000);
581     }
582
583     private void cancelPeriodicTimer() {
584         if (this.periodicTimer != null) {
585             this.periodicTimer.cancel();
586         }
587     }
588
589     private void generateAndSendReply(InetAddress sourceIP, byte[] sourceMAC) {
590         Set<HostNodeConnector> hosts = arpRequestors.remove(sourceIP);
591         if ((hosts == null) || hosts.isEmpty()) {
592             return;
593         }
594         countDownTimers.remove(sourceIP);
595         for (HostNodeConnector host : hosts) {
596             logger.debug(
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());
603         }
604     }
605 }