aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngManager.java')
-rw-r--r--framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngManager.java532
1 files changed, 532 insertions, 0 deletions
diff --git a/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngManager.java b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngManager.java
new file mode 100644
index 00000000..5e82b7e8
--- /dev/null
+++ b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngManager.java
@@ -0,0 +1,532 @@
+/*
+ * 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.virtualbng;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Maps;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is a virtual Broadband Network Gateway (BNG) application. It mainly
+ * has 3 functions:
+ * (1) assigns and replies a public IP address to a REST request with a private
+ * IP address
+ * (2) maintains the mapping from the private IP address to the public IP address
+ * (3) installs point to point intents for the host configured with private IP
+ * address to access Internet
+ */
+@Component(immediate = true)
+@Service
+public class VbngManager implements VbngService {
+
+ private static final String APP_NAME = "org.onosproject.virtualbng";
+ private static final String VBNG_MAP_NAME = "vbng_mapping";
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService intentService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected VbngConfigurationService vbngConfigurationService;
+
+ private ApplicationId appId;
+ private Map<IpAddress, PointToPointIntent> p2pIntentsFromHost;
+ private Map<IpAddress, PointToPointIntent> p2pIntentsToHost;
+
+ // This map stores the mapping from the private IP addresses to VcpeHost.
+ // The IP addresses in this map are all the private IP addresses we failed
+ // to create vBNGs due to the next hop host was not in ONOS.
+ private Map<IpAddress, VcpeHost> privateIpAddressMap;
+
+ // Store the mapping from hostname to connect point
+ private Map<String, ConnectPoint> nodeToPort;
+
+ private HostListener hostListener;
+ private IpAddress nextHopIpAddress;
+
+ private static final DeviceId FABRIC_DEVICE_ID =
+ DeviceId.deviceId("of:8f0e486e73000187");
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication(APP_NAME);
+ p2pIntentsFromHost = new ConcurrentHashMap<>();
+ p2pIntentsToHost = new ConcurrentHashMap<>();
+ privateIpAddressMap = new ConcurrentHashMap<>();
+
+ setupMap();
+
+ nextHopIpAddress = vbngConfigurationService.getNextHopIpAddress();
+ hostListener = new InternalHostListener();
+ hostService.addListener(hostListener);
+
+ log.info("vBNG Started");
+
+ // Recover the status before vBNG restarts
+ statusRecovery();
+ }
+
+ @Deactivate
+ public void deactivate() {
+ hostService.removeListener(hostListener);
+ log.info("vBNG Stopped");
+ }
+
+ /**
+ * Recovers from XOS record. Re-sets up the mapping between private IP
+ * address and public IP address, re-calculates intents and re-installs
+ * those intents.
+ */
+ private void statusRecovery() {
+ log.info("vBNG starts to recover from XOS record......");
+ RestClient restClient =
+ new RestClient(vbngConfigurationService.getXosIpAddress(),
+ vbngConfigurationService.getXosRestPort());
+ ObjectNode map = restClient.getRest();
+ if (map == null) {
+ log.info("Stop to recover vBNG status due to the vBNG map "
+ + "is null!");
+ return;
+ }
+
+ log.info("Get record from XOS: {}", map);
+
+ ArrayNode array = (ArrayNode) map.get(VBNG_MAP_NAME);
+ Iterator<JsonNode> entries = array.elements();
+ while (entries.hasNext()) {
+ ObjectNode entry = (ObjectNode) entries.next();
+
+ IpAddress hostIpAdddress =
+ IpAddress.valueOf(entry.get("private_ip").asText());
+ IpAddress publicIpAddress =
+ IpAddress.valueOf(entry.get("routeable_subnet").asText());
+ MacAddress macAddress =
+ MacAddress.valueOf(entry.get("mac").asText());
+ String hostName = entry.get("hostname").asText();
+
+ // Create vBNG
+ createVbng(hostIpAdddress, publicIpAddress, macAddress, hostName);
+
+ }
+ }
+
+ /**
+ * Sets up mapping from hostname to connect point.
+ */
+ private void setupMap() {
+ nodeToPort = Maps.newHashMap();
+
+ nodeToPort.put("cordcompute01.onlab.us",
+ new ConnectPoint(FABRIC_DEVICE_ID,
+ PortNumber.portNumber(48)));
+
+ nodeToPort.put("cordcompute02.onlab.us",
+ new ConnectPoint(FABRIC_DEVICE_ID,
+ PortNumber.portNumber(47)));
+ }
+
+ /**
+ * Creates a new vBNG.
+ *
+ * @param privateIpAddress a private IP address
+ * @param publicIpAddress the public IP address for the private IP address
+ * @param hostMacAddress the MAC address for the private IP address
+ * @param hostName the host name for the private IP address
+ */
+ private void createVbng(IpAddress privateIpAddress,
+ IpAddress publicIpAddress,
+ MacAddress hostMacAddress,
+ String hostName) {
+ boolean result = vbngConfigurationService
+ .assignSpecifiedPublicIp(publicIpAddress, privateIpAddress);
+ if (!result) {
+ log.info("Assign public IP address {} for private IP address {} "
+ + "failed!", publicIpAddress, privateIpAddress);
+ log.info("Failed to create vBNG for private IP address {}",
+ privateIpAddress);
+ return;
+ }
+ log.info("[ADD] Private IP to Public IP mapping: {} --> {}",
+ privateIpAddress, publicIpAddress);
+
+ // Setup paths between the host configured with private IP and
+ // next hop
+ if (!setupForwardingPaths(privateIpAddress, publicIpAddress,
+ hostMacAddress, hostName)) {
+ privateIpAddressMap.put(privateIpAddress,
+ new VcpeHost(hostMacAddress, hostName));
+ }
+ }
+
+ @Override
+ public IpAddress createVbng(IpAddress privateIpAddress,
+ MacAddress hostMacAddress,
+ String hostName) {
+
+ IpAddress publicIpAddress =
+ vbngConfigurationService.getAvailablePublicIpAddress(
+ privateIpAddress);
+ if (publicIpAddress == null) {
+ log.info("Did not find an available public IP address to use.");
+ return null;
+ }
+ log.info("[ADD] Private IP to Public IP mapping: {} --> {}",
+ privateIpAddress, publicIpAddress);
+
+ // Setup paths between the host configured with private IP and
+ // next hop
+ if (!setupForwardingPaths(privateIpAddress, publicIpAddress,
+ hostMacAddress, hostName)) {
+ privateIpAddressMap.put(privateIpAddress,
+ new VcpeHost(hostMacAddress, hostName));
+ }
+ return publicIpAddress;
+ }
+
+ @Override
+ public IpAddress deleteVbng(IpAddress privateIpAddress) {
+ // Recycle the public IP address assigned to this private IP address.
+ // Recycling will also delete the mapping entry from the private IP
+ // address to public IP address.
+ IpAddress assignedPublicIpAddress = vbngConfigurationService
+ .recycleAssignedPublicIpAddress(privateIpAddress);
+ if (assignedPublicIpAddress == null) {
+ return null;
+ }
+
+ // Remove the private IP address from privateIpAddressMap
+ privateIpAddressMap.remove(privateIpAddress);
+
+ // Remove intents
+ removeForwardingPaths(privateIpAddress);
+
+ return assignedPublicIpAddress;
+ }
+
+ /**
+ * Removes the forwarding paths in both two directions between host
+ * configured with private IP and next hop.
+ *
+ * @param privateIp the private IP address of a local host
+ */
+ private void removeForwardingPaths(IpAddress privateIp) {
+ PointToPointIntent toNextHopIntent =
+ p2pIntentsFromHost.remove(privateIp);
+ if (toNextHopIntent != null) {
+ intentService.withdraw(toNextHopIntent);
+ //intentService.purge(toNextHopIntent);
+ }
+ PointToPointIntent toLocalHostIntent =
+ p2pIntentsToHost.remove(privateIp);
+ if (toLocalHostIntent != null) {
+ intentService.withdraw(toLocalHostIntent);
+ //intentService.purge(toLocalHostIntent);
+ }
+ }
+
+ /**
+ * Sets up forwarding paths in both two directions between host configured
+ * with private IP and next hop.
+ *
+ * @param privateIp the private IP address of a local host
+ * @param publicIp the public IP address assigned for the private IP address
+ * @param hostMacAddress the MAC address for the IP address
+ * @param hostName the host name for the IP address
+ */
+ private boolean setupForwardingPaths(IpAddress privateIp,
+ IpAddress publicIp,
+ MacAddress hostMacAddress,
+ String hostName) {
+ checkNotNull(privateIp);
+ checkNotNull(publicIp);
+ checkNotNull(hostMacAddress);
+ checkNotNull(hostName);
+
+ if (nextHopIpAddress == null) {
+ log.warn("Did not find next hop IP address");
+ return false;
+ }
+
+ // If there are already intents for private IP address in the system,
+ // we will do nothing and directly return.
+ if (p2pIntentsFromHost.containsKey(privateIp)
+ && p2pIntentsToHost.containsKey(privateIp)) {
+ return true;
+ }
+
+ Host nextHopHost = null;
+ if (!hostService.getHostsByIp(nextHopIpAddress).isEmpty()) {
+ nextHopHost = hostService.getHostsByIp(nextHopIpAddress)
+ .iterator().next();
+ } else {
+ hostService.startMonitoringIp(nextHopIpAddress);
+ if (hostService.getHostsByIp(privateIp).isEmpty()) {
+ hostService.startMonitoringIp(privateIp);
+ }
+ return false;
+ }
+
+ ConnectPoint nextHopConnectPoint =
+ new ConnectPoint(nextHopHost.location().elementId(),
+ nextHopHost.location().port());
+ ConnectPoint localHostConnectPoint = nodeToPort.get(hostName);
+
+ // Generate and install intent for traffic from host configured with
+ // private IP
+ if (!p2pIntentsFromHost.containsKey(privateIp)) {
+ PointToPointIntent toNextHopIntent
+ = srcMatchIntentGenerator(privateIp,
+ publicIp,
+ nextHopHost.mac(),
+ nextHopConnectPoint,
+ localHostConnectPoint
+ );
+ p2pIntentsFromHost.put(privateIp, toNextHopIntent);
+ intentService.submit(toNextHopIntent);
+ }
+
+ // Generate and install intent for traffic to host configured with
+ // private IP
+ if (!p2pIntentsToHost.containsKey(privateIp)) {
+ PointToPointIntent toLocalHostIntent
+ = dstMatchIntentGenerator(publicIp,
+ privateIp,
+ hostMacAddress,
+ localHostConnectPoint,
+ nextHopConnectPoint);
+ p2pIntentsToHost.put(privateIp, toLocalHostIntent);
+ intentService.submit(toLocalHostIntent);
+ }
+
+ return true;
+ }
+
+ /**
+ * Listener for host events.
+ */
+ private class InternalHostListener implements HostListener {
+ @Override
+ public void event(HostEvent event) {
+ log.debug("Received HostEvent {}", event);
+
+ Host host = event.subject();
+ if (event.type() != HostEvent.Type.HOST_ADDED) {
+ return;
+ }
+
+ for (IpAddress ipAddress: host.ipAddresses()) {
+ // The POST method from XOS gives us MAC and host name, so we
+ // do not need to do anything after receive a vCPE host event
+ // for now.
+ /*if (privateIpAddressSet.contains(ipAddress)) {
+ createVbngAgain(ipAddress);
+ }*/
+
+ if (nextHopIpAddress != null &&
+ ipAddress.equals(nextHopIpAddress)) {
+
+ for (Entry<IpAddress, VcpeHost> entry:
+ privateIpAddressMap.entrySet()) {
+ createVbngAgain(entry.getKey());
+ }
+
+ }
+ }
+ }
+ }
+
+ /**
+ * Tries to create vBNG again after receiving a host event if the IP
+ * address of the host is the next hop IP address.
+ *
+ * @param privateIpAddress the private IP address
+ */
+ private void createVbngAgain(IpAddress privateIpAddress) {
+ IpAddress publicIpAddress = vbngConfigurationService
+ .getAssignedPublicIpAddress(privateIpAddress);
+ if (publicIpAddress == null) {
+ // We only need to handle the private IP addresses for which we
+ // already returned the REST replies with assigned public IP
+ // addresses. If a private IP addresses does not have an assigned
+ // public IP address, we should not get it an available public IP
+ // address here, and we should delete it in the unhandled private
+ // IP address map.
+ privateIpAddressMap.remove(privateIpAddress);
+ return;
+ }
+ VcpeHost vcpeHost = privateIpAddressMap.get(privateIpAddress);
+ if (setupForwardingPaths(privateIpAddress, publicIpAddress,
+ vcpeHost.macAddress, vcpeHost.hostName)) {
+ privateIpAddressMap.remove(privateIpAddress);
+ }
+ }
+
+ /**
+ * PointToPointIntent Generator.
+ * <p>
+ * The intent will match the source IP address in packet, rewrite the
+ * source IP address, and rewrite the destination MAC address.
+ * </p>
+ *
+ * @param srcIpAddress the source IP address in packet to match
+ * @param newSrcIpAddress the new source IP address to set
+ * @param dstMacAddress the destination MAC address to set
+ * @param dstConnectPoint the egress point
+ * @param srcConnectPoint the ingress point
+ * @return a PointToPointIntent
+ */
+ private PointToPointIntent srcMatchIntentGenerator(
+ IpAddress srcIpAddress,
+ IpAddress newSrcIpAddress,
+ MacAddress dstMacAddress,
+ ConnectPoint dstConnectPoint,
+ ConnectPoint srcConnectPoint) {
+ checkNotNull(srcIpAddress);
+ checkNotNull(newSrcIpAddress);
+ checkNotNull(dstMacAddress);
+ checkNotNull(dstConnectPoint);
+ checkNotNull(srcConnectPoint);
+
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchEthType(Ethernet.TYPE_IPV4);
+ selector.matchIPSrc(IpPrefix.valueOf(srcIpAddress,
+ IpPrefix.MAX_INET_MASK_LENGTH));
+
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+ treatment.setEthDst(dstMacAddress);
+ treatment.setIpSrc(newSrcIpAddress);
+
+ Key key = Key.of(srcIpAddress.toString() + "MatchSrc", appId);
+ PointToPointIntent intent = PointToPointIntent.builder()
+ .appId(appId)
+ .key(key)
+ .selector(selector.build())
+ .treatment(treatment.build())
+ .egressPoint(dstConnectPoint)
+ .ingressPoint(srcConnectPoint)
+ .build();
+
+ log.info("Generated a PointToPointIntent for traffic from local host "
+ + ": {}", intent);
+ return intent;
+ }
+
+ /**
+ * PointToPointIntent Generator.
+ * <p>
+ * The intent will match the destination IP address in packet, rewrite the
+ * destination IP address, and rewrite the destination MAC address.
+ * </p>
+ *
+ * @param dstIpAddress the destination IP address in packet to match
+ * @param newDstIpAddress the new destination IP address to set
+ * @param dstMacAddress the destination MAC address to set
+ * @param dstConnectPoint the egress point
+ * @param srcConnectPoint the ingress point
+ * @return a PointToPointIntent
+ */
+ private PointToPointIntent dstMatchIntentGenerator(
+ IpAddress dstIpAddress,
+ IpAddress newDstIpAddress,
+ MacAddress dstMacAddress,
+ ConnectPoint dstConnectPoint,
+ ConnectPoint srcConnectPoint) {
+ checkNotNull(dstIpAddress);
+ checkNotNull(newDstIpAddress);
+ checkNotNull(dstMacAddress);
+ checkNotNull(dstConnectPoint);
+ checkNotNull(srcConnectPoint);
+
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchEthType(Ethernet.TYPE_IPV4);
+ selector.matchIPDst(IpPrefix.valueOf(dstIpAddress,
+ IpPrefix.MAX_INET_MASK_LENGTH));
+
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+ treatment.setEthDst(dstMacAddress);
+ treatment.setIpDst(newDstIpAddress);
+
+ Key key = Key.of(newDstIpAddress.toString() + "MatchDst", appId);
+ PointToPointIntent intent = PointToPointIntent.builder()
+ .appId(appId)
+ .key(key)
+ .selector(selector.build())
+ .treatment(treatment.build())
+ .egressPoint(dstConnectPoint)
+ .ingressPoint(srcConnectPoint)
+ .build();
+ log.info("Generated a PointToPointIntent for traffic to local host "
+ + ": {}", intent);
+
+ return intent;
+ }
+
+ /**
+ * Constructor to store the a vCPE host info.
+ */
+ private class VcpeHost {
+ MacAddress macAddress;
+ String hostName;
+ public VcpeHost(MacAddress macAddress, String hostName) {
+ this.macAddress = macAddress;
+ this.hostName = hostName;
+ }
+ }
+}