3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
13 package org.opendaylight.controller.arphandler.internal;
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;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
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;
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>());
56 void setHostListener(IfHostListener s) {
57 if (this.hostListener != null) {
58 this.hostListener.add(s);
62 void unsetHostListener(IfHostListener s) {
63 if (this.hostListener != null) {
64 this.hostListener.remove(s);
68 void setDataPacketService(IDataPacketService s) {
69 this.dataPacketService = s;
72 void unsetDataPacketService(IDataPacketService s) {
73 if (this.dataPacketService == s) {
74 this.dataPacketService = null;
78 public IfIptoHost getHostTracker() {
82 public void setHostTracker(IfIptoHost hostTracker) {
83 logger.debug("Setting HostTracker");
84 this.hostTracker = hostTracker;
87 public void unsetHostTracker(IfIptoHost s) {
88 logger.debug("UNSetting HostTracker");
89 if (this.hostTracker == s) {
90 this.hostTracker = null;
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();
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);
107 Ethernet ethernet = new Ethernet();
108 ethernet.setSourceMACAddress(sMAC).setDestinationMACAddress(tMAC)
109 .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
111 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
112 destPkt.setOutgoingNodeConnector(p);
114 this.dataPacketService.transmitDataPacket(destPkt);
117 private boolean isBroadcastMAC(byte[] mac) {
118 if (BitBufferHelper.toNumber(mac) == 0xffffffffffffL) { //TODO: implement this in our Ethernet
124 private boolean isUnicastMAC(byte[] mac) {
125 if ((BitBufferHelper.toNumber(mac) & 0x010000000000L) == 0) {
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);
135 logger.debug("Received ARP REPLY Packet from NodeConnector:" + p);
137 InetAddress targetIP = null;
139 targetIP = InetAddress.getByAddress(pkt.getTargetProtocolAddress());
140 } catch (UnknownHostException e1) {
143 InetAddress sourceIP = null;
145 sourceIP = InetAddress.getByAddress(pkt.getSenderProtocolAddress());
146 } catch (UnknownHostException e1) {
149 byte[] targetMAC = eHeader.getDestinationMACAddress();
150 byte[] sourceMAC = eHeader.getSourceMACAddress();
153 * Sanity Check; drop ARP packets originated by the controller itself.
154 * This is to avoid continuous flooding
156 if (Arrays.equals(sourceMAC, getControllerMAC())) {
158 "Receive the self originated packet (srcMAC {}) --> DROP",
159 HexEncode.bytesToHexString(sourceMAC));
163 Subnet subnet = null;
164 if (switchManager != null) {
165 subnet = switchManager.getSubnetByNetworkAddress(sourceIP);
167 if (subnet == null) {
168 logger.debug("can't find subnet matching {}, drop packet", sourceIP
172 logger.debug("Found {} matching {}", subnet.toString(), sourceIP
175 * Make sure that the host is a legitimate member of this subnet
177 if (!subnet.hasNodeConnector(p)) {
178 logger.debug("{} showing up on {} does not belong to {}",
179 new Object[] { sourceIP.toString(), p, subnet.toString() });
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;
189 host = new HostNodeConnector(sourceMAC, sourceIP, p, subnet
191 } catch (ConstructionException e) {
195 * Learn host from the received ARP REQ/REPLY, inform
198 logger.debug("Inform Host tracker of new host {}", host);
199 synchronized (this.hostListener) {
200 for (IfHostListener listener : this.hostListener) {
201 listener.hostListener(host);
207 * No further action is needed if this is a gratuitous ARP
209 if (sourceIP.equals(targetIP)) {
214 * No further action is needed if this is a ARP Reply
216 if (pkt.getOpCode() != ARP.REQUEST) {
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
227 * Send ARP reply if target IP is gateway IP
229 if ((targetIP.equals(subnet.getNetworkAddress()))
230 && (isBroadcastMAC(targetMAC) || Arrays.equals(targetMAC,
231 getControllerMAC()))) {
232 sendARPReply(p, getControllerMAC(), targetIP, pkt
233 .getSenderHardwareAddress(), sourceIP);
238 * unknown host, initiate ARP request
240 HostNodeConnector host = hostTracker.hostQuery(targetIP);
242 sendBcastARPRequest(targetIP, subnet);
246 * Known target host, send ARP REPLY
247 * make sure that targetMAC matches the host's MAC if it is not broadcastMAC
249 if (isBroadcastMAC(targetMAC)
250 || Arrays.equals(host.getDataLayerAddressBytes(), targetMAC)) {
251 sendARPReply(p, host.getDataLayerAddressBytes(), host
252 .getNetworkAddress(), pkt.getSenderHardwareAddress(),
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
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
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));
281 nodeConnectors = subnet.getNodeConnectors();
283 for (NodeConnector p : nodeConnectors) {
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,
296 (byte) 0, (byte) 0 })
297 .setTargetProtocolAddress(targetIPB);
299 Ethernet ethernet = new Ethernet();
300 ethernet.setSourceMACAddress(getControllerMAC())
301 .setDestinationMACAddress(new byte[] { (byte) -1,
307 .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
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);
314 this.dataPacketService.transmitDataPacket(destPkt);
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
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();
330 logger.error("cannot send UcastARP because cannot extract node "
331 + "from HostNodeConnector:" + host);
334 NodeConnector outPort = host.getnodeConnector();
335 if (outPort == null) {
336 logger.error("cannot send UcastARP because cannot extract "
337 + "outPort from HostNodeConnector:" + host);
341 byte[] senderIP = subnet.getNetworkAddress().getAddress();
342 byte[] targetIP = host.getNetworkAddress().getAddress();
343 byte[] targetMAC = host.getDataLayerAddressBytes();
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);
353 Ethernet ethernet = new Ethernet();
354 ethernet.setSourceMACAddress(getControllerMAC())
355 .setDestinationMACAddress(targetMAC).setEtherType(
356 EtherTypes.ARP.shortValue()).setPayload(arp);
358 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
359 destPkt.setOutgoingNodeConnector(outPort);
361 this.dataPacketService.transmitDataPacket(destPkt);
364 public void find(InetAddress networkAddress) {
365 logger.debug("Received find IP {}", networkAddress.toString());
367 Subnet subnet = null;
368 if (switchManager != null) {
369 subnet = switchManager.getSubnetByNetworkAddress(networkAddress);
371 if (subnet == null) {
372 logger.debug("can't find subnet matching IP {}", networkAddress
376 logger.debug("found subnet {}", subnet.toString());
378 // send a broadcast ARP Request to this interface
379 sendBcastARPRequest(networkAddress, subnet);
383 * Probe the host by sending a unicast ARP Request to the host
385 public void probe(HostNodeConnector host) {
386 logger.debug("Received probe host {}", host);
388 Subnet subnet = null;
389 if (switchManager != null) {
390 subnet = switchManager.getSubnetByNetworkAddress(host
391 .getNetworkAddress());
393 if (subnet == null) {
394 logger.debug("can't find subnet matching {}", host
395 .getNetworkAddress().toString());
398 sendUcastARPRequest(host, subnet);
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
406 protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p) {
407 InetAddress dIP = null;
409 dIP = InetAddress.getByAddress(NetUtils.intToByteArray4(pkt
410 .getDestinationAddress()));
411 } catch (UnknownHostException e1) {
415 Subnet subnet = null;
416 if (switchManager != null) {
417 subnet = switchManager.getSubnetByNetworkAddress(dIP);
419 if (subnet == null) {
420 logger.debug("can't find subnet matching {}, drop packet", dIP
424 logger.debug("Found {} matching {}", subnet.toString(), dIP.toString());
426 * unknown destination host, initiate ARP request
428 sendBcastARPRequest(dIP, subnet);
432 public byte[] getControllerMAC() {
433 if (switchManager == null) {
436 return switchManager.getControllerMAC();
440 * Function called by the dependency manager when all the required
441 * dependencies are satisfied
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.
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
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
474 void setSwitchManager(ISwitchManager s) {
475 logger.debug("SwitchManager set");
476 this.switchManager = s;
479 void unsetSwitchManager(ISwitchManager s) {
480 if (this.switchManager == s) {
481 logger.debug("SwitchManager removed!");
482 this.switchManager = null;
487 public PacketResult receiveDataPacket(RawPacket inPkt) {
489 return PacketResult.IGNORED;
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");
502 if (nextPak instanceof ARP) {
503 handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt
504 .getIncomingNodeConnector());
505 logger.trace("Handled ARP packet");
508 return PacketResult.IGNORED;