OpenDaylight Controller functional modules.
[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 = s;
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).setProtocolType(
288                     EtherTypes.IPv4.shortValue()).setHardwareAddressLength(
289                     (byte) 6).setProtocolAddressLength((byte) 4).setOpCode(
290                     ARP.REQUEST).setSenderHardwareAddress(getControllerMAC())
291                     .setSenderProtocolAddress(senderIP)
292                     .setTargetHardwareAddress(
293                             new byte[] { (byte) 0, (byte) 0, (byte) 0,
294                                     (byte) 0, (byte) 0, (byte) 0 })
295                     .setTargetProtocolAddress(targetIPB);
296
297             Ethernet ethernet = new Ethernet();
298             ethernet.setSourceMACAddress(getControllerMAC())
299                     .setDestinationMACAddress(
300                             new byte[] { (byte) -1, (byte) -1, (byte) -1,
301                                     (byte) -1, (byte) -1, (byte) -1 })
302                     .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
303
304             // TODO For now send port-by-port, see how to optimize to
305             // send to a bunch of port on the same node in a shoot
306             RawPacket destPkt = this.dataPacketService
307                     .encodeDataPacket(ethernet);
308             destPkt.setOutgoingNodeConnector(p);
309
310             this.dataPacketService.transmitDataPacket(destPkt);
311         }
312     }
313
314     /*
315      * Send a unicast ARP Request to the known host on a specific switch/port as
316      * defined in the host.
317      * The sender IP is the networkAddress of the subnet
318      * The sender MAC is the controller's MAC
319      */
320     protected void sendUcastARPRequest(HostNodeConnector host, Subnet subnet) {
321         //Long swID = host.getnodeconnectornodeId();
322         //Short portID = host.getnodeconnectorportId();
323         //Node n = NodeCreator.createOFNode(swID);
324         Node n = host.getnodeconnectorNode();
325         if (n == null) {
326             logger.error("cannot send UcastARP because cannot extract node "
327                     + "from HostNodeConnector:" + host);
328             return;
329         }
330         NodeConnector outPort = host.getnodeConnector();
331         if (outPort == null) {
332             logger.error("cannot send UcastARP because cannot extract "
333                     + "outPort from HostNodeConnector:" + host);
334             return;
335         }
336
337         byte[] senderIP = subnet.getNetworkAddress().getAddress();
338         byte[] targetIP = host.getNetworkAddress().getAddress();
339         byte[] targetMAC = host.getDataLayerAddressBytes();
340         ARP arp = new ARP();
341         arp.setHardwareType(ARP.HW_TYPE_ETHERNET).setProtocolType(
342                 EtherTypes.IPv4.shortValue())
343                 .setHardwareAddressLength((byte) 6).setProtocolAddressLength(
344                         (byte) 4).setOpCode(ARP.REQUEST)
345                 .setSenderHardwareAddress(getControllerMAC())
346                 .setSenderProtocolAddress(senderIP).setTargetHardwareAddress(
347                         targetMAC).setTargetProtocolAddress(targetIP);
348
349         Ethernet ethernet = new Ethernet();
350         ethernet.setSourceMACAddress(getControllerMAC())
351                 .setDestinationMACAddress(targetMAC).setEtherType(
352                         EtherTypes.ARP.shortValue()).setPayload(arp);
353
354         RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
355         destPkt.setOutgoingNodeConnector(outPort);
356
357         this.dataPacketService.transmitDataPacket(destPkt);
358     }
359
360     public void find(InetAddress networkAddress) {
361         logger.debug("Received find IP {}", networkAddress.toString());
362
363         Subnet subnet = null;
364         if (switchManager != null) {
365             subnet = switchManager.getSubnetByNetworkAddress(networkAddress);
366         }
367         if (subnet == null) {
368             logger.debug("can't find subnet matching IP {}", networkAddress
369                     .toString());
370             return;
371         }
372         logger.debug("found subnet {}", subnet.toString());
373
374         // send a broadcast ARP Request to this interface
375         sendBcastARPRequest(networkAddress, subnet);
376     }
377
378     /*
379      * Probe the host by sending a unicast ARP Request to the host
380      */
381     public void probe(HostNodeConnector host) {
382         logger.debug("Received probe host {}", host);
383
384         Subnet subnet = null;
385         if (switchManager != null) {
386             subnet = switchManager.getSubnetByNetworkAddress(host
387                     .getNetworkAddress());
388         }
389         if (subnet == null) {
390             logger.debug("can't find subnet matching {}", host
391                     .getNetworkAddress().toString());
392             return;
393         }
394         sendUcastARPRequest(host, subnet);
395     }
396
397     /*
398      * An IP packet is punted to the controller, this means that the
399      * destination host is not known to the controller.
400      * Need to discover it by sending a Broadcast ARP Request
401      */
402     protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p) {
403         InetAddress dIP = null;
404         try {
405             dIP = InetAddress.getByAddress(NetUtils.intToByteArray4(pkt
406                     .getDestinationAddress()));
407         } catch (UnknownHostException e1) {
408             return;
409         }
410
411         Subnet subnet = null;
412         if (switchManager != null) {
413             subnet = switchManager.getSubnetByNetworkAddress(dIP);
414         }
415         if (subnet == null) {
416             logger.debug("can't find subnet matching {}, drop packet", dIP
417                     .toString());
418             return;
419         }
420         logger.debug("Found {} matching {}", subnet.toString(), dIP.toString());
421         /*
422          * unknown destination host, initiate ARP request
423          */
424         sendBcastARPRequest(dIP, subnet);
425         return;
426     }
427
428     public byte[] getControllerMAC() {
429         if (switchManager == null) {
430             return null;
431         }
432         return switchManager.getControllerMAC();
433     }
434
435     /**
436      * Function called by the dependency manager when all the required
437      * dependencies are satisfied
438      *
439      */
440     void init() {
441     }
442
443     /**
444      * Function called by the dependency manager when at least one
445      * dependency become unsatisfied or when the component is shutting
446      * down because for example bundle is being stopped.
447      *
448      */
449     void destroy() {
450     }
451
452     /**
453      * Function called by dependency manager after "init ()" is called
454      * and after the services provided by the class are registered in
455      * the service registry
456      *
457      */
458     void start() {
459     }
460
461     /**
462      * Function called by the dependency manager before the services
463      * exported by the component are unregistered, this will be
464      * followed by a "destroy ()" calls
465      *
466      */
467     void stop() {
468     }
469
470     void setSwitchManager(ISwitchManager s) {
471         logger.debug("SwitchManager set");
472         this.switchManager = s;
473     }
474
475     void unsetSwitchManager(ISwitchManager s) {
476         if (this.switchManager == s) {
477             logger.debug("SwitchManager removed!");
478             this.switchManager = null;
479         }
480     }
481
482     @Override
483     public PacketResult receiveDataPacket(RawPacket inPkt) {
484         if (inPkt == null) {
485             return PacketResult.IGNORED;
486         }
487         logger
488                 .trace("Received a frame of size:"
489                         + inPkt.getPacketData().length);
490         Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
491         if (formattedPak instanceof Ethernet) {
492             Object nextPak = formattedPak.getPayload();
493             if (nextPak instanceof IPv4) {
494                 handlePuntedIPPacket((IPv4) nextPak, inPkt
495                         .getIncomingNodeConnector());
496                 logger.trace("Handled IP packet");
497             }
498             if (nextPak instanceof ARP) {
499                 handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt
500                         .getIncomingNodeConnector());
501                 logger.trace("Handled ARP packet");
502             }
503         }
504         return PacketResult.IGNORED;
505     }
506 }