[CORD-631] ICMPv6 Echo support
Changes:
- ICMPv6 Echo support;
- Introduces SegmentRoutingNeighbourHandler;
- Simplifies ArpHandler;
- Simplifies IcmpHandler;
Change-Id: I93f04d94ff15f43ca83f96cbab3da5064c215f9c
diff --git a/src/main/java/org/onosproject/segmentrouting/ArpHandler.java b/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
index 675e63d..8ef889f 100644
--- a/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
@@ -27,42 +27,32 @@
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.host.HostService;
-import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.nio.ByteBuffer;
import java.util.Set;
import java.util.stream.Collectors;
-import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.incubator.net.neighbour.NeighbourMessageType.REQUEST;
/**
* Handler of ARP packets that responses or forwards ARP packets that
* are sent to the controller.
*/
-public class ArpHandler {
+public class ArpHandler extends SegmentRoutingNeighbourHandler {
private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
- private SegmentRoutingManager srManager;
- private DeviceConfiguration config;
-
/**
* Creates an ArpHandler object.
*
* @param srManager SegmentRoutingManager object
*/
public ArpHandler(SegmentRoutingManager srManager) {
- this.srManager = srManager;
- this.config = checkNotNull(srManager.deviceConfiguration);
+ super(srManager);
}
/**
@@ -114,7 +104,7 @@
// ARP request for router. Send ARP reply.
if (isArpForRouter(pkt)) {
MacAddress targetMac = config.getRouterMacForAGatewayIp(pkt.target().getIp4Address());
- sendArpResponse(pkt, targetMac, hostService);
+ sendResponse(pkt, targetMac, hostService);
} else {
Set<Host> hosts = hostService.getHostsByIp(pkt.target());
if (hosts.size() > 1) {
@@ -197,128 +187,22 @@
* @param inPort in-port
*/
public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
- byte[] senderMacAddress;
- byte[] senderIpAddress;
-
- try {
- senderMacAddress = config.getDeviceMac(deviceId).toBytes();
- senderIpAddress = config.getRouterIpAddressForASubnetHost(targetAddress.getIp4Address())
- .toOctets();
- } catch (DeviceConfigNotFoundException e) {
- log.warn(e.getMessage() + " Aborting sendArpRequest.");
- return;
- }
-
- ARP arpRequest = new ARP();
- arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
- .setProtocolType(ARP.PROTO_TYPE_IP)
- .setHardwareAddressLength(
- (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
- .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
- .setOpCode(ARP.OP_REQUEST)
- .setSenderHardwareAddress(senderMacAddress)
- .setTargetHardwareAddress(MacAddress.ZERO.toBytes())
- .setSenderProtocolAddress(senderIpAddress)
- .setTargetProtocolAddress(targetAddress.toOctets());
-
- Ethernet eth = new Ethernet();
- eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes())
- .setSourceMACAddress(senderMacAddress)
- .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
-
- flood(eth, inPort);
- }
-
- private void sendArpResponse(NeighbourMessageContext pkt, MacAddress targetMac, HostService hostService) {
- HostId dstId = HostId.hostId(pkt.srcMac(), pkt.vlan());
- Host dst = hostService.getHost(dstId);
- if (dst == null) {
- log.warn("Cannot send ARP response to host {} - does not exist in the store",
- dstId);
- return;
- }
- pkt.reply(targetMac);
- }
-
- /**
- * Remove VLAN tag and flood to all ports in the same subnet.
- *
- * @param packet packet to be flooded
- * @param inPort where the packet comes from
- */
- private void flood(Ethernet packet, ConnectPoint inPort) {
- Ip4Address targetProtocolAddress = Ip4Address.valueOf(
- ((ARP) packet.getPayload()).getTargetProtocolAddress()
+ byte[] senderMacAddress = new byte[MacAddress.MAC_ADDRESS_LENGTH];
+ byte[] senderIpAddress = new byte[Ip4Address.BYTE_LENGTH];
+ /*
+ * Retrieves device info.
+ */
+ getSenderInfo(senderMacAddress, senderIpAddress, deviceId, targetAddress);
+ /*
+ * Creates the request.
+ */
+ Ethernet arpRequest = ARP.buildArpRequest(
+ senderMacAddress,
+ senderIpAddress,
+ targetAddress.toOctets(),
+ VlanId.NO_VID
);
-
- try {
- srManager.deviceConfiguration
- .getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> {
- if (subnet.contains(targetProtocolAddress)) {
- ports.stream()
- .filter(port -> port != inPort.port())
- .forEach(port -> {
- forward(packet, new ConnectPoint(inPort.deviceId(), port));
- });
- }
- });
- } catch (DeviceConfigNotFoundException e) {
- log.warn(e.getMessage()
- + " Cannot flood in subnet as device config not available"
- + " for device: " + inPort.deviceId());
- }
+ flood(arpRequest, inPort, targetAddress);
}
- /**
- * Remove VLAN tag and flood to all ports in the same subnet.
- *
- * @param pkt arp packet to be flooded
- */
- private void flood(NeighbourMessageContext pkt) {
- try {
- srManager.deviceConfiguration
- .getSubnetPortsMap(pkt.inPort().deviceId()).forEach((subnet, ports) -> {
- if (subnet.contains(pkt.target())) {
- ports.stream()
- .filter(port -> port != pkt.inPort().port())
- .forEach(port -> {
- ConnectPoint outPoint = new ConnectPoint(
- pkt.inPort().deviceId(),
- port
- );
- pkt.forward(outPoint);
- });
- }
- });
- } catch (DeviceConfigNotFoundException e) {
- log.warn(e.getMessage()
- + " Cannot flood in subnet as device config not available"
- + " for device: " + pkt.inPort().deviceId());
- }
- }
-
- /**
- * Remove VLAN tag and packet out to given port.
- *
- * Note: In current implementation, we expect all communication with
- * end hosts within a subnet to be untagged.
- * <p>
- * For those pipelines that internally assigns a VLAN, the VLAN tag will be
- * removed before egress.
- * <p>
- * For those pipelines that do not assign internal VLAN, the packet remains
- * untagged.
- *
- * @param packet packet to be forwarded
- * @param outPort where the packet should be forwarded
- */
- private void forward(Ethernet packet, ConnectPoint outPort) {
- packet.setEtherType(Ethernet.TYPE_ARP);
- ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
-
- TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
- tbuilder.setOutput(outPort.port());
- srManager.packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
- tbuilder.build(), buf));
- }
}
diff --git a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
index 9ac2b26..1594364 100644
--- a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
@@ -17,7 +17,9 @@
import org.onlab.packet.Ethernet;
import org.onlab.packet.ICMP;
+import org.onlab.packet.ICMP6;
import org.onlab.packet.IPv4;
+import org.onlab.packet.IPv6;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip6Address;
import org.onlab.packet.IpAddress;
@@ -25,6 +27,7 @@
import org.onlab.packet.MPLS;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
+import org.onlab.packet.ndp.NeighborSolicitation;
import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
import org.onosproject.incubator.net.neighbour.NeighbourMessageType;
import org.onosproject.net.ConnectPoint;
@@ -35,10 +38,8 @@
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.host.HostService;
import org.onosproject.net.packet.DefaultOutboundPacket;
-import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,17 +47,13 @@
import java.nio.ByteBuffer;
import java.util.Set;
-import static com.google.common.base.Preconditions.checkNotNull;
-
/**
* Handler of ICMP packets that responses or forwards ICMP packets that
* are sent to the controller.
*/
-public class IcmpHandler {
+public class IcmpHandler extends SegmentRoutingNeighbourHandler {
private static Logger log = LoggerFactory.getLogger(IcmpHandler.class);
- private SegmentRoutingManager srManager;
- private DeviceConfiguration config;
/**
* Creates an IcmpHandler object.
@@ -64,8 +61,55 @@
* @param srManager SegmentRoutingManager object
*/
public IcmpHandler(SegmentRoutingManager srManager) {
- this.srManager = srManager;
- this.config = checkNotNull(srManager.deviceConfiguration);
+ super(srManager);
+ }
+
+ /**
+ * Utility function to send packet out.
+ *
+ * @param outport the output port
+ * @param payload the packet to send
+ * @param sid the segment id
+ * @param destIpAddress the destination ip address
+ * @param allowedHops the hop limit/ttl
+ */
+ private void sendPacketOut(ConnectPoint outport,
+ Ethernet payload,
+ int sid,
+ IpAddress destIpAddress,
+ byte allowedHops) {
+ int destSid;
+ if (destIpAddress.isIp4()) {
+ destSid = config.getIPv4SegmentId(payload.getDestinationMAC());
+ } else {
+ destSid = config.getIPv6SegmentId(payload.getDestinationMAC());
+ }
+
+ if (sid == -1 || destSid == sid ||
+ config.inSameSubnet(outport.deviceId(), destIpAddress)) {
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().
+ setOutput(outport.port()).build();
+ OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
+ treatment, ByteBuffer.wrap(payload.serialize()));
+ srManager.packetService.emit(packet);
+ } else {
+ log.debug("Send a MPLS packet as a ICMP response");
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(outport.port())
+ .build();
+
+ payload.setEtherType(Ethernet.MPLS_UNICAST);
+ MPLS mplsPkt = new MPLS();
+ mplsPkt.setLabel(sid);
+ mplsPkt.setTtl(allowedHops);
+ mplsPkt.setPayload(payload.getPayload());
+ payload.setPayload(mplsPkt);
+
+ OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
+ treatment, ByteBuffer.wrap(payload.serialize()));
+
+ srManager.packetService.emit(packet);
+ }
}
//////////////////////////////////////
@@ -79,17 +123,13 @@
* If it is an ICMP packet to unknown host in a subnet, then sends an ARP request
* to the subnet.
*
- * @param pkt inbound packet
+ * @param eth inbound ICMP packet
+ * @param inPort the input port
*/
- public void processPacketIn(InboundPacket pkt) {
-
- Ethernet ethernet = pkt.parsed();
- IPv4 ipv4 = (IPv4) ethernet.getPayload();
-
- ConnectPoint connectPoint = pkt.receivedFrom();
- DeviceId deviceId = connectPoint.deviceId();
- Ip4Address destinationAddress =
- Ip4Address.valueOf(ipv4.getDestinationAddress());
+ public void processIcmp(Ethernet eth, ConnectPoint inPort) {
+ DeviceId deviceId = inPort.deviceId();
+ IPv4 ipv4Packet = (IPv4) eth.getPayload();
+ Ip4Address destinationAddress = Ip4Address.valueOf(ipv4Packet.getDestinationAddress());
Set<IpAddress> gatewayIpAddresses = config.getPortIPs(deviceId);
IpAddress routerIp;
try {
@@ -98,14 +138,13 @@
log.warn(e.getMessage() + " Aborting processPacketIn.");
return;
}
- IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
- Ip4Address routerIpAddress = routerIpPrefix.getIp4Prefix().address();
-
// ICMP to the router IP or gateway IP
- if (((ICMP) ipv4.getPayload()).getIcmpType() == ICMP.TYPE_ECHO_REQUEST &&
- (destinationAddress.equals(routerIpAddress) ||
+ if (((ICMP) ipv4Packet.getPayload()).getIcmpType() == ICMP.TYPE_ECHO_REQUEST &&
+ (destinationAddress.equals(routerIp.getIp4Address()) ||
gatewayIpAddresses.contains(destinationAddress))) {
- sendIcmpResponse(ethernet, connectPoint);
+ sendIcmpResponse(eth, inPort);
+ // We remove the packet from the queue
+ srManager.ipHandler.dequeuePacket(ipv4Packet, destinationAddress);
// ICMP for any known host
} else if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) {
@@ -114,12 +153,13 @@
// ICMP for an unknown host in the subnet of the router
} else if (config.inSameSubnet(deviceId, destinationAddress)) {
- srManager.arpHandler.sendArpRequest(deviceId, destinationAddress, connectPoint);
+ srManager.arpHandler.sendArpRequest(deviceId, destinationAddress, inPort);
// ICMP for an unknown host
} else {
log.debug("ICMP request for unknown host {} ", destinationAddress);
- // Do nothing
+ // We remove the packet from the queue
+ srManager.ipHandler.dequeuePacket(ipv4Packet, destinationAddress);
}
}
@@ -134,71 +174,90 @@
private void sendIcmpResponse(Ethernet icmpRequest, ConnectPoint outport) {
// Note: We assume that packets arrive at the edge switches have
// untagged VLAN.
- Ethernet icmpReplyEth = new Ethernet();
-
+ Ethernet icmpReplyEth = ICMP.buildIcmpReply(icmpRequest);
IPv4 icmpRequestIpv4 = (IPv4) icmpRequest.getPayload();
- IPv4 icmpReplyIpv4 = new IPv4();
-
- int destAddress = icmpRequestIpv4.getDestinationAddress();
- icmpReplyIpv4.setDestinationAddress(icmpRequestIpv4.getSourceAddress());
- icmpReplyIpv4.setSourceAddress(destAddress);
- icmpReplyIpv4.setTtl((byte) 64);
- icmpReplyIpv4.setChecksum((short) 0);
-
- ICMP icmpReply = new ICMP();
- icmpReply.setPayload(((ICMP) icmpRequestIpv4.getPayload()).getPayload());
- icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY);
- icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY);
- icmpReply.setChecksum((short) 0);
- icmpReplyIpv4.setPayload(icmpReply);
-
- icmpReplyEth.setPayload(icmpReplyIpv4);
- icmpReplyEth.setEtherType(Ethernet.TYPE_IPV4);
- icmpReplyEth.setDestinationMACAddress(icmpRequest.getSourceMACAddress());
- icmpReplyEth.setSourceMACAddress(icmpRequest.getDestinationMACAddress());
-
- Ip4Address destIpAddress = Ip4Address.valueOf(icmpReplyIpv4.getDestinationAddress());
+ IPv4 icmpReplyIpv4 = (IPv4) icmpReplyEth.getPayload();
+ Ip4Address destIpAddress = Ip4Address.valueOf(icmpRequestIpv4.getSourceAddress());
Ip4Address destRouterAddress = config.getRouterIpAddressForASubnetHost(destIpAddress);
int destSid = config.getIPv4SegmentId(destRouterAddress);
if (destSid < 0) {
- log.warn("Cannot find the Segment ID for {}", destAddress);
+ log.warn("Cannot find the Segment ID for {}", destIpAddress);
return;
}
-
- sendPacketOut(outport, icmpReplyEth, destSid);
-
+ sendPacketOut(outport, icmpReplyEth, destSid, destIpAddress, icmpReplyIpv4.getTtl());
}
- private void sendPacketOut(ConnectPoint outport, Ethernet payload, int destSid) {
+ ///////////////////////////////////////////
+ // ICMPv6 Echo/Reply Protocol //
+ ///////////////////////////////////////////
- IPv4 ipPacket = (IPv4) payload.getPayload();
- Ip4Address destIpAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress());
-
- if (destSid == -1 || config.getIPv4SegmentId(payload.getDestinationMAC()) == destSid ||
- config.inSameSubnet(outport.deviceId(), destIpAddress)) {
- TrafficTreatment treatment = DefaultTrafficTreatment.builder().
- setOutput(outport.port()).build();
- OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
- treatment, ByteBuffer.wrap(payload.serialize()));
- srManager.packetService.emit(packet);
- } else {
- log.debug("Send a MPLS packet as a ICMP response");
- TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .setOutput(outport.port())
- .build();
-
- payload.setEtherType(Ethernet.MPLS_UNICAST);
- MPLS mplsPkt = new MPLS();
- mplsPkt.setLabel(destSid);
- mplsPkt.setTtl(((IPv4) payload.getPayload()).getTtl());
- mplsPkt.setPayload(payload.getPayload());
- payload.setPayload(mplsPkt);
-
- OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
- treatment, ByteBuffer.wrap(payload.serialize()));
-
- srManager.packetService.emit(packet);
+ /**
+ * Process incoming ICMPv6 packet.
+ * If it is an ICMP request to router or known host, then sends an ICMP response.
+ * If it is an ICMP packet to known host and forward the packet to the host.
+ * If it is an ICMP packet to unknown host in a subnet, then sends an ARP request
+ * to the subnet.
+ *
+ * @param eth the incoming ICMPv6 packet
+ * @param inPort the input port
+ */
+ public void processIcmpv6(Ethernet eth, ConnectPoint inPort) {
+ DeviceId deviceId = inPort.deviceId();
+ IPv6 ipv6Packet = (IPv6) eth.getPayload();
+ Ip6Address destinationAddress = Ip6Address.valueOf(ipv6Packet.getDestinationAddress());
+ Set<IpAddress> gatewayIpAddresses = config.getPortIPs(deviceId);
+ IpAddress routerIp;
+ try {
+ routerIp = config.getRouterIpv6(deviceId);
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn(e.getMessage() + " Aborting processPacketIn.");
+ return;
}
+ ICMP6 icmp6 = (ICMP6) ipv6Packet.getPayload();
+ // ICMP to the router IP or gateway IP
+ if (icmp6.getIcmpType() == ICMP6.ECHO_REQUEST &&
+ (destinationAddress.equals(routerIp.getIp6Address()) ||
+ gatewayIpAddresses.contains(destinationAddress))) {
+ sendIcmpv6Response(eth, inPort);
+ // We remove the packet from the queue
+ srManager.ipHandler.dequeuePacket(ipv6Packet, destinationAddress);
+ // ICMP for any known host
+ } else if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) {
+ // TODO: known host packet should not be coming to controller - resend flows?
+ srManager.ipHandler.forwardPackets(deviceId, destinationAddress);
+ // ICMP for an unknown host in the subnet of the router
+ } else if (config.inSameSubnet(deviceId, destinationAddress)) {
+ sendNdpRequest(deviceId, destinationAddress, inPort);
+ // ICMP for an unknown host or not configured host
+ } else {
+ log.debug("ICMPv6 request for unknown host or not configured host {} ", destinationAddress);
+ // We remove the packet from the queue
+ srManager.ipHandler.dequeuePacket(ipv6Packet, destinationAddress);
+ }
+ }
+
+ /**
+ * Sends an ICMPv6 reply message.
+ *
+ * Note: we assume that packets sending from the edge switches to the hosts
+ * have untagged VLAN.
+ * @param ethRequest the original ICMP request
+ * @param outport the output port where the ICMP reply should be sent to
+ */
+ private void sendIcmpv6Response(Ethernet ethRequest, ConnectPoint outport) {
+ // Note: We assume that packets arrive at the edge switches have
+ // untagged VLAN.
+ Ethernet ethReply = ICMP6.buildIcmp6Reply(ethRequest);
+ IPv6 icmpRequestIpv6 = (IPv6) ethRequest.getPayload();
+ IPv6 icmpReplyIpv6 = (IPv6) ethRequest.getPayload();
+ Ip6Address destIpAddress = Ip6Address.valueOf(icmpRequestIpv6.getSourceAddress());
+ Ip6Address destRouterAddress = config.getRouterIpAddressForASubnetHost(destIpAddress);
+ int sid = config.getIPv6SegmentId(destRouterAddress);
+ if (sid < 0) {
+ log.warn("Cannot find the Segment ID for {}", destIpAddress);
+ return;
+ }
+ sendPacketOut(outport, ethReply, sid, destIpAddress, icmpReplyIpv6.getHopLimit());
}
///////////////////////////////////////////
@@ -273,7 +332,7 @@
*/
if (isNdpForGateway(pkt)) {
log.debug("Sending NDP reply on behalf of the router");
- sendNdpReply(pkt, config.getRouterMacForAGatewayIp(pkt.target()));
+ sendResponse(pkt, config.getRouterMacForAGatewayIp(pkt.target()), hostService);
} else {
/*
* ND request for an host. We do a search by Ip.
@@ -360,49 +419,37 @@
}
/**
- * Utility to send a ND reply using the supplied information.
+ * Sends a NDP request for the target IP address to all ports except in-port.
*
- * @param pkt the ndp request
- * @param targetMac the target mac
+ * @param deviceId Switch device ID
+ * @param targetAddress target IP address for ARP
+ * @param inPort in-port
*/
- private void sendNdpReply(NeighbourMessageContext pkt, MacAddress targetMac) {
- HostId dstId = HostId.hostId(pkt.srcMac(), pkt.vlan());
- Host dst = srManager.hostService.getHost(dstId);
- if (dst == null) {
- log.warn("Cannot send NDP response to host {} - does not exist in the store",
- dstId);
- return;
- }
- pkt.reply(targetMac);
+ public void sendNdpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
+ byte[] senderMacAddress = new byte[MacAddress.MAC_ADDRESS_LENGTH];
+ byte[] senderIpAddress = new byte[Ip6Address.BYTE_LENGTH];
+ /*
+ * Retrieves device info.
+ */
+ getSenderInfo(senderMacAddress, senderIpAddress, deviceId, targetAddress);
+ /*
+ * We have to compute the dst mac address and dst
+ * ip address.
+ */
+ byte[] dstIp = IPv6.getSolicitNodeAddress(targetAddress.toOctets());
+ byte[] dstMac = IPv6.getMCastMacAddress(dstIp);
+ /*
+ * Creates the request.
+ */
+ Ethernet ndpRequest = NeighborSolicitation.buildNdpSolicit(
+ targetAddress.toOctets(),
+ senderIpAddress,
+ dstIp,
+ senderMacAddress,
+ dstMac,
+ VlanId.NONE
+ );
+ flood(ndpRequest, inPort, targetAddress);
}
- /*
- * Floods only on the port which have been configured with the subnet
- * of the target address. The in port is excluded.
- *
- * @param pkt the ndp packet and context information
- */
- private void flood(NeighbourMessageContext pkt) {
- try {
- srManager.deviceConfiguration
- .getSubnetPortsMap(pkt.inPort().deviceId())
- .forEach((subnet, ports) -> {
- if (subnet.contains(pkt.target())) {
- ports.stream()
- .filter(portNumber -> portNumber != pkt.inPort().port())
- .forEach(portNumber -> {
- ConnectPoint outPoint = new ConnectPoint(
- pkt.inPort().deviceId(),
- portNumber
- );
- pkt.forward(outPoint);
- });
- }
- });
- } catch (DeviceConfigNotFoundException e) {
- log.warn(e.getMessage()
- + " Cannot flood in subnet as device config not available"
- + " for device: " + pkt.inPort().deviceId());
- }
- }
}
diff --git a/src/main/java/org/onosproject/segmentrouting/IpHandler.java b/src/main/java/org/onosproject/segmentrouting/IpHandler.java
index b8e16a8..235c82e 100644
--- a/src/main/java/org/onosproject/segmentrouting/IpHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/IpHandler.java
@@ -16,16 +16,18 @@
package org.onosproject.segmentrouting;
import org.onlab.packet.Ethernet;
+import org.onlab.packet.IP;
import org.onlab.packet.IPv4;
+import org.onlab.packet.IPv6;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.packet.DefaultOutboundPacket;
-import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.DeviceConfiguration;
@@ -37,6 +39,7 @@
import java.util.concurrent.ConcurrentLinkedQueue;
import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.packet.IpAddress.Version.INET6;
/**
* Handler of IP packets that forwards IP packets that are sent to the controller,
@@ -47,7 +50,7 @@
private static Logger log = LoggerFactory.getLogger(IpHandler.class);
private SegmentRoutingManager srManager;
private DeviceConfiguration config;
- private ConcurrentHashMap<Ip4Address, ConcurrentLinkedQueue<IPv4>> ipPacketQueue;
+ private ConcurrentHashMap<IpAddress, ConcurrentLinkedQueue<IP>> ipPacketQueue;
/**
* Creates an IpHandler object.
@@ -61,6 +64,53 @@
}
/**
+ * Enqueues the packet using the destination address as key.
+ *
+ * @param ipPacket the ip packet to store
+ * @param destinationAddress the destination address
+ */
+ private void enqueuePacket(IP ipPacket, IpAddress destinationAddress) {
+
+ ipPacketQueue
+ .computeIfAbsent(destinationAddress, a -> new ConcurrentLinkedQueue<>())
+ .add(ipPacket);
+
+ }
+
+ /**
+ * Dequeues the packet using the destination address as key.
+ *
+ * @param ipPacket the ip packet to remove
+ * @param destinationAddress the destination address
+ */
+ public void dequeuePacket(IP ipPacket, IpAddress destinationAddress) {
+
+ if (ipPacketQueue.get(destinationAddress) == null) {
+ return;
+ }
+ ipPacketQueue.get(destinationAddress).remove(ipPacket);
+ }
+
+ /**
+ * Forwards the packet to a given host and deque the packet.
+ *
+ * @param deviceId the target device
+ * @param eth the packet to send
+ * @param dest the target host
+ */
+ private void forwardToHost(DeviceId deviceId, Ethernet eth, Host dest) {
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().
+ setOutput(dest.location().port()).build();
+ OutboundPacket packet = new DefaultOutboundPacket(deviceId,
+ treatment, ByteBuffer.wrap(eth.serialize()));
+ srManager.packetService.emit(packet);
+ }
+
+ //////////////////////
+ // IPv4 Handling //
+ ////////////////////
+
+ /**
* Processes incoming IP packets.
*
* If it is an IP packet for known host, then forward it to the host.
@@ -68,27 +118,24 @@
* to the subnet.
*
* @param pkt incoming packet
+ * @param connectPoint the target device
*/
- public void processPacketIn(InboundPacket pkt) {
- Ethernet ethernet = pkt.parsed();
- IPv4 ipv4 = (IPv4) ethernet.getPayload();
+ public void processPacketIn(IPv4 pkt, ConnectPoint connectPoint) {
- ConnectPoint connectPoint = pkt.receivedFrom();
DeviceId deviceId = connectPoint.deviceId();
- Ip4Address destinationAddress =
- Ip4Address.valueOf(ipv4.getDestinationAddress());
+ Ip4Address destinationAddress = Ip4Address.valueOf(pkt.getDestinationAddress());
// IP packet for know hosts
if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) {
forwardPackets(deviceId, destinationAddress);
- // IP packet for unknown host in the subnet of the router
+ // IP packet for unknown host in one of the configured subnets of the router
} else if (config.inSameSubnet(deviceId, destinationAddress)) {
srManager.arpHandler.sendArpRequest(deviceId, destinationAddress, connectPoint);
// IP packets for unknown host
} else {
- log.debug("IP request for unknown host {} which is not in the subnet",
+ log.debug("IPv4 packet for unknown host {} which is not in the subnet",
destinationAddress);
// Do nothing
}
@@ -107,12 +154,8 @@
if (ipPacket.getProtocol() == IPv4.PROTOCOL_TCP) {
return;
}
-
- Ip4Address destIpAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress());
-
- ipPacketQueue
- .computeIfAbsent(destIpAddress, a -> new ConcurrentLinkedQueue<>())
- .add(ipPacket);
+ IpAddress destIpAddress = IpAddress.valueOf(ipPacket.getDestinationAddress());
+ enqueuePacket(ipPacket, destIpAddress);
}
/**
@@ -127,33 +170,30 @@
if (ipPacketQueue.get(destIpAddress) == null) {
return;
}
-
- for (IPv4 ipPacket : ipPacketQueue.get(destIpAddress)) {
- Ip4Address destAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress());
- if (config.inSameSubnet(deviceId, destAddress)) {
- ipPacket.setTtl((byte) (ipPacket.getTtl() - 1));
- ipPacket.setChecksum((short) 0);
- for (Host dest: srManager.hostService.getHostsByIp(destIpAddress)) {
- Ethernet eth = new Ethernet();
- eth.setDestinationMACAddress(dest.mac());
- try {
- eth.setSourceMACAddress(config.getDeviceMac(deviceId));
- } catch (DeviceConfigNotFoundException e) {
- log.warn(e.getMessage()
- + " Skipping forwardPackets for this destination.");
- continue;
+ for (IP ipPacket : ipPacketQueue.get(destIpAddress)) {
+ if (ipPacket.getVersion() == ((byte) 4)) {
+ IPv4 ipv4Packet = (IPv4) ipPacket;
+ Ip4Address destAddress = Ip4Address.valueOf(ipv4Packet.getDestinationAddress());
+ if (config.inSameSubnet(deviceId, destAddress)) {
+ ipv4Packet.setTtl((byte) (ipv4Packet.getTtl() - 1));
+ ipv4Packet.setChecksum((short) 0);
+ for (Host dest : srManager.hostService.getHostsByIp(destIpAddress)) {
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(dest.mac());
+ try {
+ eth.setSourceMACAddress(config.getDeviceMac(deviceId));
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn(e.getMessage()
+ + " Skipping forwardPackets for this destination.");
+ continue;
+ }
+ eth.setEtherType(Ethernet.TYPE_IPV4);
+ eth.setPayload(ipv4Packet);
+ forwardToHost(deviceId, eth, dest);
+ ipPacketQueue.get(destIpAddress).remove(ipPacket);
}
- eth.setEtherType(Ethernet.TYPE_IPV4);
- eth.setPayload(ipPacket);
-
- TrafficTreatment treatment = DefaultTrafficTreatment.builder().
- setOutput(dest.location().port()).build();
- OutboundPacket packet = new DefaultOutboundPacket(deviceId,
- treatment, ByteBuffer.wrap(eth.serialize()));
- srManager.packetService.emit(packet);
ipPacketQueue.get(destIpAddress).remove(ipPacket);
}
- ipPacketQueue.get(destIpAddress).remove(ipPacket);
}
}
}
@@ -163,6 +203,53 @@
////////////////////
/**
+ * Processes incoming IPv6 packets.
+ *
+ * If it is an IPv6 packet for known host, then forward it to the host.
+ * If it is an IPv6 packet for unknown host in subnet, then send an NDP request
+ * to the subnet.
+ *
+ * @param pkt incoming packet
+ * @param connectPoint the target device
+ */
+ public void processPacketIn(IPv6 pkt, ConnectPoint connectPoint) {
+
+ DeviceId deviceId = connectPoint.deviceId();
+ Ip6Address destinationAddress = Ip6Address.valueOf(pkt.getDestinationAddress());
+
+ // IPv6 packet for know hosts
+ if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) {
+ forwardPackets(deviceId, destinationAddress);
+
+ // IPv6 packet for unknown host in one of the configured subnets of the router
+ } else if (config.inSameSubnet(deviceId, destinationAddress)) {
+ srManager.icmpHandler.sendNdpRequest(deviceId, destinationAddress, connectPoint);
+
+ // IPv6 packets for unknown host
+ } else {
+ log.debug("IPv6 packet for unknown host {} which is not in the subnet",
+ destinationAddress);
+ }
+ }
+
+ /**
+ * Adds the IPv6 packet to a buffer.
+ * The packets are forwarded to corresponding destination when the destination
+ * MAC address is known via NDP response.
+ *
+ * @param ipPacket IP packet to add to the buffer
+ */
+ public void addToPacketBuffer(IPv6 ipPacket) {
+
+ // Better not buffer TCP packets due to out-of-order packet transfer
+ if (ipPacket.getNextHeader() == IPv6.PROTOCOL_TCP) {
+ return;
+ }
+ IpAddress destIpAddress = IpAddress.valueOf(INET6, ipPacket.getDestinationAddress());
+ enqueuePacket(ipPacket, destIpAddress);
+ }
+
+ /**
* Forwards IP packets in the buffer to the destination IP address.
* It is called when the controller finds the destination MAC address
* via NDP replies.
@@ -171,9 +258,35 @@
* @param destIpAddress the destination ip address
*/
public void forwardPackets(DeviceId deviceId, Ip6Address destIpAddress) {
- /*
- * TODO in the following commit.
- */
+ if (ipPacketQueue.get(destIpAddress) == null) {
+ return;
+ }
+ for (IP ipPacket : ipPacketQueue.get(destIpAddress)) {
+ if (ipPacket.getVersion() == ((byte) 6)) {
+ IPv6 ipv6Packet = (IPv6) ipPacket;
+ Ip6Address destAddress = Ip6Address.valueOf(ipv6Packet.getDestinationAddress());
+ if (config.inSameSubnet(deviceId, destAddress)) {
+ ipv6Packet.setHopLimit((byte) (ipv6Packet.getHopLimit() - 1));
+ for (Host dest : srManager.hostService.getHostsByIp(destIpAddress)) {
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(dest.mac());
+ try {
+ eth.setSourceMACAddress(config.getDeviceMac(deviceId));
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn(e.getMessage()
+ + " Skipping forwardPackets for this destination.");
+ continue;
+ }
+ eth.setEtherType(Ethernet.TYPE_IPV6);
+ eth.setPayload(ipv6Packet);
+ forwardToHost(deviceId, eth, dest);
+ ipPacketQueue.get(destIpAddress).remove(ipPacket);
+ }
+ ipPacketQueue.get(destIpAddress).remove(ipPacket);
+ }
+ }
+ ipPacketQueue.get(destIpAddress).remove(ipPacket);
+ }
}
}
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 757e408..1ea185e 100644
--- a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -602,6 +602,12 @@
? VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET)
: srManager.getSubnetAssignedVlanId(deviceId, portSubnet);
+ if (assignedVlan == null) {
+ log.warn("Assigned vlan is null for {} in {} - Aborting populateRouterMacVlanFilters.",
+ port.number(), deviceId);
+ return null;
+ }
+
FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
fob.withKey(Criteria.matchInPort(port.number()))
.addCondition(Criteria.matchEthDst(deviceMac))
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 1b8887b..6694aea 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -23,6 +23,7 @@
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP6;
import org.onlab.packet.IPv4;
import org.onlab.packet.IPv6;
import org.onlab.packet.Ip4Prefix;
@@ -35,10 +36,10 @@
import org.onosproject.event.Event;
import org.onosproject.incubator.net.config.basics.McastConfig;
import org.onosproject.incubator.net.intf.InterfaceService;
-import org.onosproject.incubator.net.neighbour.NeighbourResolutionService;
import org.onosproject.incubator.net.routing.RouteEvent;
import org.onosproject.incubator.net.routing.RouteListener;
import org.onosproject.incubator.net.routing.RouteService;
+import org.onosproject.incubator.net.neighbour.NeighbourResolutionService;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
@@ -187,7 +188,7 @@
HostHandler hostHandler = null;
private CordConfigHandler cordConfigHandler = null;
private RouteHandler routeHandler = null;
- private SegmentRoutingNeighbourHandler neighbourHandler = null;
+ private SegmentRoutingNeighbourDispatcher neighbourHandler = null;
private InternalEventHandler eventHandler = new InternalEventHandler();
private final InternalHostListener hostListener = new InternalHostListener();
private final InternalConfigListener cfgListener = new InternalConfigListener(this);
@@ -360,7 +361,7 @@
hostHandler = new HostHandler(this);
cordConfigHandler = new CordConfigHandler(this);
routeHandler = new RouteHandler(this);
- neighbourHandler = new SegmentRoutingNeighbourHandler(this);
+ neighbourHandler = new SegmentRoutingNeighbourDispatcher(this);
cfgService.addListener(cfgListener);
cfgService.registerConfigFactory(deviceConfigFactory);
@@ -656,10 +657,10 @@
context.inPacket().receivedFrom());
log.debug("{}", ethernet);
} else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
- IPv4 ipPacket = (IPv4) ethernet.getPayload();
- // ipHandler.addToPacketBuffer(ipPacket);
- if (ipPacket.getProtocol() == IPv4.PROTOCOL_ICMP) {
- icmpHandler.processPacketIn(pkt);
+ IPv4 ipv4Packet = (IPv4) ethernet.getPayload();
+ //ipHandler.addToPacketBuffer(ipv4Packet);
+ if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_ICMP) {
+ icmpHandler.processIcmp(ethernet, pkt.receivedFrom());
} else {
// NOTE: We don't support IP learning at this moment so this
// is not necessary. Also it causes duplication of DHCP packets.
@@ -667,9 +668,24 @@
}
} else if (ethernet.getEtherType() == Ethernet.TYPE_IPV6) {
IPv6 ipv6Packet = (IPv6) ethernet.getPayload();
+ //ipHandler.addToPacketBuffer(ipv6Packet);
/*
- * TODO send to ICMPv6 handler and generalize the interaction with IP Handler
+ * We deal with the packet only if the packet is a ICMP6 ECHO/REPLY
*/
+ if (ipv6Packet.getNextHeader() == IPv6.PROTOCOL_ICMP6) {
+ ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
+ if (icmp6Packet.getIcmpType() == ICMP6.ECHO_REQUEST ||
+ icmp6Packet.getIcmpType() == ICMP6.ECHO_REPLY) {
+ icmpHandler.processIcmpv6(ethernet, pkt.receivedFrom());
+ } else {
+ log.warn("Received ICMPv6 0x{} - not handled",
+ Integer.toHexString(icmp6Packet.getIcmpType() & 0xff));
+ }
+ } else {
+ // NOTE: We don't support IP learning at this moment so this
+ // is not necessary. Also it causes duplication of DHCPv6 packets.
+ // ipHandler.processPacketIn(ipv6Packet, pkt.receivedFrom());
+ }
}
}
}
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourDispatcher.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourDispatcher.java
new file mode 100644
index 0000000..4eaac4e
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourDispatcher.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting;
+
+import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
+import org.onosproject.incubator.net.neighbour.NeighbourMessageHandler;
+import org.onosproject.net.host.HostService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This handler dispatches to the appropriate handlers the
+ * neighbour discovery protocols.
+ */
+public class SegmentRoutingNeighbourDispatcher implements NeighbourMessageHandler {
+
+ private static Logger log = LoggerFactory.getLogger(SegmentRoutingNeighbourDispatcher.class);
+ private SegmentRoutingManager manager;
+
+ /**
+ * Create a segment routing neighbour dispatcher.
+ *
+ * @param segmentRoutingManager the segment routing manager
+ */
+ public SegmentRoutingNeighbourDispatcher(SegmentRoutingManager segmentRoutingManager) {
+ this.manager = segmentRoutingManager;
+ }
+
+ @Override
+ public void handleMessage(NeighbourMessageContext context, HostService hostService) {
+ log.debug("Received a {} packet {}", context.protocol(), context.packet());
+ switch (context.protocol()) {
+ case ARP:
+ if (this.manager.arpHandler != null) {
+ this.manager.arpHandler.processPacketIn(context, hostService);
+ }
+ break;
+ case NDP:
+ if (this.manager.icmpHandler != null) {
+ this.manager.icmpHandler.processPacketIn(context, hostService);
+ }
+ break;
+ default:
+ log.warn("Unknown protocol", context.protocol());
+ }
+ }
+
+
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourHandler.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourHandler.java
index 5ea5694..ae32bfc 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourHandler.java
@@ -16,51 +16,180 @@
package org.onosproject.segmentrouting;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
-import org.onosproject.incubator.net.neighbour.NeighbourMessageHandler;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.host.HostService;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
+import org.onosproject.segmentrouting.config.DeviceConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.nio.ByteBuffer;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
- * This handler deals with the ICMPv6 Echo protocol
- * and ICMPv6 ND protocol.
+ * This handler provides provides useful functions to the
+ * neighbour handlers (ARP, NDP).
*/
-public class SegmentRoutingNeighbourHandler implements NeighbourMessageHandler {
+public class SegmentRoutingNeighbourHandler {
private static Logger log = LoggerFactory.getLogger(SegmentRoutingNeighbourHandler.class);
- private SegmentRoutingManager manager;
+
+ protected SegmentRoutingManager srManager;
+ protected DeviceConfiguration config;
/**
- * Create a segment routing neighbour handler.
+ * Creates an SegmentRoutingNeighbourHandler object.
*
- * @param segmentRoutingManager the segment routing manager
+ * @param srManager SegmentRoutingManager object
*/
- public SegmentRoutingNeighbourHandler(SegmentRoutingManager segmentRoutingManager) {
- this.manager = segmentRoutingManager;
+ public SegmentRoutingNeighbourHandler(SegmentRoutingManager srManager) {
+ this.srManager = srManager;
+ this.config = checkNotNull(srManager.deviceConfiguration);
}
- /*
- * We use this method to handle the NDP messages
+ /**
+ * Creates an SegmentRoutingNeighbourHandler object.
*/
- @Override
- public void handleMessage(NeighbourMessageContext context, HostService hostService) {
- log.debug("Received a {} packet {}", context.protocol(), context.packet());
- switch (context.protocol()) {
- case ARP:
- if (this.manager.arpHandler != null) {
- this.manager.arpHandler.processPacketIn(context, hostService);
+ public SegmentRoutingNeighbourHandler() {
+ this.srManager = null;
+ this.config = null;
+ }
+
+ /**
+ * Retrieve router (device) info.
+ *
+ * @param mac where to copy the mac
+ * @param ip where to copy the ip
+ * @param deviceId the device id
+ * @param targetAddress the target address
+ */
+ protected void getSenderInfo(byte[] mac,
+ byte[] ip,
+ DeviceId deviceId,
+ IpAddress targetAddress) {
+ byte[] senderMacAddress;
+ byte[] senderIpAddress;
+ try {
+ senderMacAddress = config.getDeviceMac(deviceId).toBytes();
+ senderIpAddress = config.getRouterIpAddressForASubnetHost(targetAddress.getIp4Address())
+ .toOctets();
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn(e.getMessage() + " Aborting sendArpRequest.");
+ return;
+ }
+ /*
+ * FIXME understand how to manage IPv4/IPv6 together.
+ */
+ System.arraycopy(senderMacAddress, 0, mac, 0, senderMacAddress.length);
+ System.arraycopy(senderIpAddress, 0, ip, 0, senderIpAddress.length);
+ }
+
+ /**
+ * Utility to send a ND reply using the supplied information.
+ *
+ * @param pkt the request
+ * @param targetMac the target mac
+ * @param hostService the host service
+ */
+ protected void sendResponse(NeighbourMessageContext pkt, MacAddress targetMac, HostService hostService) {
+ HostId dstId = HostId.hostId(pkt.srcMac(), pkt.vlan());
+ Host dst = hostService.getHost(dstId);
+ if (dst == null) {
+ log.warn("Cannot send {} response to host {} - does not exist in the store",
+ pkt.protocol(), dstId);
+ return;
+ }
+ pkt.reply(targetMac);
+ }
+
+ /**
+ * Flood to all ports in the same subnet.
+ *
+ * @param packet packet to be flooded
+ * @param inPort where the packet comes from
+ * @param targetAddress the target address
+ */
+ protected void flood(Ethernet packet, ConnectPoint inPort, IpAddress targetAddress) {
+ try {
+ srManager.deviceConfiguration
+ .getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> {
+ if (subnet.contains(targetAddress)) {
+ ports.stream()
+ .filter(port -> port != inPort.port())
+ .forEach(port -> {
+ forward(packet, new ConnectPoint(inPort.deviceId(), port));
+ });
}
- break;
- case NDP:
- if (this.manager.icmpHandler != null) {
- this.manager.icmpHandler.processPacketIn(context, hostService);
- }
- break;
- default:
- log.warn("Unknown protocol", context.protocol());
+ });
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn(e.getMessage()
+ + " Cannot flood in subnet as device config not available"
+ + " for device: " + inPort.deviceId());
}
}
+ /*
+ * Floods only on the port which have been configured with the subnet
+ * of the target address. The in port is excluded.
+ *
+ * @param pkt the ndp/arp packet and context information
+ */
+ protected void flood(NeighbourMessageContext pkt) {
+ try {
+ srManager.deviceConfiguration
+ .getSubnetPortsMap(pkt.inPort().deviceId()).forEach((subnet, ports) -> {
+ if (subnet.contains(pkt.target())) {
+ ports.stream()
+ .filter(port -> port != pkt.inPort().port())
+ .forEach(port -> {
+ ConnectPoint outPoint = new ConnectPoint(
+ pkt.inPort().deviceId(),
+ port
+ );
+ pkt.forward(outPoint);
+ });
+ }
+ });
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn(e.getMessage()
+ + " Cannot flood in subnet as device config not available"
+ + " for device: " + pkt.inPort().deviceId());
+ }
+ }
+
+ /**
+ * Packet out to given port.
+ *
+ * Note: In current implementation, we expect all communication with
+ * end hosts within a subnet to be untagged.
+ * <p>
+ * For those pipelines that internally assigns a VLAN, the VLAN tag will be
+ * removed before egress.
+ * <p>
+ * For those pipelines that do not assign internal VLAN, the packet remains
+ * untagged.
+ *
+ * @param packet packet to be forwarded
+ * @param outPort where the packet should be forwarded
+ */
+ private void forward(Ethernet packet, ConnectPoint outPort) {
+ ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
+
+ TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+ tbuilder.setOutput(outPort.port());
+ srManager.packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
+ tbuilder.build(), buf));
+ }
}