From c9e16bfa1d3d95b1fb704c99f82525cf2cb1fee8 Mon Sep 17 00:00:00 2001 From: Pingping Lin Date: Fri, 10 Apr 2015 14:42:41 -0700 Subject: [PATCH] add a virtual gateway for reactive routing There is no physical gateway in SDN network. However a host needs a gateway when it tries to communicate with a remote host. So we designed a virtual gateway for SDN network. The virtual gateway can have multiple IP addresses. Each IP address is used as the default gateway address of an IP prefix. We only configure one MAC address to the virtual gateway. You can choose any MAC address from the BGP speakers as the virtual gateway MAC address. We configure this MAC address staticly in the sdnip.json configuration file. Change-Id: I2a72bef797fc55d25bb5473e8fca624ad659e1d1 --- apps/reactive-routing/pom.xml | 5 ++ .../routing/SdnIpReactiveRouting.java | 82 +++++++++++++------ .../routing/config/LocalIpPrefixEntry.java | 16 +++- .../config/RoutingConfigurationService.java | 16 ++++ .../routing/config/impl/Configuration.java | 24 +++++- .../config/impl/RoutingConfigurationImpl.java | 18 ++++ .../main/resources/config-examples/sdnip.json | 12 ++- .../net/proxyarp/ProxyArpService.java | 1 - .../net/proxyarp/impl/ProxyArpManager.java | 40 +-------- .../src/main/java/org/onlab/packet/ARP.java | 35 ++++++++ 10 files changed, 181 insertions(+), 68 deletions(-) diff --git a/apps/reactive-routing/pom.xml b/apps/reactive-routing/pom.xml index d7bde79ce0..a5059cab59 100644 --- a/apps/reactive-routing/pom.xml +++ b/apps/reactive-routing/pom.xml @@ -41,6 +41,11 @@ onos-app-routing ${project.version} + + + org.onosproject + onos-api + \ No newline at end of file diff --git a/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java b/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java index a74387fa4e..46f7fd9889 100644 --- a/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java +++ b/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java @@ -16,13 +16,17 @@ package org.onosproject.reactive.routing; import static org.slf4j.LoggerFactory.getLogger; +import java.nio.ByteBuffer; + import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onlab.packet.ARP; import org.onlab.packet.Ethernet; import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Address; import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; import org.onosproject.core.ApplicationId; @@ -40,6 +44,7 @@ import org.onosproject.net.packet.PacketPriority; import org.onosproject.net.packet.PacketProcessor; import org.onosproject.net.packet.PacketService; import org.onosproject.routing.RoutingService; +import org.onosproject.routing.config.RoutingConfigurationService; import org.slf4j.Logger; /** @@ -64,6 +69,9 @@ public class SdnIpReactiveRouting { @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected RoutingService routingService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected RoutingConfigurationService config; + private ApplicationId appId; private ReactiveRoutingProcessor processor = @@ -78,6 +86,9 @@ public class SdnIpReactiveRouting { TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); // TODO: to support IPv6 later selector.matchEthType(Ethernet.TYPE_IPV4); + packetService.requestPackets(selector.build(), + PacketPriority.REACTIVE, appId); + selector.matchEthType(Ethernet.TYPE_ARP); packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId); @@ -100,31 +111,56 @@ public class SdnIpReactiveRouting { if (ethPkt == null) { return; } - - // In theory, we do not need to check whether it is Ethernet - // TYPE_IPV4. However, due to the current implementation of the - // packetService, we will receive all packets from all subscribers. - // Hence, we have to check the Ethernet type again here. - if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) { - return; - } - - // Parse packet - IPv4 ipv4Packet = (IPv4) ethPkt.getPayload(); - IpAddress dstIp = - IpAddress.valueOf(ipv4Packet.getDestinationAddress()); - IpAddress srcIp = - IpAddress.valueOf(ipv4Packet.getSourceAddress()); ConnectPoint srcConnectPoint = pkt.receivedFrom(); - MacAddress srcMac = ethPkt.getSourceMAC(); - routingService.packetReactiveProcessor(dstIp, srcIp, - srcConnectPoint, srcMac); - // TODO emit packet first or packetReactiveProcessor first - ConnectPoint egressConnectPoint = null; - egressConnectPoint = routingService.getEgressConnectPoint(dstIp); - if (egressConnectPoint != null) { - forwardPacketToDst(context, egressConnectPoint); + switch (ethPkt.getEtherType()) { + case Ethernet.TYPE_ARP: + ARP arpPacket = (ARP) ethPkt.getPayload(); + Ip4Address targetIpAddress = Ip4Address + .valueOf(arpPacket.getTargetProtocolAddress()); + // Only when it is an ARP request packet and the target IP + // address is a virtual gateway IP address, then it will be + // processed. + if (arpPacket.getOpCode() == ARP.OP_REQUEST + && config.isVirtualGatewayIpAddress(targetIpAddress)) { + MacAddress gatewayMacAddress = + config.getVirtualGatewayMacAddress(); + if (gatewayMacAddress == null) { + break; + } + Ethernet eth = ARP.buildArpReply(targetIpAddress, + gatewayMacAddress, + ethPkt); + + TrafficTreatment.Builder builder = + DefaultTrafficTreatment.builder(); + builder.setOutput(srcConnectPoint.port()); + packetService.emit(new DefaultOutboundPacket( + srcConnectPoint.deviceId(), + builder.build(), + ByteBuffer.wrap(eth.serialize()))); + } + break; + case Ethernet.TYPE_IPV4: + // Parse packet + IPv4 ipv4Packet = (IPv4) ethPkt.getPayload(); + IpAddress dstIp = + IpAddress.valueOf(ipv4Packet.getDestinationAddress()); + IpAddress srcIp = + IpAddress.valueOf(ipv4Packet.getSourceAddress()); + MacAddress srcMac = ethPkt.getSourceMAC(); + routingService.packetReactiveProcessor(dstIp, srcIp, + srcConnectPoint, srcMac); + + // TODO emit packet first or packetReactiveProcessor first + ConnectPoint egressConnectPoint = null; + egressConnectPoint = routingService.getEgressConnectPoint(dstIp); + if (egressConnectPoint != null) { + forwardPacketToDst(context, egressConnectPoint); + } + break; + default: + break; } } } diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/config/LocalIpPrefixEntry.java b/apps/routing-api/src/main/java/org/onosproject/routing/config/LocalIpPrefixEntry.java index a2f3c8c180..3f62db4f59 100644 --- a/apps/routing-api/src/main/java/org/onosproject/routing/config/LocalIpPrefixEntry.java +++ b/apps/routing-api/src/main/java/org/onosproject/routing/config/LocalIpPrefixEntry.java @@ -20,6 +20,7 @@ import com.google.common.base.MoreObjects; import java.util.Objects; +import org.onlab.packet.IpAddress; import org.onlab.packet.IpPrefix; /** @@ -28,6 +29,7 @@ import org.onlab.packet.IpPrefix; public class LocalIpPrefixEntry { private final IpPrefix ipPrefix; private final IpPrefixType type; + private final IpAddress gatewayIpAddress; /** * Specifies the type of local IP prefix. @@ -55,9 +57,12 @@ public class LocalIpPrefixEntry { * @param type an IP prefix type as an IpPrefixType */ public LocalIpPrefixEntry(@JsonProperty("ipPrefix") String ipPrefix, - @JsonProperty("type") IpPrefixType type) { + @JsonProperty("type") IpPrefixType type, + @JsonProperty("gatewayIp") IpAddress + gatewayIpAddress) { this.ipPrefix = IpPrefix.valueOf(ipPrefix); this.type = type; + this.gatewayIpAddress = gatewayIpAddress; } /** @@ -78,6 +83,15 @@ public class LocalIpPrefixEntry { return type; } + /** + * Gets the gateway IP address of the IP prefix entry. + * + * @return the gateway IP address + */ + public IpAddress getGatewayIpAddress() { + return gatewayIpAddress; + } + /** * Tests whether the IP version of this entry is IPv4. * diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/config/RoutingConfigurationService.java b/apps/routing-api/src/main/java/org/onosproject/routing/config/RoutingConfigurationService.java index a30632866c..a500ff4c23 100644 --- a/apps/routing-api/src/main/java/org/onosproject/routing/config/RoutingConfigurationService.java +++ b/apps/routing-api/src/main/java/org/onosproject/routing/config/RoutingConfigurationService.java @@ -17,6 +17,7 @@ package org.onosproject.routing.config; import org.onlab.packet.IpAddress; import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; import org.onosproject.net.ConnectPoint; import java.util.Map; @@ -41,6 +42,21 @@ public interface RoutingConfigurationService { */ public Map getBgpPeers(); + /** + * Gets the MAC address configured for virtual gateway in SDN network. + * + * @return the MAC address of virtual gateway + */ + public MacAddress getVirtualGatewayMacAddress(); + + /** + * Evaluates whether an IP address is a virtual gateway IP address. + * + * @param ipAddress the IP address to evaluate + * @return true if the IP address is a virtual gateway address, otherwise false + */ + public boolean isVirtualGatewayIpAddress(IpAddress ipAddress); + /** * Evaluates whether an IP address belongs to local SDN network. * diff --git a/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java b/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java index 76f1df0ba7..d5a9e29a39 100644 --- a/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java +++ b/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java @@ -17,6 +17,7 @@ package org.onosproject.routing.config.impl; import com.fasterxml.jackson.annotation.JsonProperty; +import org.onlab.packet.MacAddress; import org.onosproject.routing.config.BgpPeer; import org.onosproject.routing.config.BgpSpeaker; import org.onosproject.routing.config.LocalIpPrefixEntry; @@ -33,6 +34,7 @@ public class Configuration { // the BGP routers outside our SDN network the BGP peers. private List bgpSpeakers; private List peers; + private MacAddress virtualGatewayMacAddress; // All IP prefixes from the configuration are local private List localIp4PrefixEntries = @@ -77,7 +79,7 @@ public class Configuration { } /** - * Sets a list of BGP peers we are configured to peer with. + * Sets a list of BGP peers we configured to peer with. * * @param peers the list of BGP peers */ @@ -86,6 +88,26 @@ public class Configuration { this.peers = peers; } + /** + * Gets the MAC address we configured for virtual gateway + * in SDN network. + * + * @return the MAC address of virtual gateway + */ + public MacAddress getVirtualGatewayMacAddress() { + return virtualGatewayMacAddress; + } + + /** + * Sets the MAC address for virtual gateway in SDN network. + * + * @param virtualGatewayMacAddress the MAC address of virtual gateway + */ + @JsonProperty("virtualGatewayMacAddress") + public void setVirtualGatewayMacAddress(MacAddress virtualGatewayMacAddress) { + this.virtualGatewayMacAddress = virtualGatewayMacAddress; + } + /** * Gets a list of local IPv4 prefix entries configured for local * SDN network. diff --git a/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java b/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java index abb6a13290..c45d286b73 100644 --- a/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java +++ b/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java @@ -29,6 +29,7 @@ import org.onlab.packet.Ip4Address; import org.onlab.packet.Ip6Address; import org.onlab.packet.IpAddress; import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; import org.onosproject.net.ConnectPoint; import org.onosproject.net.host.HostService; import org.onosproject.routing.config.BgpPeer; @@ -43,6 +44,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Collections; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -68,6 +70,7 @@ public class RoutingConfigurationImpl implements RoutingConfigurationService { private Map bgpSpeakers = new ConcurrentHashMap<>(); private Map bgpPeers = new ConcurrentHashMap<>(); + private Set gatewayIpAddresses = new HashSet<>(); private InvertedRadixTree localPrefixTable4 = new ConcurrentInvertedRadixTree<>( @@ -76,6 +79,7 @@ public class RoutingConfigurationImpl implements RoutingConfigurationService { localPrefixTable6 = new ConcurrentInvertedRadixTree<>( new DefaultByteArrayNodeFactory()); + private MacAddress virtualGatewayMacAddress; private HostToInterfaceAdaptor hostAdaptor; @Activate @@ -109,12 +113,16 @@ public class RoutingConfigurationImpl implements RoutingConfigurationService { for (LocalIpPrefixEntry entry : config.getLocalIp4PrefixEntries()) { localPrefixTable4.put(createBinaryString(entry.ipPrefix()), entry); + gatewayIpAddresses.add(entry.getGatewayIpAddress()); } for (LocalIpPrefixEntry entry : config.getLocalIp6PrefixEntries()) { localPrefixTable6.put(createBinaryString(entry.ipPrefix()), entry); + gatewayIpAddresses.add(entry.getGatewayIpAddress()); } + virtualGatewayMacAddress = config.getVirtualGatewayMacAddress(); + } catch (FileNotFoundException e) { log.warn("Configuration file not found: {}", configFileName); } catch (IOException e) { @@ -178,4 +186,14 @@ public class RoutingConfigurationImpl implements RoutingConfigurationService { createBinaryString(ipPrefix)) != null); } + @Override + public boolean isVirtualGatewayIpAddress(IpAddress ipAddress) { + return gatewayIpAddresses.contains(ipAddress); + } + + @Override + public MacAddress getVirtualGatewayMacAddress() { + return virtualGatewayMacAddress; + } + } diff --git a/apps/sdnip/src/main/resources/config-examples/sdnip.json b/apps/sdnip/src/main/resources/config-examples/sdnip.json index 913687ee81..c51de68ae0 100644 --- a/apps/sdnip/src/main/resources/config-examples/sdnip.json +++ b/apps/sdnip/src/main/resources/config-examples/sdnip.json @@ -66,17 +66,21 @@ "ip4LocalPrefixes" : [ { "ipPrefix" : "100.0.0.0/24", - "type" : "PUBLIC" + "type" : "PUBLIC", + "gatewayIp" : "100.0.0.1" }, { "ipPrefix" : "200.0.0.0/8", - "type" : "PUBLIC" + "type" : "PUBLIC", + "gatewayIp" : "200.0.0.3" }, { "ipPrefix" : "192.0.0.0/24", - "type" : "PRIVATE" + "type" : "PRIVATE", + "gatewayIp" : "192.0.0.254" } ], "ip6LocalPrefixes" : [ - ] + ], + "virtualGatewayMacAddress" : "00:00:00:00:00:01" } 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 920585eb2e..55c7247edd 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 @@ -61,5 +61,4 @@ public interface ProxyArpService { * @return true if handled, false otherwise. */ boolean handlePacket(PacketContext context); - } 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 161518c957..27c97613bb 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 @@ -156,7 +156,7 @@ public class ProxyArpManager implements ProxyArpService { for (InterfaceIpAddress ia : addresses.ipAddresses()) { if (ia.ipAddress().equals(targetAddress)) { Ethernet arpReply = - buildArpReply(targetAddress, addresses.mac(), eth); + ARP.buildArpReply(targetAddress, addresses.mac(), eth); sendTo(arpReply, inPort); } } @@ -181,7 +181,7 @@ public class ProxyArpManager implements ProxyArpService { if (src != null && dst != null) { // We know the target host so we can respond - Ethernet arpReply = buildArpReply(targetAddress, dst.mac(), eth); + Ethernet arpReply = ARP.buildArpReply(targetAddress, dst.mac(), eth); sendTo(arpReply, inPort); return; } @@ -296,7 +296,6 @@ public class ProxyArpManager implements ProxyArpService { sendTo(ndpReply, inPort); } - /** * Outputs the given packet out the given port. * @@ -481,41 +480,6 @@ public class ProxyArpManager implements ProxyArpService { return portNumbers; } - /** - * Builds an ARP reply based on a request. - * - * @param srcIp the IP address to use as the reply source - * @param srcMac the MAC address to use as the reply source - * @param request the ARP request we got - * @return an Ethernet frame containing the ARP reply - */ - private Ethernet buildArpReply(Ip4Address srcIp, MacAddress srcMac, - Ethernet request) { - - Ethernet eth = new Ethernet(); - eth.setDestinationMACAddress(request.getSourceMAC()); - eth.setSourceMACAddress(srcMac); - eth.setEtherType(Ethernet.TYPE_ARP); - eth.setVlanID(request.getVlanID()); - - ARP arp = new ARP(); - arp.setOpCode(ARP.OP_REPLY); - arp.setProtocolType(ARP.PROTO_TYPE_IP); - arp.setHardwareType(ARP.HW_TYPE_ETHERNET); - - arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH); - arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH); - arp.setSenderHardwareAddress(srcMac.toBytes()); - arp.setTargetHardwareAddress(request.getSourceMACAddress()); - - arp.setTargetProtocolAddress(((ARP) request.getPayload()) - .getSenderProtocolAddress()); - arp.setSenderProtocolAddress(srcIp.toInt()); - - eth.setPayload(arp); - return eth; - } - /** * Builds an Neighbor Discovery reply based on a request. * diff --git a/utils/misc/src/main/java/org/onlab/packet/ARP.java b/utils/misc/src/main/java/org/onlab/packet/ARP.java index 86fb289696..88e1d4ff49 100644 --- a/utils/misc/src/main/java/org/onlab/packet/ARP.java +++ b/utils/misc/src/main/java/org/onlab/packet/ARP.java @@ -357,4 +357,39 @@ public class ARP extends BasePacket { + ", targetProtocolAddress=" + Arrays.toString(this.targetProtocolAddress) + "]"; } + + /** + * Builds an ARP reply based on a request. + * + * @param srcIp the IP address to use as the reply source + * @param srcMac the MAC address to use as the reply source + * @param request the ARP request we got + * @return an Ethernet frame containing the ARP reply + */ + public static Ethernet buildArpReply(Ip4Address srcIp, MacAddress srcMac, + Ethernet request) { + + Ethernet eth = new Ethernet(); + eth.setDestinationMACAddress(request.getSourceMAC()); + eth.setSourceMACAddress(srcMac); + eth.setEtherType(Ethernet.TYPE_ARP); + eth.setVlanID(request.getVlanID()); + + ARP arp = new ARP(); + arp.setOpCode(ARP.OP_REPLY); + arp.setProtocolType(ARP.PROTO_TYPE_IP); + arp.setHardwareType(ARP.HW_TYPE_ETHERNET); + + arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH); + arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH); + arp.setSenderHardwareAddress(srcMac.toBytes()); + arp.setTargetHardwareAddress(request.getSourceMACAddress()); + + arp.setTargetProtocolAddress(((ARP) request.getPayload()) + .getSenderProtocolAddress()); + arp.setSenderProtocolAddress(srcIp.toInt()); + + eth.setPayload(arp); + return eth; + } }