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 = s;
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).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);
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);
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);
310 this.dataPacketService.transmitDataPacket(destPkt);
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
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();
326 logger.error("cannot send UcastARP because cannot extract node "
327 + "from HostNodeConnector:" + host);
330 NodeConnector outPort = host.getnodeConnector();
331 if (outPort == null) {
332 logger.error("cannot send UcastARP because cannot extract "
333 + "outPort from HostNodeConnector:" + host);
337 byte[] senderIP = subnet.getNetworkAddress().getAddress();
338 byte[] targetIP = host.getNetworkAddress().getAddress();
339 byte[] targetMAC = host.getDataLayerAddressBytes();
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);
349 Ethernet ethernet = new Ethernet();
350 ethernet.setSourceMACAddress(getControllerMAC())
351 .setDestinationMACAddress(targetMAC).setEtherType(
352 EtherTypes.ARP.shortValue()).setPayload(arp);
354 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
355 destPkt.setOutgoingNodeConnector(outPort);
357 this.dataPacketService.transmitDataPacket(destPkt);
360 public void find(InetAddress networkAddress) {
361 logger.debug("Received find IP {}", networkAddress.toString());
363 Subnet subnet = null;
364 if (switchManager != null) {
365 subnet = switchManager.getSubnetByNetworkAddress(networkAddress);
367 if (subnet == null) {
368 logger.debug("can't find subnet matching IP {}", networkAddress
372 logger.debug("found subnet {}", subnet.toString());
374 // send a broadcast ARP Request to this interface
375 sendBcastARPRequest(networkAddress, subnet);
379 * Probe the host by sending a unicast ARP Request to the host
381 public void probe(HostNodeConnector host) {
382 logger.debug("Received probe host {}", host);
384 Subnet subnet = null;
385 if (switchManager != null) {
386 subnet = switchManager.getSubnetByNetworkAddress(host
387 .getNetworkAddress());
389 if (subnet == null) {
390 logger.debug("can't find subnet matching {}", host
391 .getNetworkAddress().toString());
394 sendUcastARPRequest(host, subnet);
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
402 protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p) {
403 InetAddress dIP = null;
405 dIP = InetAddress.getByAddress(NetUtils.intToByteArray4(pkt
406 .getDestinationAddress()));
407 } catch (UnknownHostException e1) {
411 Subnet subnet = null;
412 if (switchManager != null) {
413 subnet = switchManager.getSubnetByNetworkAddress(dIP);
415 if (subnet == null) {
416 logger.debug("can't find subnet matching {}, drop packet", dIP
420 logger.debug("Found {} matching {}", subnet.toString(), dIP.toString());
422 * unknown destination host, initiate ARP request
424 sendBcastARPRequest(dIP, subnet);
428 public byte[] getControllerMAC() {
429 if (switchManager == null) {
432 return switchManager.getControllerMAC();
436 * Function called by the dependency manager when all the required
437 * dependencies are satisfied
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.
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
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
470 void setSwitchManager(ISwitchManager s) {
471 logger.debug("SwitchManager set");
472 this.switchManager = s;
475 void unsetSwitchManager(ISwitchManager s) {
476 if (this.switchManager == s) {
477 logger.debug("SwitchManager removed!");
478 this.switchManager = null;
483 public PacketResult receiveDataPacket(RawPacket inPkt) {
485 return PacketResult.IGNORED;
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");
498 if (nextPak instanceof ARP) {
499 handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt
500 .getIncomingNodeConnector());
501 logger.trace("Handled ARP packet");
504 return PacketResult.IGNORED;