ProxyArpManager: Factor out common logic for handling ARP and NDP packets.
Change-Id: I0709f9cda2506f01dbc912b8c3e12443ec217fed
diff --git a/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java b/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java
index 742d7af..2eb96df 100644
--- a/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java
+++ b/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java
@@ -82,7 +82,7 @@
packetService.addProcessor(processor, PacketProcessor.director(1));
readComponentConfiguration(context);
- requestPackests();
+ requestPackets();
log.info("Started with Application ID {}", appId.id());
}
@@ -99,13 +99,13 @@
@Modified
public void modified(ComponentContext context) {
readComponentConfiguration(context);
- requestPackests();
+ requestPackets();
}
/**
* Request packet in via PacketService.
*/
- private void requestPackests() {
+ private void requestPackets() {
TrafficSelector.Builder selectorBuilder =
DefaultTrafficSelector.builder();
selectorBuilder.matchEthType(TYPE_ARP);
diff --git a/core/api/src/main/java/org/onosproject/net/proxyarp/ProxyArpService.java b/core/api/src/main/java/org/onosproject/net/proxyarp/ProxyArpService.java
index 8ffe17a..9b27f4f 100644
--- a/core/api/src/main/java/org/onosproject/net/proxyarp/ProxyArpService.java
+++ b/core/api/src/main/java/org/onosproject/net/proxyarp/ProxyArpService.java
@@ -21,7 +21,7 @@
import org.onosproject.net.packet.PacketContext;
/**
- * Service for processing arp requests on behalf of applications.
+ * Service for processing ARP or NDP requests on behalf of applications.
*/
// TODO: move to the peer host package
public interface ProxyArpService {
diff --git a/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java b/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
index fe25236..d437bf3 100644
--- a/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
+++ b/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
@@ -33,8 +33,6 @@
import org.onosproject.net.Host;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
import org.onosproject.net.host.HostProvider;
import org.onosproject.net.host.InterfaceIpAddress;
import org.onosproject.net.packet.DefaultOutboundPacket;
@@ -43,9 +41,7 @@
import org.onosproject.net.provider.ProviderId;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -149,7 +145,7 @@
Set<Host> hosts = hostManager.getHostsByIp(ip);
if (hosts.isEmpty()) {
- sendArpNdpRequest(ip);
+ sendRequest(ip);
} else {
for (Host host : hosts) {
HostProvider provider = hostProviders.get(host.providerId());
@@ -166,12 +162,11 @@
}
/**
- * Sends an ARP or Neighbor Discovery Protocol request for the given IP
- * address.
+ * Sends an ARP or NDP request for the given IP address.
*
* @param targetIp IP address to send the request for
*/
- private void sendArpNdpRequest(IpAddress targetIp) {
+ private void sendRequest(IpAddress targetIp) {
Interface intf = interfaceService.getMatchingInterface(targetIp);
if (intf == null) {
@@ -180,31 +175,26 @@
for (InterfaceIpAddress ia : intf.ipAddresses()) {
if (ia.subnetAddress().contains(targetIp)) {
- sendArpNdpProbe(intf.connectPoint(), targetIp, ia.ipAddress(),
+ sendProbe(intf.connectPoint(), targetIp, ia.ipAddress(),
intf.mac(), intf.vlan());
}
}
}
- private void sendArpNdpProbe(ConnectPoint connectPoint,
- IpAddress targetIp,
- IpAddress sourceIp, MacAddress sourceMac,
- VlanId vlan) {
+ private void sendProbe(ConnectPoint connectPoint,
+ IpAddress targetIp,
+ IpAddress sourceIp, MacAddress sourceMac,
+ VlanId vlan) {
Ethernet probePacket = null;
if (targetIp.isIp4()) {
// IPv4: Use ARP
- probePacket = buildArpRequest(targetIp, sourceIp, sourceMac,
- vlan);
+ probePacket = buildArpRequest(targetIp, sourceIp, sourceMac, vlan);
} else {
// IPv6: Use Neighbor Discovery
- probePacket = buildNdpRequest(targetIp, sourceIp, sourceMac,
- vlan);
+ probePacket = buildNdpRequest(targetIp, sourceIp, sourceMac, vlan);
}
- List<Instruction> instructions = new ArrayList<>();
- instructions.add(Instructions.createOutput(connectPoint.port()));
-
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(connectPoint.port())
.build();
@@ -273,7 +263,7 @@
icmp6.setIcmpType(ICMP6.NEIGHBOR_SOLICITATION);
icmp6.setIcmpCode((byte) 0);
- // Create the Neighbor Solication packet
+ // Create the Neighbor Solicitation packet
NeighborSolicitation ns = new NeighborSolicitation();
ns.setTargetAddress(targetIp.toOctets());
ns.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS,
diff --git a/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
index 398260f..25a2640 100644
--- a/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
@@ -69,11 +69,9 @@
private final Logger log = getLogger(getClass());
- private static final String MAC_ADDR_NULL = "Mac address cannot be null.";
+ private static final String MAC_ADDR_NULL = "MAC address cannot be null.";
private static final String REQUEST_NULL = "ARP or NDP request cannot be null.";
- private static final String REQUEST_NOT_ARP = "Ethernet frame does not contain ARP request.";
- private static final String NOT_ARP_REQUEST = "ARP is not a request.";
- private static final String NOT_ARP_REPLY = "ARP is not a reply.";
+ private static final String MSG_NOT_REQUEST = "Message is not an ARP or NDP request";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected EdgePortService edgeService;
@@ -96,6 +94,14 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected InterfaceService interfaceService;
+ private enum Protocol {
+ ARP, NDP
+ }
+
+ private enum MessageType {
+ REQUEST, REPLY
+ }
+
@Activate
public void activate() {
store.setDelegate(this::sendTo);
@@ -123,46 +129,48 @@
checkNotNull(eth, REQUEST_NULL);
- if (eth.getEtherType() == Ethernet.TYPE_ARP) {
- replyArp(eth, inPort);
- } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
- replyNdp(eth, inPort);
+ MessageContext context = createContext(eth, inPort);
+ if (context != null) {
+ replyInternal(context);
}
}
- private void replyArp(Ethernet eth, ConnectPoint inPort) {
- ARP arp = (ARP) eth.getPayload();
- checkArgument(arp.getOpCode() == ARP.OP_REQUEST, NOT_ARP_REQUEST);
- checkNotNull(inPort);
- Ip4Address targetAddress = Ip4Address.valueOf(arp.getTargetProtocolAddress());
+ /**
+ * Handles a request message.
+ *
+ * If the MAC address of the target is known, we can reply directly to the
+ * requestor. Otherwise, we forward the request out other ports in an
+ * attempt to find the correct host.
+ *
+ * @param context request message context to process
+ */
+ private void replyInternal(MessageContext context) {
+ checkNotNull(context);
+ checkArgument(context.type() == MessageType.REQUEST, MSG_NOT_REQUEST);
- VlanId vlan = vlanId(eth.getVlanID());
-
- if (hasIpAddress(inPort)) {
+ if (hasIpAddress(context.inPort())) {
// If the request came from outside the network, only reply if it was
// for one of our external addresses.
- interfaceService.getInterfacesByPort(inPort)
+ interfaceService.getInterfacesByPort(context.inPort())
.stream()
.filter(intf -> intf.ipAddresses()
.stream()
- .anyMatch(ia -> ia.ipAddress().equals(targetAddress)))
- .forEach(intf -> buildAndSendArp(targetAddress, intf.mac(), eth, inPort));
+ .anyMatch(ia -> ia.ipAddress().equals(context.target())))
+ .forEach(intf -> buildAndSendReply(context, intf.mac()));
// Stop here and don't proxy ARPs if the port has an IP address
return;
}
// See if we have the target host in the host store
-
- Set<Host> hosts = hostService.getHostsByIp(targetAddress);
+ Set<Host> hosts = hostService.getHostsByIp(context.target());
Host dst = null;
- Host src = hostService.getHost(hostId(eth.getSourceMAC(),
- vlanId(eth.getVlanID())));
+ Host src = hostService.getHost(hostId(context.srcMac(), context.vlan()));
for (Host host : hosts) {
- if (host.vlan().equals(vlan)) {
+ if (host.vlan().equals(context.vlan())) {
dst = host;
break;
}
@@ -170,22 +178,19 @@
if (src != null && dst != null) {
// We know the target host so we can respond
- buildAndSendArp(targetAddress, dst.mac(), eth, inPort);
+ buildAndSendReply(context, dst.mac());
return;
}
// If the source address matches one of our external addresses
// it could be a request from an internal host to an external
// address. Forward it over to the correct port.
- Ip4Address source =
- Ip4Address.valueOf(arp.getSenderProtocolAddress());
-
boolean matched = false;
- Set<Interface> interfaces = interfaceService.getInterfacesByIp(source);
+ Set<Interface> interfaces = interfaceService.getInterfacesByIp(context.sender());
for (Interface intf : interfaces) {
- if (intf.vlan().equals(vlan)) {
+ if (intf.vlan().equals(context.vlan())) {
matched = true;
- sendTo(eth, intf.connectPoint());
+ sendTo(context.packet(), intf.connectPoint());
break;
}
}
@@ -196,89 +201,33 @@
// The request couldn't be resolved.
// Flood the request on all ports except the incoming port.
- flood(eth, inPort);
- }
-
- private void replyNdp(Ethernet eth, ConnectPoint inPort) {
- IPv6 ipv6 = (IPv6) eth.getPayload();
- ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
- NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload();
- Ip6Address targetAddress = Ip6Address.valueOf(nsol.getTargetAddress());
-
- VlanId vlan = vlanId(eth.getVlanID());
-
- // If the request came from outside the network, only reply if it was
- // for one of our external addresses.
- if (hasIpAddress(inPort)) {
- interfaceService.getInterfacesByPort(inPort)
- .stream()
- .filter(intf -> intf.ipAddresses()
- .stream()
- .anyMatch(ia -> ia.ipAddress().equals(targetAddress)))
- .forEach(intf -> buildAndSendNdp(targetAddress, intf.mac(), eth, inPort));
- return;
- }
-
- // Continue with normal proxy ARP case
-
- Set<Host> hosts = hostService.getHostsByIp(targetAddress);
-
- Host dst = null;
- Host src = hostService.getHost(hostId(eth.getSourceMAC(),
- vlanId(eth.getVlanID())));
-
- for (Host host : hosts) {
- if (host.vlan().equals(vlan)) {
- dst = host;
- break;
- }
- }
-
- if (src != null && dst != null) {
- // We know the target host so we can respond
- buildAndSendNdp(targetAddress, dst.mac(), eth, inPort);
- return;
- }
-
- // If the source address matches one of our external addresses
- // it could be a request from an internal host to an external
- // address. Forward it over to the correct port.
- Ip6Address source =
- Ip6Address.valueOf(ipv6.getSourceAddress());
-
- boolean matched = false;
-
- Set<Interface> interfaces = interfaceService.getInterfacesByIp(source);
- for (Interface intf : interfaces) {
- if (intf.vlan().equals(vlan)) {
- matched = true;
- sendTo(eth, intf.connectPoint());
- break;
- }
- }
-
- if (matched) {
- return;
- }
-
- // The request couldn't be resolved.
- // Flood the request on all ports except the incoming ports.
- flood(eth, inPort);
- }
- //TODO checkpoint
-
- private void buildAndSendArp(Ip4Address srcIp, MacAddress srcMac,
- Ethernet request, ConnectPoint port) {
- sendTo(ARP.buildArpReply(srcIp, srcMac, request), port);
- }
-
- private void buildAndSendNdp(Ip6Address srcIp, MacAddress srcMac,
- Ethernet request, ConnectPoint port) {
- sendTo(buildNdpReply(srcIp, srcMac, request), port);
+ flood(context.packet(), context.inPort());
}
/**
- * Outputs the given packet out the given port.
+ * Builds and sends a reply message given a request context and the resolved
+ * MAC address to answer with.
+ *
+ * @param context message context of request
+ * @param targetMac MAC address to be given in the response
+ */
+ private void buildAndSendReply(MessageContext context, MacAddress targetMac) {
+ switch (context.protocol()) {
+ case ARP:
+ sendTo(ARP.buildArpReply((Ip4Address) context.target(),
+ targetMac, context.packet()), context.inPort());
+ break;
+ case NDP:
+ sendTo(buildNdpReply((Ip6Address) context.target(), targetMac,
+ context.packet()), context.inPort());
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Outputs a packet out a specific port.
*
* @param packet the packet to send
* @param outPort the port to send it out
@@ -287,6 +236,12 @@
sendTo(outPort, ByteBuffer.wrap(packet.serialize()));
}
+ /**
+ * Outputs a packet out a specific port.
+ *
+ * @param outPort port to send it out
+ * @param packet packet to send
+ */
private void sendTo(ConnectPoint outPort, ByteBuffer packet) {
if (!edgeService.isEdgePoint(outPort)) {
// Sanity check to make sure we don't send the packet out an
@@ -298,7 +253,7 @@
TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
builder.setOutput(outPort.port());
packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
- builder.build(), packet));
+ builder.build(), packet));
}
/**
@@ -323,7 +278,7 @@
checkNotNull(eth, REQUEST_NULL);
Host h = hostService.getHost(hostId(eth.getDestinationMAC(),
- vlanId(eth.getVlanID())));
+ vlanId(eth.getVlanID())));
if (h == null) {
flood(eth, inPort);
@@ -344,42 +299,24 @@
if (ethPkt == null) {
return false;
}
- if (ethPkt.getEtherType() == Ethernet.TYPE_ARP) {
- return handleArp(context, ethPkt);
- } else if (ethPkt.getEtherType() == Ethernet.TYPE_IPV6) {
- return handleNdp(context, ethPkt);
- }
- return false;
- }
- private boolean handleArp(PacketContext context, Ethernet ethPkt) {
- ARP arp = (ARP) ethPkt.getPayload();
+ MessageContext msgContext = createContext(ethPkt, pkt.receivedFrom());
- if (arp.getOpCode() == ARP.OP_REPLY) {
- forward(ethPkt, context.inPacket().receivedFrom());
- } else if (arp.getOpCode() == ARP.OP_REQUEST) {
- reply(ethPkt, context.inPacket().receivedFrom());
- } else {
+ if (msgContext == null) {
return false;
}
- context.block();
- return true;
- }
- private boolean handleNdp(PacketContext context, Ethernet ethPkt) {
- IPv6 ipv6 = (IPv6) ethPkt.getPayload();
-
- if (ipv6.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
+ switch (msgContext.type()) {
+ case REPLY:
+ forward(msgContext.packet(), msgContext.inPort());
+ break;
+ case REQUEST:
+ replyInternal(msgContext);
+ break;
+ default:
return false;
}
- ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
- if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_ADVERTISEMENT) {
- forward(ethPkt, context.inPacket().receivedFrom());
- } else if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_SOLICITATION) {
- reply(ethPkt, context.inPacket().receivedFrom());
- } else {
- return false;
- }
+
context.block();
return true;
}
@@ -444,4 +381,148 @@
eth.setPayload(ipv6);
return eth;
}
+
+ /**
+ * Attempts to create a MessageContext for the given Ethernet frame. If the
+ * frame is a valid ARP or NDP request or response, a context will be
+ * created.
+ *
+ * @param eth input Ethernet frame
+ * @param inPort in port
+ * @return MessageContext if the packet was ARP or NDP, otherwise null
+ */
+ private MessageContext createContext(Ethernet eth, ConnectPoint inPort) {
+ if (eth.getEtherType() == Ethernet.TYPE_ARP) {
+ return createArpContext(eth, inPort);
+ } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
+ return createNdpContext(eth, inPort);
+ }
+
+ return null;
+ }
+
+ /**
+ * Extracts context information from ARP packets.
+ *
+ * @param eth input Ethernet frame that is thought to be ARP
+ * @param inPort in port
+ * @return MessageContext object if the packet was a valid ARP packet,
+ * otherwise null
+ */
+ private MessageContext createArpContext(Ethernet eth, ConnectPoint inPort) {
+ if (eth.getEtherType() != Ethernet.TYPE_ARP) {
+ return null;
+ }
+
+ ARP arp = (ARP) eth.getPayload();
+
+ IpAddress target = Ip4Address.valueOf(arp.getTargetProtocolAddress());
+ IpAddress sender = Ip4Address.valueOf(arp.getSenderProtocolAddress());
+
+ MessageType type;
+ if (arp.getOpCode() == ARP.OP_REQUEST) {
+ type = MessageType.REQUEST;
+ } else if (arp.getOpCode() == ARP.OP_REPLY) {
+ type = MessageType.REPLY;
+ } else {
+ return null;
+ }
+
+ return new MessageContext(eth, inPort, Protocol.ARP, type, target, sender);
+ }
+
+ /**
+ * Extracts context information from NDP packets.
+ *
+ * @param eth input Ethernet frame that is thought to be NDP
+ * @param inPort in port
+ * @return MessageContext object if the packet was a valid NDP packet,
+ * otherwise null
+ */
+ private MessageContext createNdpContext(Ethernet eth, ConnectPoint inPort) {
+ if (eth.getEtherType() != Ethernet.TYPE_IPV6) {
+ return null;
+ }
+ IPv6 ipv6 = (IPv6) eth.getPayload();
+
+ if (ipv6.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
+ return null;
+ }
+ ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
+
+ IpAddress sender = Ip6Address.valueOf(ipv6.getSourceAddress());
+ IpAddress target = null;
+
+ MessageType type;
+ if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_SOLICITATION) {
+ type = MessageType.REQUEST;
+ NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload();
+ target = Ip6Address.valueOf(nsol.getTargetAddress());
+ } else if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_ADVERTISEMENT) {
+ type = MessageType.REPLY;
+ } else {
+ return null;
+ }
+
+ return new MessageContext(eth, inPort, Protocol.NDP, type, target, sender);
+ }
+
+ /**
+ * Provides context information for a particular ARP or NDP message, with
+ * a unified interface to access data regardless of protocol.
+ */
+ private class MessageContext {
+ private Protocol protocol;
+ private MessageType type;
+
+ private IpAddress target;
+ private IpAddress sender;
+
+ private Ethernet eth;
+ private ConnectPoint inPort;
+
+
+ public MessageContext(Ethernet eth, ConnectPoint inPort,
+ Protocol protocol, MessageType type,
+ IpAddress target, IpAddress sender) {
+ this.eth = eth;
+ this.inPort = inPort;
+ this.protocol = protocol;
+ this.type = type;
+ this.target = target;
+ this.sender = sender;
+ }
+
+ public ConnectPoint inPort() {
+ return inPort;
+ }
+
+ public Ethernet packet() {
+ return eth;
+ }
+
+ public Protocol protocol() {
+ return protocol;
+ }
+
+ public MessageType type() {
+ return type;
+ }
+
+ public VlanId vlan() {
+ return VlanId.vlanId(eth.getVlanID());
+ }
+
+ public MacAddress srcMac() {
+ return MacAddress.valueOf(eth.getSourceMACAddress());
+ }
+
+ public IpAddress target() {
+ return target;
+ }
+
+ public IpAddress sender() {
+ return sender;
+ }
+ }
}