Merge "Removed unused private variable containerAwareRegistration - please review...
[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
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 import org.opendaylight.controller.hosttracker.IfHostListener;
26 import org.opendaylight.controller.hosttracker.IfIptoHost;
27 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
28 import org.opendaylight.controller.hosttracker.hostAware.IHostFinder;
29 import org.opendaylight.controller.sal.core.ConstructionException;
30 import org.opendaylight.controller.sal.core.Node;
31 import org.opendaylight.controller.sal.core.NodeConnector;
32 import org.opendaylight.controller.sal.packet.ARP;
33 import org.opendaylight.controller.sal.packet.BitBufferHelper;
34 import org.opendaylight.controller.sal.packet.Ethernet;
35 import org.opendaylight.controller.sal.packet.IDataPacketService;
36 import org.opendaylight.controller.sal.packet.IListenDataPacket;
37 import org.opendaylight.controller.sal.packet.IPv4;
38 import org.opendaylight.controller.sal.packet.Packet;
39 import org.opendaylight.controller.sal.packet.PacketResult;
40 import org.opendaylight.controller.sal.packet.RawPacket;
41 import org.opendaylight.controller.sal.utils.EtherTypes;
42 import org.opendaylight.controller.sal.utils.HexEncode;
43 import org.opendaylight.controller.sal.utils.NetUtils;
44 import org.opendaylight.controller.switchmanager.ISwitchManager;
45 import org.opendaylight.controller.switchmanager.Subnet;
46
47 public class ArpHandler implements IHostFinder, IListenDataPacket {
48     private static final Logger logger = LoggerFactory
49             .getLogger(ArpHandler.class);
50     private IfIptoHost hostTracker = null;
51     private ISwitchManager switchManager = null;
52     private IDataPacketService dataPacketService = null;
53     private Set<IfHostListener> hostListener = Collections
54             .synchronizedSet(new HashSet<IfHostListener>());
55
56     void setHostListener(IfHostListener s) {
57         if (this.hostListener != null) {
58             this.hostListener.add(s);
59         }
60     }
61
62     void unsetHostListener(IfHostListener s) {
63         if (this.hostListener != null) {
64             this.hostListener.remove(s);
65         }
66     }
67
68     void setDataPacketService(IDataPacketService s) {
69         this.dataPacketService = s;
70     }
71
72     void unsetDataPacketService(IDataPacketService s) {
73         if (this.dataPacketService == s) {
74             this.dataPacketService = null;
75         }
76     }
77
78     public IfIptoHost getHostTracker() {
79         return hostTracker;
80     }
81
82     public void setHostTracker(IfIptoHost hostTracker) {
83         logger.debug("Setting HostTracker");
84         this.hostTracker = hostTracker;
85     }
86
87     public void unsetHostTracker(IfIptoHost s) {
88         logger.debug("UNSetting HostTracker");
89         if (this.hostTracker == s) {
90             this.hostTracker = null;
91         }
92     }
93
94     protected void sendARPReply(NodeConnector p, byte[] sMAC, InetAddress sIP,
95             byte[] tMAC, InetAddress tIP) {
96         byte[] senderIP = sIP.getAddress();
97         byte[] targetIP = tIP.getAddress();
98         ARP arp = new ARP();
99         arp.setHardwareType(ARP.HW_TYPE_ETHERNET).setProtocolType(
100                 EtherTypes.IPv4.shortValue())
101                 .setHardwareAddressLength((byte) 6).setProtocolAddressLength(
102                         (byte) 4).setOpCode(ARP.REPLY)
103                 .setSenderHardwareAddress(sMAC).setSenderProtocolAddress(
104                         senderIP).setTargetHardwareAddress(tMAC)
105                 .setTargetProtocolAddress(targetIP);
106
107         Ethernet ethernet = new Ethernet();
108         ethernet.setSourceMACAddress(sMAC).setDestinationMACAddress(tMAC)
109                 .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
110
111         RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
112         destPkt.setOutgoingNodeConnector(p);
113
114         this.dataPacketService.transmitDataPacket(destPkt);
115     }
116
117     private boolean isBroadcastMAC(byte[] mac) {
118         if (BitBufferHelper.toNumber(mac) == 0xffffffffffffL) { //TODO: implement this in our Ethernet
119             return true;
120         }
121         return false;
122     }
123
124     private boolean isUnicastMAC(byte[] mac) {
125         if ((BitBufferHelper.toNumber(mac) & 0x010000000000L) == 0) {
126             return true;
127         }
128         return false;
129     }
130
131     protected void handleARPPacket(Ethernet eHeader, ARP pkt, NodeConnector p) {
132         if (pkt.getOpCode() == 0x1) {
133             logger.debug("Received ARP REQUEST Packet from NodeConnector: {}",
134                          p);
135         } else {
136             logger.debug("Received ARP REPLY Packet from NodeConnector: {}",
137                          p);
138         }
139         InetAddress targetIP = null;
140         try {
141             targetIP = InetAddress.getByAddress(pkt.getTargetProtocolAddress());
142         } catch (UnknownHostException e1) {
143             return;
144         }
145         InetAddress sourceIP = null;
146         try {
147             sourceIP = InetAddress.getByAddress(pkt.getSenderProtocolAddress());
148         } catch (UnknownHostException e1) {
149             return;
150         }
151         byte[] targetMAC = eHeader.getDestinationMACAddress();
152         byte[] sourceMAC = eHeader.getSourceMACAddress();
153
154         /*
155          * Sanity Check; drop ARP packets originated by the controller itself.
156          * This is to avoid continuous flooding
157          */
158         if (Arrays.equals(sourceMAC, getControllerMAC())) {
159             if (logger.isDebugEnabled()) {
160               logger.debug(
161                     "Receive the self originated packet (srcMAC {}) --> DROP",
162                     HexEncode.bytesToHexString(sourceMAC));
163             }
164             return;
165         }
166
167         Subnet subnet = null;
168         if (switchManager != null) {
169             subnet = switchManager.getSubnetByNetworkAddress(sourceIP);
170         }
171         if (subnet == null) {
172             logger.debug("can't find subnet matching {}, drop packet",sourceIP);
173             return;
174         }
175         logger.debug("Found {} matching {}", subnet, sourceIP);
176         /*
177          * Make sure that the host is a legitimate member of this subnet
178          */
179         if (!subnet.hasNodeConnector(p)) {
180             logger.debug("{} showing up on {} does not belong to {}",
181                     new Object[] { sourceIP, p, subnet });
182             return;
183         }
184
185         if (isUnicastMAC(sourceMAC)) {
186             // TODO For not this is only OPENFLOW but we need to fix this
187             if (p.getType().equals(
188                     NodeConnector.NodeConnectorIDType.OPENFLOW)) {
189                 HostNodeConnector host = null;
190                 try {
191                     host = new HostNodeConnector(sourceMAC, sourceIP, p, subnet
192                             .getVlan());
193                 } catch (ConstructionException e) {
194                     return;
195                 }
196                 /*
197                  * Learn host from the received ARP REQ/REPLY, inform
198                  * Host Tracker
199                  */
200                 logger.debug("Inform Host tracker of new host {}", host);
201                 synchronized (this.hostListener) {
202                     for (IfHostListener listener : this.hostListener) {
203                         listener.hostListener(host);
204                     }
205                 }
206             }
207         }
208         /*
209          * No further action is needed if this is a gratuitous ARP
210          */
211         if (sourceIP.equals(targetIP)) {
212             return;
213         }
214
215         /*
216          * No further action is needed if this is a ARP Reply
217          */
218         if (pkt.getOpCode() != ARP.REQUEST) {
219             return;
220         }
221         /*
222          * ARP Request Handling:
223          * If targetIP is the IP of the subnet, reply with ARP REPLY
224          * If targetIP is a known host, PROXY ARP (by sending ARP REPLY) on behalf of known target hosts.
225          * For unknown target hosts, generate and send an ARP request to ALL switches/ports using
226          * the IP address defined in the subnet as source address
227          */
228         /*
229          * Send ARP reply if target IP is gateway IP
230          */
231         if ((targetIP.equals(subnet.getNetworkAddress()))
232                 && (isBroadcastMAC(targetMAC) || Arrays.equals(targetMAC,
233                         getControllerMAC()))) {
234             sendARPReply(p, getControllerMAC(), targetIP, pkt
235                     .getSenderHardwareAddress(), sourceIP);
236             return;
237         }
238
239         /*
240          * unknown host, initiate ARP request
241          */
242         HostNodeConnector host = hostTracker.hostQuery(targetIP);
243         if (host == null) {
244             sendBcastARPRequest(targetIP, subnet);
245             return;
246         }
247         /*
248          * Known target host, send ARP REPLY
249          * make sure that targetMAC matches the host's MAC if it is not broadcastMAC
250          */
251         if (isBroadcastMAC(targetMAC)
252                 || Arrays.equals(host.getDataLayerAddressBytes(), targetMAC)) {
253             sendARPReply(p, host.getDataLayerAddressBytes(), host
254                     .getNetworkAddress(), pkt.getSenderHardwareAddress(),
255                     sourceIP);
256             return;
257         } else {
258             /*
259              * target target MAC has been changed. For now, discard it.
260              * TODO: We may need to send unicast ARP REQUEST on behalf of the
261              * target back to the sender to trigger the sender to
262              * update its table
263              */
264             return;
265         }
266     }
267
268     /*
269      *  Send a broadcast ARP Request to the switch/ ports  using
270      *  the networkAddress of the subnet as sender IP
271      *  the controller's MAC as sender MAC
272      *  the targetIP as the target Network Address
273      */
274     protected void sendBcastARPRequest(InetAddress targetIP, Subnet subnet) {
275         Set<NodeConnector> nodeConnectors;
276         if (subnet.isFlatLayer2()) {
277             nodeConnectors = new HashSet<NodeConnector>();
278             for (Node n : this.switchManager.getNodes()) {
279                 nodeConnectors.addAll(this.switchManager
280                         .getUpNodeConnectors(n));
281             }
282         } else {
283             nodeConnectors = subnet.getNodeConnectors();
284         }
285         for (NodeConnector p : nodeConnectors) {
286             ARP arp = new ARP();
287             byte[] senderIP = subnet.getNetworkAddress().getAddress();
288             byte[] targetIPB = targetIP.getAddress();
289             arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
290                .setProtocolType(EtherTypes.IPv4.shortValue())
291                .setHardwareAddressLength((byte) 6)
292                .setProtocolAddressLength((byte) 4)
293                .setOpCode(ARP.REQUEST)
294                .setSenderHardwareAddress(getControllerMAC())
295                .setSenderProtocolAddress(senderIP)
296                .setTargetHardwareAddress(new byte[] { (byte) 0, (byte) 0,
297                                                      (byte) 0, (byte) 0,
298                                                      (byte) 0, (byte) 0 })
299                .setTargetProtocolAddress(targetIPB);
300
301             Ethernet ethernet = new Ethernet();
302             ethernet.setSourceMACAddress(getControllerMAC())
303                     .setDestinationMACAddress(new byte[] { (byte) -1,
304                                                           (byte) -1,
305                                                           (byte) -1,
306                                                           (byte) -1,
307                                                           (byte) -1,
308                                                           (byte) -1 })
309                     .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
310
311             // TODO For now send port-by-port, see how to optimize to
312             // send to a bunch of port on the same node in a shoot
313             RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
314             destPkt.setOutgoingNodeConnector(p);
315
316             this.dataPacketService.transmitDataPacket(destPkt);
317         }
318     }
319
320     /*
321      * Send a unicast ARP Request to the known host on a specific switch/port as
322      * defined in the host.
323      * The sender IP is the networkAddress of the subnet
324      * The sender MAC is the controller's MAC
325      */
326     protected void sendUcastARPRequest(HostNodeConnector host, Subnet subnet) {
327         //Long swID = host.getnodeconnectornodeId();
328         //Short portID = host.getnodeconnectorportId();
329         //Node n = NodeCreator.createOFNode(swID);
330         Node n = host.getnodeconnectorNode();
331         if (n == null) {
332             logger.error("cannot send UcastARP because cannot extract node "
333                     + "from HostNodeConnector: {}", host);
334             return;
335         }
336         NodeConnector outPort = host.getnodeConnector();
337         if (outPort == null) {
338             logger.error("cannot send UcastARP because cannot extract "
339                     + "outPort from HostNodeConnector: {}", host);
340             return;
341         }
342
343         byte[] senderIP = subnet.getNetworkAddress().getAddress();
344         byte[] targetIP = host.getNetworkAddress().getAddress();
345         byte[] targetMAC = host.getDataLayerAddressBytes();
346         ARP arp = new ARP();
347         arp.setHardwareType(ARP.HW_TYPE_ETHERNET).setProtocolType(
348                 EtherTypes.IPv4.shortValue())
349                 .setHardwareAddressLength((byte) 6).setProtocolAddressLength(
350                         (byte) 4).setOpCode(ARP.REQUEST)
351                 .setSenderHardwareAddress(getControllerMAC())
352                 .setSenderProtocolAddress(senderIP).setTargetHardwareAddress(
353                         targetMAC).setTargetProtocolAddress(targetIP);
354
355         Ethernet ethernet = new Ethernet();
356         ethernet.setSourceMACAddress(getControllerMAC())
357                 .setDestinationMACAddress(targetMAC).setEtherType(
358                         EtherTypes.ARP.shortValue()).setPayload(arp);
359
360         RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
361         destPkt.setOutgoingNodeConnector(outPort);
362
363         this.dataPacketService.transmitDataPacket(destPkt);
364     }
365
366     public void find(InetAddress networkAddress) {
367         logger.debug("Received find IP {}", networkAddress);
368
369         Subnet subnet = null;
370         if (switchManager != null) {
371             subnet = switchManager.getSubnetByNetworkAddress(networkAddress);
372         }
373         if (subnet == null) {
374             logger.debug("can't find subnet matching IP {}", networkAddress);
375             return;
376         }
377         logger.debug("found subnet {}", subnet);
378
379         // send a broadcast ARP Request to this interface
380         sendBcastARPRequest(networkAddress, subnet);
381     }
382
383     /*
384      * Probe the host by sending a unicast ARP Request to the host
385      */
386     public void probe(HostNodeConnector host) {
387         logger.debug("Received probe host {}", host);
388
389         Subnet subnet = null;
390         if (switchManager != null) {
391             subnet = switchManager.getSubnetByNetworkAddress(host
392                     .getNetworkAddress());
393         }
394         if (subnet == null) {
395             logger.debug("can't find subnet matching {}", host
396                     .getNetworkAddress());
397             return;
398         }
399         sendUcastARPRequest(host, subnet);
400     }
401
402     /*
403      * An IP packet is punted to the controller, this means that the
404      * destination host is not known to the controller.
405      * Need to discover it by sending a Broadcast ARP Request
406      */
407     protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p) {
408         InetAddress dIP = null;
409         try {
410             dIP = InetAddress.getByAddress(NetUtils.intToByteArray4(pkt
411                     .getDestinationAddress()));
412         } catch (UnknownHostException e1) {
413             return;
414         }
415
416         Subnet subnet = null;
417         if (switchManager != null) {
418             subnet = switchManager.getSubnetByNetworkAddress(dIP);
419         }
420         if (subnet == null) {
421             logger.debug("can't find subnet matching {}, drop packet", dIP);
422             return;
423         }
424         logger.debug("Found {} matching {}", subnet, dIP);
425         /*
426          * unknown destination host, initiate ARP request
427          */
428         sendBcastARPRequest(dIP, subnet);
429         return;
430     }
431
432     public byte[] getControllerMAC() {
433         if (switchManager == null) {
434             return null;
435         }
436         return switchManager.getControllerMAC();
437     }
438
439     /**
440      * Function called by the dependency manager when all the required
441      * dependencies are satisfied
442      *
443      */
444     void init() {
445     }
446
447     /**
448      * Function called by the dependency manager when at least one
449      * dependency become unsatisfied or when the component is shutting
450      * down because for example bundle is being stopped.
451      *
452      */
453     void destroy() {
454     }
455
456     /**
457      * Function called by dependency manager after "init ()" is called
458      * and after the services provided by the class are registered in
459      * the service registry
460      *
461      */
462     void start() {
463     }
464
465     /**
466      * Function called by the dependency manager before the services
467      * exported by the component are unregistered, this will be
468      * followed by a "destroy ()" calls
469      *
470      */
471     void stop() {
472     }
473
474     void setSwitchManager(ISwitchManager s) {
475         logger.debug("SwitchManager set");
476         this.switchManager = s;
477     }
478
479     void unsetSwitchManager(ISwitchManager s) {
480         if (this.switchManager == s) {
481             logger.debug("SwitchManager removed!");
482             this.switchManager = null;
483         }
484     }
485
486     @Override
487     public PacketResult receiveDataPacket(RawPacket inPkt) {
488         if (inPkt == null) {
489             return PacketResult.IGNORED;
490         }
491         logger
492                 .trace("Received a frame of size: {}",
493                         inPkt.getPacketData().length);
494         Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
495         if (formattedPak instanceof Ethernet) {
496             Object nextPak = formattedPak.getPayload();
497             if (nextPak instanceof IPv4) {
498                 handlePuntedIPPacket((IPv4) nextPak, inPkt
499                         .getIncomingNodeConnector());
500                 logger.trace("Handled IP packet");
501             }
502             if (nextPak instanceof ARP) {
503                 handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt
504                         .getIncomingNodeConnector());
505                 logger.trace("Handled ARP packet");
506             }
507         }
508         return PacketResult.IGNORED;
509     }
510 }