ONOS-686, 687, 1344 : The first commit for the Segment Routing application
- ICMP/ARP/IP handlers are implemented as a part of the application for now
- Default routing and link add/failure/recovery are also supprted
- Temporary NetworkConfigHandler, which is hardcoded to support only 6 router FISH topology, is used for test
- Some fixes on GroupHanlder app to support transit routers
- Supports multi-instance (tested with two instances)
Change-Id: Idfa67903e59e1c4cac4da430f89cd4c50e821420
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
new file mode 100644
index 0000000..5b94518
--- /dev/null
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2015 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.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+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.HostId;
+import org.onosproject.net.packet.OutboundPacket;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class ArpHandler {
+
+ private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
+
+ private SegmentRoutingManager srManager;
+ private NetworkConfigHandler config;
+
+ /**
+ * Creates an ArpHandler object.
+ *
+ * @param srManager SegmentRoutingManager object
+ */
+ public ArpHandler(SegmentRoutingManager srManager) {
+ this.srManager = srManager;
+ this.config = checkNotNull(srManager.networkConfigHandler);
+ }
+
+ /**
+ * Processes incoming ARP packets.
+ * If it is an ARP request to router itself or known hosts,
+ * then it sends ARP response.
+ * If it is an ARP request to unknown hosts in its own subnet,
+ * then it flood the ARP request to the ports.
+ * If it is an ARP response, then set a flow rule for the host
+ * and forward any IP packets to the host in the packet buffer to the host.
+ *
+ * @param pkt incoming packet
+ */
+ public void processPacketIn(InboundPacket pkt) {
+
+ Ethernet ethernet = pkt.parsed();
+ ARP arp = (ARP) ethernet.getPayload();
+
+ ConnectPoint connectPoint = pkt.receivedFrom();
+ PortNumber inPort = connectPoint.port();
+ DeviceId deviceId = connectPoint.deviceId();
+ byte[] senderMacAddressByte = arp.getSenderHardwareAddress();
+ Ip4Address hostIpAddress = Ip4Address.valueOf(arp.getSenderProtocolAddress());
+
+ srManager.routingRulePopulator.populateIpRuleForHost(deviceId, hostIpAddress, MacAddress.
+ valueOf(senderMacAddressByte), inPort);
+
+ if (arp.getOpCode() == ARP.OP_REQUEST) {
+ handleArpRequest(deviceId, connectPoint, ethernet);
+ } else {
+ srManager.ipHandler.forwardPackets(deviceId, hostIpAddress);
+ }
+ }
+
+ private void handleArpRequest(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
+
+ ARP arpRequest = (ARP) payload.getPayload();
+ HostId targetHostId = HostId.hostId(MacAddress.valueOf(
+ arpRequest.getTargetHardwareAddress()));
+
+ // ARP request for router
+ if (isArpReqForRouter(deviceId, arpRequest)) {
+ Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress());
+ sendArpResponse(arpRequest, config.getRouterMac(targetAddress));
+
+ // ARP request for known hosts
+ } else if (srManager.hostService.getHost(targetHostId) != null) {
+ MacAddress targetMac = srManager.hostService.getHost(targetHostId).mac();
+ sendArpResponse(arpRequest, targetMac);
+
+ // ARP request for unknown host in the subnet
+ } else if (isArpReqForSubnet(deviceId, arpRequest)) {
+ flood(payload, inPort);
+ }
+ }
+
+
+ private boolean isArpReqForRouter(DeviceId deviceId, ARP arpRequest) {
+ Ip4Address gatewayIpAddress = config.getGatewayIpAddress(deviceId);
+ if (gatewayIpAddress != null) {
+ Ip4Address targetProtocolAddress = Ip4Address.valueOf(arpRequest
+ .getTargetProtocolAddress());
+ if (gatewayIpAddress.equals(targetProtocolAddress)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isArpReqForSubnet(DeviceId deviceId, ARP arpRequest) {
+ String subnetInfo = config.getSubnetInfo(deviceId);
+ if (subnetInfo != null) {
+ IpPrefix prefix = IpPrefix.valueOf(subnetInfo);
+ if (prefix.contains(Ip4Address.valueOf(arpRequest.getTargetProtocolAddress()))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Sends an APR request for the target IP address to all ports except in-port.
+ *
+ * @param deviceId Switch device ID
+ * @param targetAddress target IP address for ARP
+ * @param inPort in-port
+ */
+ public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
+
+ byte[] senderMacAddress = config.getRouterMacAddress(deviceId).toBytes();
+ byte[] senderIpAddress = config.getRouterIpAddress(deviceId)
+ .getIp4Prefix().address().toOctets();
+
+ 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(ARP arpRequest, MacAddress targetMac) {
+
+ ARP arpReply = new ARP();
+ arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
+ .setProtocolType(ARP.PROTO_TYPE_IP)
+ .setHardwareAddressLength(
+ (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
+ .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
+ .setOpCode(ARP.OP_REPLY)
+ .setSenderHardwareAddress(targetMac.toBytes())
+ .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
+ .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
+ .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
+
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
+ .setSourceMACAddress(targetMac.toBytes())
+ .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
+
+
+ HostId dstId = HostId.hostId(MacAddress.valueOf(
+ arpReply.getTargetHardwareAddress()));
+ Host dst = srManager.hostService.getHost(dstId);
+ if (dst == null) {
+ log.warn("Cannot send ARP response to unknown device");
+ return;
+ }
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().
+ setOutput(dst.location().port()).build();
+ OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(),
+ treatment, ByteBuffer.wrap(eth.serialize()));
+
+ srManager.packetService.emit(packet);
+ }
+
+ private void flood(Ethernet request, ConnectPoint inPort) {
+ TrafficTreatment.Builder builder;
+ ByteBuffer buf = ByteBuffer.wrap(request.serialize());
+
+ for (Port port: srManager.deviceService.getPorts(inPort.deviceId())) {
+ if (!port.number().equals(inPort.port()) &&
+ port.number().toLong() > 0) {
+ builder = DefaultTrafficTreatment.builder();
+ builder.setOutput(port.number());
+ srManager.packetService.emit(new DefaultOutboundPacket(inPort.deviceId(),
+ builder.build(), buf));
+ }
+ }
+ }
+
+}