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: {}",
136 logger.debug("Received ARP REPLY Packet from NodeConnector: {}",
139 InetAddress targetIP = null;
141 targetIP = InetAddress.getByAddress(pkt.getTargetProtocolAddress());
142 } catch (UnknownHostException e1) {
145 InetAddress sourceIP = null;
147 sourceIP = InetAddress.getByAddress(pkt.getSenderProtocolAddress());
148 } catch (UnknownHostException e1) {
151 byte[] targetMAC = eHeader.getDestinationMACAddress();
152 byte[] sourceMAC = eHeader.getSourceMACAddress();
155 * Sanity Check; drop ARP packets originated by the controller itself.
156 * This is to avoid continuous flooding
158 if (Arrays.equals(sourceMAC, getControllerMAC())) {
159 if (logger.isDebugEnabled()) {
161 "Receive the self originated packet (srcMAC {}) --> DROP",
162 HexEncode.bytesToHexString(sourceMAC));
167 Subnet subnet = null;
168 if (switchManager != null) {
169 subnet = switchManager.getSubnetByNetworkAddress(sourceIP);
171 if (subnet == null) {
172 logger.debug("can't find subnet matching {}, drop packet",sourceIP);
175 logger.debug("Found {} matching {}", subnet, sourceIP);
177 * Make sure that the host is a legitimate member of this subnet
179 if (!subnet.hasNodeConnector(p)) {
180 logger.debug("{} showing up on {} does not belong to {}",
181 new Object[] { sourceIP, p, subnet });
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;
191 host = new HostNodeConnector(sourceMAC, sourceIP, p, subnet
193 } catch (ConstructionException e) {
197 * Learn host from the received ARP REQ/REPLY, inform
200 logger.debug("Inform Host tracker of new host {}", host);
201 synchronized (this.hostListener) {
202 for (IfHostListener listener : this.hostListener) {
203 listener.hostListener(host);
209 * No further action is needed if this is a gratuitous ARP
211 if (sourceIP.equals(targetIP)) {
216 * No further action is needed if this is a ARP Reply
218 if (pkt.getOpCode() != ARP.REQUEST) {
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
229 * Send ARP reply if target IP is gateway IP
231 if ((targetIP.equals(subnet.getNetworkAddress()))
232 && (isBroadcastMAC(targetMAC) || Arrays.equals(targetMAC,
233 getControllerMAC()))) {
234 sendARPReply(p, getControllerMAC(), targetIP, pkt
235 .getSenderHardwareAddress(), sourceIP);
240 * unknown host, initiate ARP request
242 HostNodeConnector host = hostTracker.hostQuery(targetIP);
244 sendBcastARPRequest(targetIP, subnet);
248 * Known target host, send ARP REPLY
249 * make sure that targetMAC matches the host's MAC if it is not broadcastMAC
251 if (isBroadcastMAC(targetMAC)
252 || Arrays.equals(host.getDataLayerAddressBytes(), targetMAC)) {
253 sendARPReply(p, host.getDataLayerAddressBytes(), host
254 .getNetworkAddress(), pkt.getSenderHardwareAddress(),
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
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
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));
283 nodeConnectors = subnet.getNodeConnectors();
285 for (NodeConnector p : nodeConnectors) {
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,
298 (byte) 0, (byte) 0 })
299 .setTargetProtocolAddress(targetIPB);
301 Ethernet ethernet = new Ethernet();
302 ethernet.setSourceMACAddress(getControllerMAC())
303 .setDestinationMACAddress(new byte[] { (byte) -1,
309 .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
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);
316 this.dataPacketService.transmitDataPacket(destPkt);
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
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();
332 logger.error("cannot send UcastARP because cannot extract node "
333 + "from HostNodeConnector: {}", host);
336 NodeConnector outPort = host.getnodeConnector();
337 if (outPort == null) {
338 logger.error("cannot send UcastARP because cannot extract "
339 + "outPort from HostNodeConnector: {}", host);
343 byte[] senderIP = subnet.getNetworkAddress().getAddress();
344 byte[] targetIP = host.getNetworkAddress().getAddress();
345 byte[] targetMAC = host.getDataLayerAddressBytes();
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);
355 Ethernet ethernet = new Ethernet();
356 ethernet.setSourceMACAddress(getControllerMAC())
357 .setDestinationMACAddress(targetMAC).setEtherType(
358 EtherTypes.ARP.shortValue()).setPayload(arp);
360 RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
361 destPkt.setOutgoingNodeConnector(outPort);
363 this.dataPacketService.transmitDataPacket(destPkt);
366 public void find(InetAddress networkAddress) {
367 logger.debug("Received find IP {}", networkAddress);
369 Subnet subnet = null;
370 if (switchManager != null) {
371 subnet = switchManager.getSubnetByNetworkAddress(networkAddress);
373 if (subnet == null) {
374 logger.debug("can't find subnet matching IP {}", networkAddress);
377 logger.debug("found subnet {}", subnet);
379 // send a broadcast ARP Request to this interface
380 sendBcastARPRequest(networkAddress, subnet);
384 * Probe the host by sending a unicast ARP Request to the host
386 public void probe(HostNodeConnector host) {
387 logger.debug("Received probe host {}", host);
389 Subnet subnet = null;
390 if (switchManager != null) {
391 subnet = switchManager.getSubnetByNetworkAddress(host
392 .getNetworkAddress());
394 if (subnet == null) {
395 logger.debug("can't find subnet matching {}", host
396 .getNetworkAddress());
399 sendUcastARPRequest(host, subnet);
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
407 protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p) {
408 InetAddress dIP = null;
410 dIP = InetAddress.getByAddress(NetUtils.intToByteArray4(pkt
411 .getDestinationAddress()));
412 } catch (UnknownHostException e1) {
416 Subnet subnet = null;
417 if (switchManager != null) {
418 subnet = switchManager.getSubnetByNetworkAddress(dIP);
420 if (subnet == null) {
421 logger.debug("can't find subnet matching {}, drop packet", dIP);
424 logger.debug("Found {} matching {}", subnet, dIP);
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;