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";
}
}
- 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);
byte[] targetIP = tIP.getAddress();
ARP arp = createARP(ARP.REPLY, sMAC, senderIP, tMAC, targetIP);
+ if(log.isTraceEnabled()) {
+ log.trace("Sending Arp Reply with srcMac {} - srcIp {} - dstMac {} - dstIp {} - outport {}",
+ HexEncode.bytesToHexString(sMAC),
+ sIP, HexEncode.bytesToHexString(tMAC), tIP, p);
+ }
+
Ethernet ethernet = createEthernet(sMAC, tMAC, arp);
RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
this.dataPacketService.transmitDataPacket(destPkt);
}
+ private void logArpPacket(ARP pkt, NodeConnector p) {
+ 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.warn("Illegal Ip Address in the ARP packet", e);
+ }
+ }
+
protected void handleARPPacket(Ethernet eHeader, ARP pkt, NodeConnector p) {
+ if(log.isTraceEnabled()) {
+ logArpPacket(pkt, p);
+ }
+
byte[] sourceMAC = eHeader.getSourceMACAddress();
byte[] targetMAC = eHeader.getDestinationMACAddress();
/*
byte[] targetIPByte = targetIP.getAddress();
ARP arp = createARP(ARP.REQUEST, getControllerMAC(), senderIP, targetHardwareAddress, targetIPByte);
+ if(log.isTraceEnabled()) {
+ log.trace("Sending Broadcast Arp Request with srcMac {} - srcIp {} - dstMac {} - dstIp {} - outport {}", HexEncode.bytesToHexString(getControllerMAC()),
+ subnet.getNetworkAddress(), HexEncode.bytesToHexString(targetHardwareAddress), targetIP, p);
+ }
+
byte[] destMACAddress = NetUtils.getBroadcastMACAddr();
Ethernet ethernet = createEthernet(getControllerMAC(), destMACAddress, arp);
byte[] targetMAC = host.getDataLayerAddressBytes();
ARP arp = createARP(ARP.REQUEST, getControllerMAC(), senderIP, targetMAC, targetIP);
+ if(log.isTraceEnabled()) {
+ log.trace("Sending Unicast Arp Request with srcMac {} - srcIp {} - dstMac {} - dstIp {} - outport {}",
+ HexEncode.bytesToHexString(getControllerMAC()),
+ subnet.getNetworkAddress(), HexEncode.bytesToHexString(targetMAC), host.getNetworkAddress(),
+ outPort);
+ }
+
Ethernet ethernet = createEthernet(getControllerMAC(), targetMAC, arp);
RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
log.debug("Can't find subnet matching {}, drop packet", dIP);
return;
}
+ // If packet is sent to the default gw (us), ignore it for now
+ if (subnet.getNetworkAddress().equals(dIP)) {
+ log.trace("Ignore IP packet destined to default gw");
+ return;
+ }
// 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);
}
}