import org.opendaylight.controller.sal.packet.ARP;
import org.opendaylight.controller.sal.packet.Ethernet;
import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IEEE8021Q;
import org.opendaylight.controller.sal.packet.IListenDataPacket;
import org.opendaylight.controller.sal.packet.IPv4;
import org.opendaylight.controller.sal.packet.Packet;
import org.opendaylight.controller.sal.packet.PacketResult;
import org.opendaylight.controller.sal.packet.RawPacket;
-import org.opendaylight.controller.sal.routing.IRouting;
import org.opendaylight.controller.sal.utils.EtherTypes;
import org.opendaylight.controller.sal.utils.HexEncode;
import org.opendaylight.controller.sal.utils.NetUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * The ArpHandler offers services to react on ARP requests and replies
+ * sent by network hosts. Moreover it allows for creating ARP messages
+ * by the controller itself.
+ *
+ * The ARP Handler on ODL doesn't use the requester MAC address in
+ * order to avoid to have to build a spanning tree where to forward
+ * ARP Requests. The ARP requests are broadcast packets so in order to
+ * reach everywhere need to be flooded, when you flood in a network
+ * that is not a tree (all the networks has some level of redundancy)
+ * that would create forwarding loops without a spanning tree. Given
+ * the need is only to send out the ARP requests toward all the hosts
+ * we actually don't need to implement a flooding mechanism in software
+ * (which would be expensive) we just send out the ARP request toward
+ * all the ports that are suspected to be host ports on all the
+ * switches (from the controller). Now the condition for which a port
+ * is marked as host port could potentially be incorrect so when the
+ * controller sends out the ARP Request that could come back to the
+ * controller and could cause another request not needed. So changing
+ * the source MAC address of the request to be the one of the controller,
+ * controller can protect itself from honoring twice the same request.
+ * This enables an ARP handler resolution, without the need of spanning
+ * tree and limiting software flooding to the minimum required.
+ */
+
public class ArpHandler implements IHostFinder, IListenDataPacket, ICacheUpdateAware<ARPEvent, Boolean> {
private static final Logger log = LoggerFactory.getLogger(ArpHandler.class);
static final String ARP_EVENT_CACHE_NAME = "arphandler.arpRequestReplyEvent";
private ISwitchManager switchManager;
private ITopologyManager topologyManager;
private IDataPacketService dataPacketService;
- private IRouting routing;
private IClusterContainerServices clusterContainerService;
private IConnectionManager connectionManager;
private Set<IfHostListener> hostListeners = new CopyOnWriteArraySet<IfHostListener>();
}
}
- void setRouting(IRouting r) {
- this.routing = r;
- }
-
- void unsetRouting(IRouting r) {
- if (this.routing == r) {
- this.routing = null;
- }
- }
-
void setHostListener(IfHostListener s) {
if (this.hostListeners != null) {
this.hostListeners.add(s);
}
}
- protected void sendARPReply(NodeConnector p, byte[] sMAC, InetAddress sIP, byte[] tMAC, InetAddress tIP) {
+ protected void sendARPReply(NodeConnector p, byte[] sMAC, InetAddress sIP, byte[] tMAC, InetAddress tIP, short vlan) {
byte[] senderIP = sIP.getAddress();
byte[] targetIP = tIP.getAddress();
ARP arp = createARP(ARP.REPLY, sMAC, senderIP, tMAC, targetIP);
sIP, HexEncode.bytesToHexString(tMAC), tIP, p);
}
- Ethernet ethernet = createEthernet(sMAC, tMAC, arp);
+ Ethernet ethernet = createEthernet(sMAC, tMAC, arp, vlan);
RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
destPkt.setOutgoingNodeConnector(p);
this.dataPacketService.transmitDataPacket(destPkt);
}
- private void logArpPacket(ARP pkt, NodeConnector p) {
+ private void logArpPacket(ARP pkt, NodeConnector p, short vlan) {
try {
- if (pkt.getOpCode() == ARP.REQUEST) {
- log.trace("Received Arp Request with srcMac {} - srcIp {} - dstMac {} - dstIp {} - inport {}", HexEncode.bytesToHexString(pkt.getSenderHardwareAddress()),
- InetAddress.getByAddress(pkt.getSenderProtocolAddress()), HexEncode.bytesToHexString(pkt.getTargetHardwareAddress()),
- InetAddress.getByAddress(pkt.getTargetProtocolAddress()), p);
- } else if(pkt.getOpCode() == ARP.REPLY) {
- log.trace("Received Arp Reply with srcMac {} - srcIp {} - dstMac {} - dstIp {} - inport {}", HexEncode.bytesToHexString(pkt.getSenderHardwareAddress()),
- InetAddress.getByAddress(pkt.getSenderProtocolAddress()), HexEncode.bytesToHexString(pkt.getTargetHardwareAddress()),
- InetAddress.getByAddress(pkt.getTargetProtocolAddress()), p);
- }
- } catch(UnknownHostException e) {
+ log.trace("Received Arp {} with srcMac {} - srcIp {} - dstMac {} - dstIp {} - inport {} {}",
+ ((pkt.getOpCode() == ARP.REQUEST) ? "Request" : "Reply"),
+ HexEncode.bytesToHexString(pkt.getSenderHardwareAddress()),
+ InetAddress.getByAddress(pkt.getSenderProtocolAddress()),
+ HexEncode.bytesToHexString(pkt.getTargetHardwareAddress()),
+ InetAddress.getByAddress(pkt.getTargetProtocolAddress()), p, (vlan != 0 ? "on vlan " + vlan : ""));
+
+ } catch (UnknownHostException e) {
log.warn("Illegal Ip Address in the ARP packet", e);
}
}
- protected void handleARPPacket(Ethernet eHeader, ARP pkt, NodeConnector p) {
+ protected void handleARPPacket(Ethernet eHeader, ARP pkt, NodeConnector p, short vlan) {
if(log.isTraceEnabled()) {
- logArpPacket(pkt, p);
+ logArpPacket(pkt, p, vlan);
}
byte[] sourceMAC = eHeader.getSourceMACAddress();
HostNodeConnector requestor = null;
if (NetUtils.isUnicastMACAddr(sourceMAC) && p.getNode() != null) {
try {
- requestor = new HostNodeConnector(sourceMAC, sourceIP, p, subnet.getVlan());
+ requestor = new HostNodeConnector(sourceMAC, sourceIP, p, vlan);
} catch (ConstructionException e) {
log.debug("Received ARP packet with invalid MAC: {}", HexEncode.bytesToHexString(sourceMAC));
return;
// the true value indicates we should generate replies to requestors
// across the cluster
log.trace("Received ARP reply packet from {}, reply to all requestors.", sourceIP);
- arpRequestReplyEvent.put(new ARPReply(sourceIP, sourceMAC), true);
+ arpRequestReplyEvent.put(new ARPReply(sourceIP, sourceMAC, vlan), true);
return;
}
log.trace("Received local ARP req. for default gateway. Replying with controller MAC: {}",
HexEncode.bytesToHexString(getControllerMAC()));
}
- sendARPReply(p, getControllerMAC(), targetIP, pkt.getSenderHardwareAddress(), sourceIP);
+ sendARPReply(p, getControllerMAC(), targetIP, pkt.getSenderHardwareAddress(), sourceIP, vlan);
} else {
log.trace("Received non-local ARP req. for default gateway. Raising reply event");
arpRequestReplyEvent.put(
- new ARPReply(p, targetIP, getControllerMAC(), sourceIP, pkt.getSenderHardwareAddress()), false);
+ new ARPReply(p, targetIP, getControllerMAC(), sourceIP, pkt.getSenderHardwareAddress(), vlan), false);
}
return;
}
log.trace("Received ARP req. for known host {}, sending reply...", targetIP);
if (connectionManager.getLocalityStatus(p.getNode()) == ConnectionLocality.LOCAL) {
sendARPReply(p, host.getDataLayerAddressBytes(), host.getNetworkAddress(),
- pkt.getSenderHardwareAddress(), sourceIP);
+ pkt.getSenderHardwareAddress(), sourceIP, vlan);
} else {
arpRequestReplyEvent.put(new ARPReply(p, host.getNetworkAddress(), host.getDataLayerAddressBytes(),
- sourceIP, pkt.getSenderHardwareAddress()), false);
+ sourceIP, pkt.getSenderHardwareAddress(), vlan), false);
}
} else {
/*
}
byte[] destMACAddress = NetUtils.getBroadcastMACAddr();
- Ethernet ethernet = createEthernet(getControllerMAC(), destMACAddress, arp);
+ Ethernet ethernet = createEthernet(getControllerMAC(), destMACAddress, arp, (short)0);
// TODO For now send port-by-port, see how to optimize to
// send to multiple ports at once
}
/**
- * Send a unicast ARP Request to the known host on a specific switch/port as
- * defined in the host. The sender IP is the networkAddress of the subnet
- * The sender MAC is the controller's MAC
+ * Send a unicast ARP Request to the known host on specific (switch/port,
+ * vlan) as defined in the host. The sender IP is the networkAddress of the
+ * subnet The sender MAC is the controller's MAC
*/
protected void sendUcastARPRequest(HostNodeConnector host, Subnet subnet) {
log.trace("sendUcastARPRequest host:{} subnet:{}", host, subnet);
outPort);
}
- Ethernet ethernet = createEthernet(getControllerMAC(), targetMAC, arp);
+ Ethernet ethernet = createEthernet(getControllerMAC(), targetMAC, arp, host.getVlan());
RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
destPkt.setOutgoingNodeConnector(outPort);
* @param pkt
* @param p
*/
- protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p) {
+ protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p, short vlan) {
InetAddress dIP = NetUtils.getInetAddress(pkt.getDestinationAddress());
if (dIP == null) {
// see if we know about the host
// Hosttracker hosts db key implementation
- IHostId id = HostIdFactory.create(dIP, null);
- HostNodeConnector host = hostTracker.hostFind(id);
+ HostNodeConnector host = hostTracker.hostFind(dIP);
if (host == null) {
- // if we don't, know about the host, try to find it
+ // if we don't know about the host, try to find it
log.trace("Punted IP pkt to {}, sending bcast ARP event...", dIP);
/*
* unknown destination host, initiate bcast ARP request
*/
arpRequestReplyEvent.put(new ARPRequest(dIP, subnet), false);
- } else if (routing == null || routing.getRoute(p.getNode(), host.getnodeconnectorNode()) != null) {
- /*
- * if IRouting is available, make sure that this packet can get it's
- * destination normally before teleporting it there. If it's not
- * available, then assume it's reachable.
- *
- * TODO: come up with a way to do this in the absence of IRouting
- */
-
- log.trace("forwarding punted IP pkt to {} received at {}", dIP, p);
-
- /*
- * if we know where the host is and there's a path from where this
- * packet was punted to where the host is, then deliver it to the
- * host for now
- */
- NodeConnector nc = host.getnodeConnector();
-
- // re-encode the Ethernet packet (the parent of the IPv4 packet)
- RawPacket rp = this.dataPacketService.encodeDataPacket(pkt.getParent());
- rp.setOutgoingNodeConnector(nc);
- this.dataPacketService.transmitDataPacket(rp);
} else {
- log.trace("ignoring punted IP pkt to {} because there is no route from {}", dIP, p);
+ log.trace("Ignoring punted IP pkt to known host: {} (received on: {})", dIP, p);
}
}
log.trace("Received a frame of size: {}", inPkt.getPacketData().length);
Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
if (formattedPak instanceof Ethernet) {
- Object nextPak = formattedPak.getPayload();
+ Packet nextPak = formattedPak.getPayload();
+ short vlan = 0;
+ if (nextPak instanceof IEEE8021Q) {
+ vlan = ((IEEE8021Q) nextPak).getVid();
+ log.trace("Moved after the dot1Q header");
+ nextPak = ((IEEE8021Q) nextPak).getPayload();
+ }
if (nextPak instanceof IPv4) {
log.trace("Handle IP packet: {}", formattedPak);
- handlePuntedIPPacket((IPv4) nextPak, inPkt.getIncomingNodeConnector());
+ handlePuntedIPPacket((IPv4) nextPak, inPkt.getIncomingNodeConnector(), vlan);
} else if (nextPak instanceof ARP) {
log.trace("Handle ARP packet: {}", formattedPak);
- handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt.getIncomingNodeConnector());
+ handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt.getIncomingNodeConnector(), vlan);
}
}
return PacketResult.IGNORED;
return arp;
}
- private Ethernet createEthernet(byte[] sourceMAC, byte[] targetMAC, ARP arp) {
+ private Ethernet createEthernet(byte[] sourceMAC, byte[] targetMAC, ARP arp, short vlan) {
Ethernet ethernet = new Ethernet();
ethernet.setSourceMACAddress(sourceMAC);
ethernet.setDestinationMACAddress(targetMAC);
- ethernet.setEtherType(EtherTypes.ARP.shortValue());
- ethernet.setPayload(arp);
+ if (vlan == 0) {
+ ethernet.setEtherType(EtherTypes.ARP.shortValue());
+ ethernet.setPayload(arp);
+ } else {
+ IEEE8021Q dot1q = new IEEE8021Q();
+ dot1q.setVid(vlan);
+ dot1q.setEtherType(EtherTypes.ARP.shortValue());
+ dot1q.setPayload(arp);
+ ethernet.setEtherType(EtherTypes.VLANTAGGED.shortValue());
+ ethernet.setPayload(dot1q);
+ }
return ethernet;
}
}
}
- private void generateAndSendReply(InetAddress sourceIP, byte[] sourceMAC) {
+ private void generateAndSendReply(InetAddress sourceIP, byte[] sourceMAC, short vlan) {
if (log.isTraceEnabled()) {
log.trace("generateAndSendReply called with params sourceIP:{} sourceMAC:{}", sourceIP,
HexEncode.bytesToHexString(sourceMAC));
for (HostNodeConnector host : hosts) {
if (log.isTraceEnabled()) {
log.trace(
- "Sending ARP Reply with src {}/{}, target {}/{}",
+ "Sending ARP Reply with src {}/{}, target {}/{} {}",
new Object[] { HexEncode.bytesToHexString(sourceMAC), sourceIP,
- HexEncode.bytesToHexString(host.getDataLayerAddressBytes()), host.getNetworkAddress() });
+ HexEncode.bytesToHexString(host.getDataLayerAddressBytes()), host.getNetworkAddress(),
+ (vlan != 0 ? "on vlan " + vlan : "") });
}
if (connectionManager.getLocalityStatus(host.getnodeconnectorNode()) == ConnectionLocality.LOCAL) {
sendARPReply(host.getnodeConnector(), sourceMAC, sourceIP, host.getDataLayerAddressBytes(),
- host.getNetworkAddress());
+ host.getNetworkAddress(), vlan);
} else {
/*
* In the remote event a requestor moved to another controller
*/
arpRequestReplyEvent.put(
new ARPReply(host.getnodeConnector(), sourceIP, sourceMAC, host.getNetworkAddress(), host
- .getDataLayerAddressBytes()), false);
+ .getDataLayerAddressBytes(), vlan), false);
}
}
}
// requestors across the cluster
if (ev.isNewReply()) {
log.trace("Trigger a generateAndSendReply in response to {}", rep);
- generateAndSendReply(rep.getTargetIP(), rep.getTargetMac());
+ generateAndSendReply(rep.getTargetIP(), rep.getTargetMac(), rep.getVlan());
// Otherwise, a specific reply. If local, send out.
} else if (connectionManager.getLocalityStatus(rep.getPort().getNode()) == ConnectionLocality.LOCAL) {
log.trace("ARPCacheEventHandler - sendUcatARPReply locally in response to {}", rep);
sendARPReply(rep.getPort(), rep.getSourceMac(), rep.getSourceIP(), rep.getTargetMac(),
- rep.getTargetIP());
+ rep.getTargetIP(), rep.getVlan());
}
}
} catch (InterruptedException e) {