Merge "FRM logging improvements"
[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:" + p);
134         } else {
135             logger.debug("Received ARP REPLY Packet from NodeConnector:" + p);
136         }
137         InetAddress targetIP = null;
138         try {
139             targetIP = InetAddress.getByAddress(pkt.getTargetProtocolAddress());
140         } catch (UnknownHostException e1) {
141             return;
142         }
143         InetAddress sourceIP = null;
144         try {
145             sourceIP = InetAddress.getByAddress(pkt.getSenderProtocolAddress());
146         } catch (UnknownHostException e1) {
147             return;
148         }
149         byte[] targetMAC = eHeader.getDestinationMACAddress();
150         byte[] sourceMAC = eHeader.getSourceMACAddress();
151
152         /*
153          * Sanity Check; drop ARP packets originated by the controller itself.
154          * This is to avoid continuous flooding
155          */
156         if (Arrays.equals(sourceMAC, getControllerMAC())) {
157             logger.debug(
158                     "Receive the self originated packet (srcMAC {}) --> DROP",
159                     HexEncode.bytesToHexString(sourceMAC));
160             return;
161         }
162
163         Subnet subnet = null;
164         if (switchManager != null) {
165             subnet = switchManager.getSubnetByNetworkAddress(sourceIP);
166         }
167         if (subnet == null) {
168             logger.debug("can't find subnet matching {}, drop packet", sourceIP
169                     .toString());
170             return;
171         }
172         logger.debug("Found {} matching {}", subnet.toString(), sourceIP
173                 .toString());
174         /*
175          * Make sure that the host is a legitimate member of this subnet
176          */
177         if (!subnet.hasNodeConnector(p)) {
178             logger.debug("{} showing up on {} does not belong to {}",
179                     new Object[] { sourceIP.toString(), p, subnet.toString() });
180             return;
181         }
182
183         if (isUnicastMAC(sourceMAC)) {
184             // TODO For not this is only OPENFLOW but we need to fix this
185             if (p.getType().equals(
186                     NodeConnector.NodeConnectorIDType.OPENFLOW)) {
187                 HostNodeConnector host = null;
188                 try {
189                     host = new HostNodeConnector(sourceMAC, sourceIP, p, subnet
190                             .getVlan());
191                 } catch (ConstructionException e) {
192                     return;
193                 }
194                 /*
195                  * Learn host from the received ARP REQ/REPLY, inform
196                  * Host Tracker
197                  */
198                 logger.debug("Inform Host tracker of new host {}", host);
199                 synchronized (this.hostListener) {
200                     for (IfHostListener listener : this.hostListener) {
201                         listener.hostListener(host);
202                     }
203                 }
204             }
205         }
206         /*
207          * No further action is needed if this is a gratuitous ARP
208          */
209         if (sourceIP.equals(targetIP)) {
210             return;
211         }
212
213         /*
214          * No further action is needed if this is a ARP Reply
215          */
216         if (pkt.getOpCode() != ARP.REQUEST) {
217             return;
218         }
219         /*
220          * ARP Request Handling:
221          * If targetIP is the IP of the subnet, reply with ARP REPLY
222          * If targetIP is a known host, PROXY ARP (by sending ARP REPLY) on behalf of known target hosts.
223          * For unknown target hosts, generate and send an ARP request to ALL switches/ports using
224          * the IP address defined in the subnet as source address
225          */
226         /*
227          * Send ARP reply if target IP is gateway IP
228          */
229         if ((targetIP.equals(subnet.getNetworkAddress()))
230                 && (isBroadcastMAC(targetMAC) || Arrays.equals(targetMAC,
231                         getControllerMAC()))) {
232             sendARPReply(p, getControllerMAC(), targetIP, pkt
233                     .getSenderHardwareAddress(), sourceIP);
234             return;
235         }
236
237         /*
238          * unknown host, initiate ARP request
239          */
240         HostNodeConnector host = hostTracker.hostQuery(targetIP);
241         if (host == null) {
242             sendBcastARPRequest(targetIP, subnet);
243             return;
244         }
245         /*
246          * Known target host, send ARP REPLY
247          * make sure that targetMAC matches the host's MAC if it is not broadcastMAC
248          */
249         if (isBroadcastMAC(targetMAC)
250                 || Arrays.equals(host.getDataLayerAddressBytes(), targetMAC)) {
251             sendARPReply(p, host.getDataLayerAddressBytes(), host
252                     .getNetworkAddress(), pkt.getSenderHardwareAddress(),
253                     sourceIP);
254             return;
255         } else {
256             /*
257              * target target MAC has been changed. For now, discard it.
258              * TODO: We may need to send unicast ARP REQUEST on behalf of the
259              * target back to the sender to trigger the sender to
260              * update its table
261              */
262             return;
263         }
264     }
265
266     /*
267      *  Send a broadcast ARP Request to the switch/ ports  using
268      *  the networkAddress of the subnet as sender IP
269      *  the controller's MAC as sender MAC
270      *  the targetIP as the target Network Address
271      */
272     protected void sendBcastARPRequest(InetAddress targetIP, Subnet subnet) {
273         Set<NodeConnector> nodeConnectors;
274         if (subnet.isFlatLayer2()) {
275             nodeConnectors = new HashSet<NodeConnector>();
276             for (Node n : this.switchManager.getNodes()) {
277                 nodeConnectors.addAll(this.switchManager
278                         .getUpNodeConnectors(n));
279             }
280         } else {
281             nodeConnectors = subnet.getNodeConnectors();
282         }
283         for (NodeConnector p : nodeConnectors) {
284             ARP arp = new ARP();
285             byte[] senderIP = subnet.getNetworkAddress().getAddress();
286             byte[] targetIPB = targetIP.getAddress();
287             arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
288                .setProtocolType(EtherTypes.IPv4.shortValue())
289                .setHardwareAddressLength((byte) 6)
290                .setProtocolAddressLength((byte) 4)
291                .setOpCode(ARP.REQUEST)
292                .setSenderHardwareAddress(getControllerMAC())
293                .setSenderProtocolAddress(senderIP)
294                .setTargetHardwareAddress(new byte[] { (byte) 0, (byte) 0,
295                                                      (byte) 0, (byte) 0,
296                                                      (byte) 0, (byte) 0 })
297                .setTargetProtocolAddress(targetIPB);
298
299             Ethernet ethernet = new Ethernet();
300             ethernet.setSourceMACAddress(getControllerMAC())
301                     .setDestinationMACAddress(new byte[] { (byte) -1,
302                                                           (byte) -1,
303                                                           (byte) -1,
304                                                           (byte) -1,
305                                                           (byte) -1,
306                                                           (byte) -1 })
307                     .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
308
309             // TODO For now send port-by-port, see how to optimize to
310             // send to a bunch of port on the same node in a shoot
311             RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
312             destPkt.setOutgoingNodeConnector(p);
313
314             this.dataPacketService.transmitDataPacket(destPkt);
315         }
316     }
317
318     /*
319      * Send a unicast ARP Request to the known host on a specific switch/port as
320      * defined in the host.
321      * The sender IP is the networkAddress of the subnet
322      * The sender MAC is the controller's MAC
323      */
324     protected void sendUcastARPRequest(HostNodeConnector host, Subnet subnet) {
325         //Long swID = host.getnodeconnectornodeId();
326         //Short portID = host.getnodeconnectorportId();
327         //Node n = NodeCreator.createOFNode(swID);
328         Node n = host.getnodeconnectorNode();
329         if (n == null) {
330             logger.error("cannot send UcastARP because cannot extract node "
331                     + "from HostNodeConnector:" + host);
332             return;
333         }
334         NodeConnector outPort = host.getnodeConnector();
335         if (outPort == null) {
336             logger.error("cannot send UcastARP because cannot extract "
337                     + "outPort from HostNodeConnector:" + host);
338             return;
339         }
340
341         byte[] senderIP = subnet.getNetworkAddress().getAddress();
342         byte[] targetIP = host.getNetworkAddress().getAddress();
343         byte[] targetMAC = host.getDataLayerAddressBytes();
344         ARP arp = new ARP();
345         arp.setHardwareType(ARP.HW_TYPE_ETHERNET).setProtocolType(
346                 EtherTypes.IPv4.shortValue())
347                 .setHardwareAddressLength((byte) 6).setProtocolAddressLength(
348                         (byte) 4).setOpCode(ARP.REQUEST)
349                 .setSenderHardwareAddress(getControllerMAC())
350                 .setSenderProtocolAddress(senderIP).setTargetHardwareAddress(
351                         targetMAC).setTargetProtocolAddress(targetIP);
352
353         Ethernet ethernet = new Ethernet();
354         ethernet.setSourceMACAddress(getControllerMAC())
355                 .setDestinationMACAddress(targetMAC).setEtherType(
356                         EtherTypes.ARP.shortValue()).setPayload(arp);
357
358         RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
359         destPkt.setOutgoingNodeConnector(outPort);
360
361         this.dataPacketService.transmitDataPacket(destPkt);
362     }
363
364     public void find(InetAddress networkAddress) {
365         logger.debug("Received find IP {}", networkAddress.toString());
366
367         Subnet subnet = null;
368         if (switchManager != null) {
369             subnet = switchManager.getSubnetByNetworkAddress(networkAddress);
370         }
371         if (subnet == null) {
372             logger.debug("can't find subnet matching IP {}", networkAddress
373                     .toString());
374             return;
375         }
376         logger.debug("found subnet {}", subnet.toString());
377
378         // send a broadcast ARP Request to this interface
379         sendBcastARPRequest(networkAddress, subnet);
380     }
381
382     /*
383      * Probe the host by sending a unicast ARP Request to the host
384      */
385     public void probe(HostNodeConnector host) {
386         logger.debug("Received probe host {}", host);
387
388         Subnet subnet = null;
389         if (switchManager != null) {
390             subnet = switchManager.getSubnetByNetworkAddress(host
391                     .getNetworkAddress());
392         }
393         if (subnet == null) {
394             logger.debug("can't find subnet matching {}", host
395                     .getNetworkAddress().toString());
396             return;
397         }
398         sendUcastARPRequest(host, subnet);
399     }
400
401     /*
402      * An IP packet is punted to the controller, this means that the
403      * destination host is not known to the controller.
404      * Need to discover it by sending a Broadcast ARP Request
405      */
406     protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p) {
407         InetAddress dIP = null;
408         try {
409             dIP = InetAddress.getByAddress(NetUtils.intToByteArray4(pkt
410                     .getDestinationAddress()));
411         } catch (UnknownHostException e1) {
412             return;
413         }
414
415         Subnet subnet = null;
416         if (switchManager != null) {
417             subnet = switchManager.getSubnetByNetworkAddress(dIP);
418         }
419         if (subnet == null) {
420             logger.debug("can't find subnet matching {}, drop packet", dIP
421                     .toString());
422             return;
423         }
424         logger.debug("Found {} matching {}", subnet.toString(), dIP.toString());
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 }