diff options
Diffstat (limited to 'framework/src/onos/apps/virtualbng')
16 files changed, 1876 insertions, 0 deletions
diff --git a/framework/src/onos/apps/virtualbng/features.xml b/framework/src/onos/apps/virtualbng/features.xml new file mode 100644 index 00000000..2b48bec3 --- /dev/null +++ b/framework/src/onos/apps/virtualbng/features.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ~ 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. + --> +<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}"> + <repository>mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features</repository> + <feature name="${project.artifactId}" version="${project.version}" + description="${project.description}"> + <bundle>mvn:com.sun.jersey/jersey-client/1.19</bundle> + <bundle>mvn:${project.groupId}/onos-app-virtualbng/${project.version}</bundle> + <feature>onos-thirdparty-web</feature> + </feature> +</features> diff --git a/framework/src/onos/apps/virtualbng/pom.xml b/framework/src/onos/apps/virtualbng/pom.xml new file mode 100644 index 00000000..835272ae --- /dev/null +++ b/framework/src/onos/apps/virtualbng/pom.xml @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-apps</artifactId> + <version>1.3.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-app-virtualbng</artifactId> + <packaging>bundle</packaging> + + <description>A virtual Broadband Network Gateway(BNG) application</description> + + <properties> + <onos.app.name>org.onosproject.virtualbng</onos.app.name> + <web.context>/onos/virtualbng</web.context> + <api.version>1.0.0</api.version> + <api.title>ONOS Virtual BNG Gateway REST API</api.title> + <api.description> + APIs for interacting with the Virtual Broadband Network Gateway (BNG) application. + </api.description> + <api.package>org.onosproject.virtualbng</api.package> + </properties> + + <dependencies> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + <version>1.19</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-cli</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.karaf.shell</groupId> + <artifactId>org.apache.karaf.shell.console</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-rest</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>javax.ws.rs</groupId> + <artifactId>jsr311-api</artifactId> + <version>1.1.1</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <_wab>src/main/webapp/</_wab> + <Include-Resource> + WEB-INF/classes/apidoc/swagger.json=target/swagger.json, + {maven-resources} + </Include-Resource> + <Bundle-SymbolicName> + ${project.groupId}.${project.artifactId} + </Bundle-SymbolicName> + <Import-Package> + org.slf4j, + javax.ws.rs, + javax.ws.rs.core, + com.sun.jersey.api.core, + com.sun.jersey.api.client, + com.sun.jersey.spi.container.servlet, + com.sun.jersey.server.impl.container.servlet, + com.fasterxml.jackson.databind, + com.fasterxml.jackson.databind.node, + org.apache.karaf.shell.commands, + com.google.common.*, + org.onlab.packet.*, + org.onlab.rest.*, + org.onosproject.* + </Import-Package> + <Web-ContextPath>${web.context}</Web-ContextPath> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/IpAddressMapEntryCodec.java b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/IpAddressMapEntryCodec.java new file mode 100644 index 00000000..b2096b7e --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/IpAddressMapEntryCodec.java @@ -0,0 +1,42 @@ +/* + * 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.node.ObjectNode; + +import java.util.Map.Entry; + +import org.onlab.packet.IpAddress; +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; + +/** + * Codec for encoding a IP address mapping entry to JSON. + */ +public final class IpAddressMapEntryCodec extends JsonCodec<Entry<IpAddress, IpAddress>> { + + @Override + public ObjectNode encode(Entry<IpAddress, IpAddress> entry, CodecContext context) { + checkNotNull(entry, "IP address map entry cannot be null"); + final ObjectNode result = context.mapper().createObjectNode() + .put(entry.getKey().toString(), entry.getValue().toString()); + + return result; + } +} diff --git a/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/RestClient.java b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/RestClient.java new file mode 100644 index 00000000..e779fe78 --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/RestClient.java @@ -0,0 +1,92 @@ +/* + * 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.net.MediaType.JSON_UTF_8; +import static java.net.HttpURLConnection.HTTP_OK; +import static org.slf4j.LoggerFactory.getLogger; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; + +import java.io.IOException; + +import org.onlab.packet.IpAddress; +import org.slf4j.Logger; + +public class RestClient { + private final Logger log = getLogger(getClass()); + private static final String UTF_8 = JSON_UTF_8.toString(); + private static final ObjectMapper MAPPER = new ObjectMapper(); + private final String url; + + /** + * Constructor. + * + * @param xosServerIpAddress the IP address of the XOS server + * @param xosServerPort the port for the REST service on XOS server + */ + RestClient(IpAddress xosServerIpAddress, int xosServerPort) { + this.url = "http://" + xosServerIpAddress.toString() + ":" + + xosServerPort + "/xoslib/rs/vbng_mapping/"; + } + /** + * Gets a client web resource builder. + * + * @param url the URL to access remote resource + * @return web resource builder + */ + public WebResource.Builder getClientBuilder(String url) { + log.info("URL: {}", url); + Client client = Client.create(); + WebResource resource = client.resource(url); + return resource.accept(UTF_8).type(UTF_8); + } + + /** + * Builds a REST client and fetches XOS mapping data in JSON format. + * + * @return the vBNG map if REST GET succeeds, otherwise return null + */ + public ObjectNode getRest() { + WebResource.Builder builder = getClientBuilder(url); + ClientResponse response = builder.get(ClientResponse.class); + + if (response.getStatus() != HTTP_OK) { + log.info("REST GET request returned error code {}", + response.getStatus()); + return null; + } + + String jsonString = response.getEntity(String.class); + log.info("Fetched JSON string: {}", jsonString); + + JsonNode node; + try { + node = MAPPER.readTree(jsonString); + } catch (IOException e) { + log.error("Failed to read JSON string", e); + return null; + } + + return (ObjectNode) node; + } +} diff --git a/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfiguration.java b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfiguration.java new file mode 100644 index 00000000..ee2cbeaa --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfiguration.java @@ -0,0 +1,123 @@ +/* + * 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 com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Collections; +import java.util.List; + +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; + +/** + * Contains the configuration data for virtual BNG that has been read from a + * JSON-formatted configuration file. + */ +public final class VbngConfiguration { + + private final List<IpPrefix> localPublicIpPrefixes; + private final IpAddress nextHopIpAddress; + private final MacAddress publicFacingMac; + private final IpAddress xosIpAddress; + private final int xosRestPort; + + /** + * Default constructor. + */ + private VbngConfiguration() { + localPublicIpPrefixes = null; + nextHopIpAddress = null; + publicFacingMac = null; + xosIpAddress = null; + xosRestPort = 0; + } + + /** + * Constructor. + * + * @param nextHopIpAddress the IP address of the next hop + * @param prefixes the public IP prefix list for local SDN network + * @param publicFacingMac the MAC address configured for all local + * public IP addresses + * @param xosIpAddress the XOS server IP address + * @param xosRestPort the port of the XOS server for REST + */ + @JsonCreator + public VbngConfiguration(@JsonProperty("localPublicIpPrefixes") + List<IpPrefix> prefixes, + @JsonProperty("nextHopIpAddress") + IpAddress nextHopIpAddress, + @JsonProperty("publicFacingMac") + MacAddress publicFacingMac, + @JsonProperty("xosIpAddress") + IpAddress xosIpAddress, + @JsonProperty("xosRestPort") + int xosRestPort) { + localPublicIpPrefixes = prefixes; + this.nextHopIpAddress = nextHopIpAddress; + this.publicFacingMac = publicFacingMac; + this.xosIpAddress = xosIpAddress; + this.xosRestPort = xosRestPort; + } + + /** + * Gets a list of public IP prefixes configured for local SDN network. + * + * @return the list of public IP prefixes + */ + public List<IpPrefix> getLocalPublicIpPrefixes() { + return Collections.unmodifiableList(localPublicIpPrefixes); + } + + /** + * Gets the IP address configured for the next hop (upstream gateway). + * + * @return the IP address of the next hop + */ + public IpAddress getNextHopIpAddress() { + return nextHopIpAddress; + } + + /** + * Gets the MAC address configured for all the public IP addresses. + * + * @return the MAC address + */ + public MacAddress getPublicFacingMac() { + return publicFacingMac; + } + + /** + * Gets the IP address configured for XOS server. + * + * @return the IP address configured for the XOS server + */ + public IpAddress getXosIpAddress() { + return xosIpAddress; + } + + /** + * Gets the REST communication port configured for XOS server. + * + * @return the REST communication port configured for XOS server + */ + public int getXosRestPort() { + return xosRestPort; + } +} diff --git a/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationManager.java b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationManager.java new file mode 100644 index 00000000..d27d6904 --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationManager.java @@ -0,0 +1,308 @@ +/* + * 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 com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Collections; +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.Service; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Implementation of ConfigurationService which reads virtual BNG + * configuration from a file. + */ +@Component(immediate = true) +@Service +public class VbngConfigurationManager implements VbngConfigurationService { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private static final String CONFIG_DIR = "../config"; + private static final String DEFAULT_CONFIG_FILE = "virtualbng.json"; + private String configFileName = DEFAULT_CONFIG_FILE; + + // If all the IP addresses of one IP prefix are assigned, then we + // mark the value of this IP prefix as false, otherwise as true. + private Map<IpPrefix, Boolean> localPublicIpPrefixes = + new ConcurrentHashMap<>(); + + // Map from private IP address to public IP address + private Map<IpAddress, IpAddress> ipAddressMap = + new ConcurrentHashMap<>(); + + private IpAddress nextHopIpAddress; + private MacAddress macOfPublicIpAddresses; + private IpAddress xosIpAddress; + private int xosRestPort; + + @Activate + public void activate() { + readConfiguration(); + log.info("vBNG configuration service started"); + } + + @Deactivate + public void deactivate() { + log.info("vBNG configuration service stopped"); + } + + /** + * Instructs the configuration reader to read the configuration from the + * file. + */ + public void readConfiguration() { + readConfiguration(configFileName); + } + + /** + * Reads virtual BNG information contained in configuration file. + * + * @param configFilename the name of the configuration file for the virtual + * BNG application + */ + private void readConfiguration(String configFilename) { + File configFile = new File(CONFIG_DIR, configFilename); + ObjectMapper mapper = new ObjectMapper(); + + try { + log.info("Loading config: {}", configFile.getAbsolutePath()); + VbngConfiguration config = mapper.readValue(configFile, + VbngConfiguration.class); + for (IpPrefix prefix : config.getLocalPublicIpPrefixes()) { + localPublicIpPrefixes.put(prefix, true); + } + nextHopIpAddress = config.getNextHopIpAddress(); + macOfPublicIpAddresses = config.getPublicFacingMac(); + xosIpAddress = config.getXosIpAddress(); + xosRestPort = config.getXosRestPort(); + + } catch (FileNotFoundException e) { + log.warn("Configuration file not found: {}", configFileName); + } catch (IOException e) { + log.error("Error loading configuration", e); + } + } + + @Override + public IpAddress getNextHopIpAddress() { + return nextHopIpAddress; + } + + @Override + public MacAddress getPublicFacingMac() { + return macOfPublicIpAddresses; + } + + @Override + public IpAddress getXosIpAddress() { + return xosIpAddress; + } + + @Override + public int getXosRestPort() { + return xosRestPort; + } + + // TODO handle the case: the number of public IP addresses is not enough + // for 1:1 mapping from public IP to private IP. + @Override + public synchronized IpAddress getAvailablePublicIpAddress(IpAddress + privateIpAddress) { + // If there is already a mapping entry for the private IP address, + // then fetch the public IP address in the mapping entry and return it. + IpAddress publicIpAddress = ipAddressMap.get(privateIpAddress); + if (publicIpAddress != null) { + return publicIpAddress; + } + // There is no mapping for the private IP address. + Iterator<Entry<IpPrefix, Boolean>> prefixes = + localPublicIpPrefixes.entrySet().iterator(); + while (prefixes.hasNext()) { + Entry<IpPrefix, Boolean> prefix = prefixes.next(); + if (!prefix.getValue()) { + continue; + } + + if (prefix.getKey().prefixLength() == 32) { + updateIpPrefixStatus(prefix.getKey(), false); + publicIpAddress = prefix.getKey().address(); + ipAddressMap.put(privateIpAddress, publicIpAddress); + return publicIpAddress; + } + + int prefixLen = prefix.getKey().prefixLength(); + int availableIpNum = (int) Math.pow(2, + IpPrefix.MAX_INET_MASK_LENGTH - prefixLen) - 1; + for (int i = 1; i <= availableIpNum; i++) { + publicIpAddress = + increaseIpAddress(prefix.getKey().address(), i); + if (publicIpAddress == null) { + return null; + } + if (ipAddressMap.values().contains(publicIpAddress)) { + continue; + } else if (i == availableIpNum) { + // All the IP addresses are assigned out + // Update this IP prefix status to false + // Note: in this version we do not consider the + // IP recycling issue. + updateIpPrefixStatus(prefix.getKey(), false); + ipAddressMap.put(privateIpAddress, publicIpAddress); + return publicIpAddress; + } else { + ipAddressMap.put(privateIpAddress, publicIpAddress); + return publicIpAddress; + } + } + } + return null; + } + + @Override + public IpAddress getAssignedPublicIpAddress(IpAddress privateIpAddress) { + return ipAddressMap.get(privateIpAddress); + } + + @Override + public boolean isAssignedPublicIpAddress(IpAddress ipAddress) { + return ipAddressMap.containsValue(ipAddress); + } + + @Override + public synchronized IpAddress recycleAssignedPublicIpAddress(IpAddress + privateIpAddress) { + IpAddress publicIpAddress = ipAddressMap.remove(privateIpAddress); + if (publicIpAddress == null) { + return null; + } + + Iterator<Entry<IpPrefix, Boolean>> prefixes = + localPublicIpPrefixes.entrySet().iterator(); + while (prefixes.hasNext()) { + Entry<IpPrefix, Boolean> prefixEntry = prefixes.next(); + if (prefixEntry.getKey().contains(publicIpAddress) + && !prefixEntry.getValue()) { + updateIpPrefixStatus(prefixEntry.getKey(), true); + } + } + log.info("[DELETE] Private IP to Public IP mapping: {} --> {}", + privateIpAddress, publicIpAddress); + return publicIpAddress; + } + + @Override + public Map<IpAddress, IpAddress> getIpAddressMappings() { + return Collections.unmodifiableMap(ipAddressMap); + } + + @Override + public synchronized boolean assignSpecifiedPublicIp(IpAddress publicIpAddress, + IpAddress privateIpAddress) { + + // Judge whether this public IP address is in our public IP + // prefix/address list. + boolean isPublicIpExist = false; + for (Entry<IpPrefix, Boolean> prefix: localPublicIpPrefixes.entrySet()) { + if (prefix.getKey().contains(publicIpAddress)) { + isPublicIpExist = true; + + // Judge whether this public IP address is already assigned + if (!prefix.getValue() || + isAssignedPublicIpAddress(publicIpAddress)) { + log.info("The public IP address {} is already assigned, " + + "and not available.", publicIpAddress); + return false; + } + + // The public IP address is still available + // Store the mapping from private IP address to public IP address + ipAddressMap.put(privateIpAddress, publicIpAddress); + + // Update the prefix status + if (prefix.getKey().prefixLength() == 32) { + updateIpPrefixStatus(prefix.getKey(), false); + return true; + } + + // Judge whether the prefix of this public IP address is used + // up, if so, update the IP prefix status. + int prefixLen = prefix.getKey().prefixLength(); + int availableIpNum = (int) Math.pow(2, + IpPrefix.MAX_INET_MASK_LENGTH - prefixLen) - 1; + int usedIpNum = 0; + for (Entry<IpAddress, IpAddress> ipAddressMapEntry: + ipAddressMap.entrySet()) { + if (prefix.getKey().contains(ipAddressMapEntry.getValue())) { + usedIpNum = usedIpNum + 1; + } + } + if (usedIpNum == availableIpNum) { + updateIpPrefixStatus(prefix.getKey(), false); + } + + return true; + } + } + if (!isPublicIpExist) { + log.info("The public IP address {} retrieved from XOS mapping does " + + "not exist", publicIpAddress); + } + return false; + } + + /** + * Generates a new IP address base on a given IP address plus a number to + * increase. + * + * @param ipAddress the IP address to increase + * @param num the number for ipAddress to add + * @return the new IP address after increase + */ + private IpAddress increaseIpAddress(IpAddress ipAddress, int num) { + if (ipAddress.isIp6()) { + log.info("vBNG currently does not handle IPv6"); + return null; + } + return IpAddress.valueOf(ipAddress.getIp4Address().toInt() + num); + } + + /** + * Updates the IP prefix status in the local public IP prefix table. + * + * @param ipPprefix the IP prefix to update + * @param b the new value for the IP prefix + */ + private void updateIpPrefixStatus(IpPrefix ipPprefix, boolean b) { + localPublicIpPrefixes.replace(ipPprefix, b); + } + +} diff --git a/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationService.java b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationService.java new file mode 100644 index 00000000..ef8698a0 --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationService.java @@ -0,0 +1,110 @@ +/* + * 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 java.util.Map; + +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; + +/** + * Provides information about the virtual BNG configuration. + */ +public interface VbngConfigurationService { + + /** + * Gets the IP address configured for the next hop. + * + * @return the IP address of next hop + */ + IpAddress getNextHopIpAddress(); + + /** + * Gets the MAC address configured for all the public IP addresses. + * + * @return the MAC address + */ + MacAddress getPublicFacingMac(); + + /** + * Gets the IP address configured for XOS server. + * + * @return the IP address configured for the XOS server + */ + IpAddress getXosIpAddress(); + + /** + * Gets the REST communication port configured for XOS server. + * + * @return the REST communication port configured for XOS server + */ + int getXosRestPort(); + + /** + * Evaluates whether an IP address is an assigned public IP address. + * + * @param ipAddress the IP address to evaluate + * @return true if the input IP address is an assigned public IP address, + * otherwise false + */ + boolean isAssignedPublicIpAddress(IpAddress ipAddress); + + /** + * Gets an available public IP address from local public IP prefixes. + * + * @param privateIpAddress a private IP address + * @return an available public IP address if it exists, otherwise null + */ + IpAddress getAvailablePublicIpAddress(IpAddress privateIpAddress); + + /** + * Gets the public IP address already assigned for a private IP address. + * + * @param privateIpAddress a private IP address + * @return the assigned public IP address if it exists, otherwise null + */ + IpAddress getAssignedPublicIpAddress(IpAddress privateIpAddress); + + /** + * Recycles the public IP address assigned for a private IP address, and + * at the same time deletes the mapping entry from this private IP address + * to the public IP address. + * + * @param privateIpAddress a private IP address + * @return the assigned public IP address if it exists, otherwise null + */ + IpAddress recycleAssignedPublicIpAddress(IpAddress privateIpAddress); + + /** + * Gets all the mapping entries from private IP address to public IP + * address. + * + * @return the address map from private IP address to public IP address + */ + Map<IpAddress, IpAddress> getIpAddressMappings(); + + /** + * Tries to assign a given public IP address to a private IP address. If + * success, then sets up the mapping from this private IP address to the + * public IP address, and stores the mapping. + * + * @param publicIpAddress the public IP address try to assign + * @param privateIpAddress a private IP address + * @return true if this public IP address is available, otherwise false + */ + boolean assignSpecifiedPublicIp(IpAddress publicIpAddress, + IpAddress privateIpAddress); +} 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; + } + } +} diff --git a/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngResource.java b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngResource.java new file mode 100644 index 00000000..c6a9c9b3 --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngResource.java @@ -0,0 +1,141 @@ +/* + * 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 org.slf4j.LoggerFactory.getLogger; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.util.Map; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onosproject.rest.AbstractWebResource; +import org.slf4j.Logger; + +/** + * REST services to interact with the virtual BNG. + */ +@Path("privateip") +public class VbngResource extends AbstractWebResource { + + private final Logger log = getLogger(getClass()); + + /** + * Create a new virtual BNG connection. + * + * @param privateIp IP Address for the BNG private network + * @param mac MAC address for the host + * @param hostName name of the host + * @return public IP address for the new connection + */ + @POST + @Path("{privateip}/{mac}/{hostname}") + public String privateIpAddNotification(@PathParam("privateip") + String privateIp, @PathParam("mac") String mac, + @PathParam("hostname") String hostName) { + + log.info("Received creating vBNG request, " + + "privateIp= {}, mac={}, hostName= {}", + privateIp, mac, hostName); + + if (privateIp == null || mac == null || hostName == null) { + log.info("Parameters can not be null"); + return "0"; + } + + IpAddress privateIpAddress = IpAddress.valueOf(privateIp); + MacAddress hostMacAddress = MacAddress.valueOf(mac); + + VbngService vbngService = get(VbngService.class); + + IpAddress publicIpAddress = null; + // Create a virtual BNG + publicIpAddress = vbngService.createVbng(privateIpAddress, + hostMacAddress, + hostName); + + if (publicIpAddress != null) { + return publicIpAddress.toString(); + } else { + return "0"; + } + } + + /** + * Delete a virtual BNG connection. + * + * @param privateIp IP Address for the BNG private network + * @return public IP address for the new connection + */ + @DELETE + @Path("{privateip}") + public String privateIpDeleteNotification(@PathParam("privateip") + String privateIp) { + if (privateIp == null) { + log.info("Private IP address to delete is null"); + return "0"; + } + log.info("Received a private IP address : {} to delete", privateIp); + IpAddress privateIpAddress = IpAddress.valueOf(privateIp); + + VbngService vbngService = get(VbngService.class); + + IpAddress assignedPublicIpAddress = null; + // Delete a virtual BNG + assignedPublicIpAddress = vbngService.deleteVbng(privateIpAddress); + + if (assignedPublicIpAddress != null) { + return assignedPublicIpAddress.toString(); + } else { + return "0"; + } + } + + /** + * Query virtual BNG map. + * + * @return IP Address map + */ + @GET + @Path("map") + @Produces(MediaType.APPLICATION_JSON) + public Response privateIpDeleteNotification() { + + log.info("Received vBNG IP address map request"); + + VbngConfigurationService vbngConfigurationService = + get(VbngConfigurationService.class); + + Map<IpAddress, IpAddress> map = + vbngConfigurationService.getIpAddressMappings(); + ObjectNode result = new ObjectMapper().createObjectNode(); + + result.set("map", new IpAddressMapEntryCodec().encode(map.entrySet(), this)); + + return ok(result.toString()).build(); + } +} diff --git a/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngService.java b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngService.java new file mode 100644 index 00000000..051469ea --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngService.java @@ -0,0 +1,52 @@ +/* + * 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 org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; + +/** + * Provides service of the virtual BNG. + */ +public interface VbngService { + + /** + * Creates a virtual BNG. + * <p> + * It firstly finds out an available local public IP address. Then, it + * sets up paths between the host configured with private IP and + * next hop. Finally it returns the public IP address. + * </p> + * + * @param privateIpAddress the private IP address + * @param hostMacAddress the MAC address for the IP address + * @param hostName the host name for the IP address + * @return the public address if a virtual BGN is successfully created, + * otherwise return null + */ + IpAddress createVbng(IpAddress privateIpAddress, MacAddress hostMacAddress, + String hostName); + + /** + * Deletes a virtual BNG. + * + * @param privateIpAddress a private IP address + * @return the public address assigned for this private IP address if the + * virtual BGN exists and is deleted, otherwise return null if + * there is no vBNG for this private IP address + */ + IpAddress deleteVbng(IpAddress privateIpAddress); +} diff --git a/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VirtualPublicHosts.java b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VirtualPublicHosts.java new file mode 100644 index 00000000..5ab650bc --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VirtualPublicHosts.java @@ -0,0 +1,167 @@ +/* + * 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 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.Ip4Address; +import org.onlab.packet.MacAddress; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.ConnectPoint; +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.packet.DefaultOutboundPacket; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketPriority; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketService; +import org.slf4j.Logger; + +/** + * When the upstream gateway which is outside local SDN network wants to send + * packets to our local public IP addresses, it will send out ARP requests to + * get the MAC address of each public IP address. Actually, there are no hosts + * configured with those public IP addresses, so this class is to emulate the + * behavior of the non-existed hosts and return ARP replies. + * <p> + * Since we will rewrite the destination MAC address in the switch before + * traffic packets go to the destination, so the MAC address can be any number. + * We manually configured a random MAC address for this purpose in the vBNG + * configuration file. + * </p> + */ +@Component(immediate = true) +public class VirtualPublicHosts { + private final Logger log = getLogger(getClass()); + + private static final String APP_NAME = + "org.onosproject.virtualbng.VirtualPublicHosts"; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketService packetService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected VbngConfigurationService vbngConfigService; + + private ApplicationId appId; + private ArpRequestProcessor processor = new ArpRequestProcessor(); + + @Activate + public void activate() { + appId = coreService.registerApplication(APP_NAME); + + packetService.addProcessor(processor, PacketProcessor.director(6)); + requestIntercepts(); + log.info("vBNG virtual public hosts started"); + } + + @Deactivate + public void deactivate() { + withdrawIntercepts(); + packetService.removeProcessor(processor); + processor = null; + log.info("vBNG virtual public hosts Stopped"); + } + + /** + * Request packet in via PacketService. + */ + private void requestIntercepts() { + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + // Only IPv4 is supported in current vBNG. + selector.matchEthType(Ethernet.TYPE_ARP); + packetService.requestPackets(selector.build(), + PacketPriority.REACTIVE, appId); + } + + /** + * Cancel request for packet in via PacketService. + */ + private void withdrawIntercepts() { + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + // Only IPv4 is supported in current vBNG. + selector.matchEthType(Ethernet.TYPE_ARP); + packetService.cancelPackets(selector.build(), + PacketPriority.REACTIVE, appId); + } + + /** + * This class filters out the ARP request packets, generates the ARP + * reply packets, and emits those packets. + */ + private class ArpRequestProcessor implements PacketProcessor { + @Override + public void process(PacketContext context) { + + InboundPacket pkt = context.inPacket(); + Ethernet ethPkt = pkt.parsed(); + + // Only handle the ARP packets + if (ethPkt == null || ethPkt.getEtherType() != Ethernet.TYPE_ARP) { + return; + } + ARP arpPacket = (ARP) ethPkt.getPayload(); + // Only handle ARP request packets + if (arpPacket.getOpCode() != ARP.OP_REQUEST) { + return; + } + + Ip4Address targetIpAddress = Ip4Address + .valueOf(arpPacket.getTargetProtocolAddress()); + + // Only handle an ARP request when the target IP address inside is + // an assigned public IP address + if (!vbngConfigService.isAssignedPublicIpAddress(targetIpAddress)) { + return; + } + + MacAddress virtualHostMac = + vbngConfigService.getPublicFacingMac(); + if (virtualHostMac == null) { + return; + } + + ConnectPoint srcConnectPoint = pkt.receivedFrom(); + Ethernet eth = ARP.buildArpReply(targetIpAddress, + virtualHostMac, + ethPkt); + + TrafficTreatment.Builder builder = + DefaultTrafficTreatment.builder(); + builder.setOutput(srcConnectPoint.port()); + packetService.emit(new DefaultOutboundPacket( + srcConnectPoint.deviceId(), + builder.build(), + ByteBuffer.wrap(eth.serialize()))); + } + } +} diff --git a/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/cli/MappingListCommand.java b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/cli/MappingListCommand.java new file mode 100644 index 00000000..e8308376 --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/cli/MappingListCommand.java @@ -0,0 +1,66 @@ +/* + * 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.cli; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.karaf.shell.commands.Command; +import org.onlab.packet.IpAddress; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.virtualbng.VbngConfigurationService; + +/** + * Command to show the list of vBNG IP address mapping entries. + */ +@Command(scope = "onos", name = "vbngs", + description = "Lists all vBNG IP address mapping entries") +public class MappingListCommand extends AbstractShellCommand { + + private static final String FORMAT_HEADER = + " Private IP - Public IP"; + private static final String FORMAT_MAPPING = + " %s - %s"; + + @Override + protected void execute() { + + VbngConfigurationService service = + AbstractShellCommand.get(VbngConfigurationService.class); + + // Print all mapping entries + printMappingEntries(service.getIpAddressMappings()); + } + + /** + * Prints all vBNG IP address mapping entries. + * + * @param map the map from private IP address to public address + */ + private void printMappingEntries(Map<IpAddress, IpAddress> map) { + print(FORMAT_HEADER); + + Iterator<Entry<IpAddress, IpAddress>> entries = + map.entrySet().iterator(); + while (entries.hasNext()) { + Entry<IpAddress, IpAddress> entry = entries.next(); + print(FORMAT_MAPPING, entry.getKey(), entry.getValue()); + } + + print(""); + } +}
\ No newline at end of file diff --git a/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/cli/package-info.java b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/cli/package-info.java new file mode 100644 index 00000000..9cb53efb --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/cli/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * The CLI of vBNG application. + */ +package org.onosproject.virtualbng.cli; diff --git a/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/package-info.java b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/package-info.java new file mode 100644 index 00000000..7648d8bc --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/java/org/onosproject/virtualbng/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * A virtual Broadband Network Gateway (BNG) application. + */ +package org.onosproject.virtualbng; diff --git a/framework/src/onos/apps/virtualbng/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/virtualbng/src/main/resources/OSGI-INF/blueprint/shell-config.xml new file mode 100644 index 00000000..48532b3f --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,23 @@ +<!-- + ~ 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. + --> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"> + <command> + <action class="org.onosproject.virtualbng.cli.MappingListCommand"/> + </command> + </command-bundle> +</blueprint> diff --git a/framework/src/onos/apps/virtualbng/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/virtualbng/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..e8e0071f --- /dev/null +++ b/framework/src/onos/apps/virtualbng/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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. + --> +<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" + xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" + id="ONOS" version="2.5"> + <display-name>ONOS Virtual BNG APP REST API</display-name> + + <servlet> + <servlet-name>JAX-RS Service</servlet-name> + <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> + <init-param> + <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name> + <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value> + </init-param> + <init-param> + <param-name>com.sun.jersey.config.property.classnames</param-name> + <param-value> + org.onosproject.virtualbng.VbngResource + </param-value> + </init-param> + <load-on-startup>1</load-on-startup> + </servlet> + + <servlet-mapping> + <servlet-name>JAX-RS Service</servlet-name> + <url-pattern>/*</url-pattern> + </servlet-mapping> + +</web-app> |