blob: dded6bd5c5e287291b9c0d77ecad19e22aae33c3 [file] [log] [blame]
/*
* Copyright 2015-present Open Networking Foundation
*
* 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 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.onosproject.net.ConnectPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
/**
* 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;
private Map<String, ConnectPoint> nodeToPort;
@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();
nodeToPort = config.getHosts();
} 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;
}
@Override
public Map<String, ConnectPoint> getNodeToPort() {
return nodeToPort;
}
// 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);
}
}