blob: dded6bd5c5e287291b9c0d77ecad19e22aae33c3 [file] [log] [blame]
Pingping Linffa27d32015-04-30 14:41:03 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Pingping Linffa27d32015-04-30 14:41:03 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.virtualbng;
17
18import com.fasterxml.jackson.databind.ObjectMapper;
Jonathan Hartbbc352f2015-10-27 19:24:36 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Service;
23import org.onlab.packet.IpAddress;
24import org.onlab.packet.IpPrefix;
25import org.onlab.packet.MacAddress;
26import org.onosproject.net.ConnectPoint;
27import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
Pingping Linffa27d32015-04-30 14:41:03 -070029
30import java.io.File;
31import java.io.FileNotFoundException;
32import java.io.IOException;
Pingping Linde77ee52015-06-03 17:16:07 -070033import java.util.Collections;
Pingping Linffa27d32015-04-30 14:41:03 -070034import java.util.Iterator;
35import java.util.Map;
36import java.util.Map.Entry;
37import java.util.concurrent.ConcurrentHashMap;
38
Pingping Linffa27d32015-04-30 14:41:03 -070039/**
40 * Implementation of ConfigurationService which reads virtual BNG
41 * configuration from a file.
42 */
43@Component(immediate = true)
44@Service
45public class VbngConfigurationManager implements VbngConfigurationService {
46
47 private final Logger log = LoggerFactory.getLogger(getClass());
48
49 private static final String CONFIG_DIR = "../config";
50 private static final String DEFAULT_CONFIG_FILE = "virtualbng.json";
51 private String configFileName = DEFAULT_CONFIG_FILE;
52
53 // If all the IP addresses of one IP prefix are assigned, then we
54 // mark the value of this IP prefix as false, otherwise as true.
55 private Map<IpPrefix, Boolean> localPublicIpPrefixes =
56 new ConcurrentHashMap<>();
57
58 // Map from private IP address to public IP address
59 private Map<IpAddress, IpAddress> ipAddressMap =
60 new ConcurrentHashMap<>();
61
62 private IpAddress nextHopIpAddress;
Pingping Linbe126562015-05-06 17:57:10 -070063 private MacAddress macOfPublicIpAddresses;
Pingping Line10ece02015-06-11 19:26:24 -070064 private IpAddress xosIpAddress;
65 private int xosRestPort;
Jonathan Hartbbc352f2015-10-27 19:24:36 -070066 private Map<String, ConnectPoint> nodeToPort;
Pingping Linffa27d32015-04-30 14:41:03 -070067
68 @Activate
69 public void activate() {
70 readConfiguration();
71 log.info("vBNG configuration service started");
72 }
73
74 @Deactivate
75 public void deactivate() {
76 log.info("vBNG configuration service stopped");
77 }
78
79 /**
80 * Instructs the configuration reader to read the configuration from the
81 * file.
82 */
83 public void readConfiguration() {
84 readConfiguration(configFileName);
85 }
86
87 /**
88 * Reads virtual BNG information contained in configuration file.
89 *
90 * @param configFilename the name of the configuration file for the virtual
91 * BNG application
92 */
93 private void readConfiguration(String configFilename) {
94 File configFile = new File(CONFIG_DIR, configFilename);
95 ObjectMapper mapper = new ObjectMapper();
96
97 try {
98 log.info("Loading config: {}", configFile.getAbsolutePath());
99 VbngConfiguration config = mapper.readValue(configFile,
100 VbngConfiguration.class);
101 for (IpPrefix prefix : config.getLocalPublicIpPrefixes()) {
102 localPublicIpPrefixes.put(prefix, true);
103 }
104 nextHopIpAddress = config.getNextHopIpAddress();
Pingping Linbe126562015-05-06 17:57:10 -0700105 macOfPublicIpAddresses = config.getPublicFacingMac();
Pingping Line10ece02015-06-11 19:26:24 -0700106 xosIpAddress = config.getXosIpAddress();
107 xosRestPort = config.getXosRestPort();
Jonathan Hartbbc352f2015-10-27 19:24:36 -0700108 nodeToPort = config.getHosts();
109
Pingping Linffa27d32015-04-30 14:41:03 -0700110
111 } catch (FileNotFoundException e) {
112 log.warn("Configuration file not found: {}", configFileName);
113 } catch (IOException e) {
114 log.error("Error loading configuration", e);
115 }
116 }
117
118 @Override
119 public IpAddress getNextHopIpAddress() {
120 return nextHopIpAddress;
121 }
122
Pingping Linbe126562015-05-06 17:57:10 -0700123 @Override
124 public MacAddress getPublicFacingMac() {
125 return macOfPublicIpAddresses;
126 }
127
Pingping Line10ece02015-06-11 19:26:24 -0700128 @Override
129 public IpAddress getXosIpAddress() {
130 return xosIpAddress;
131 }
132
133 @Override
134 public int getXosRestPort() {
135 return xosRestPort;
136 }
137
Jonathan Hartbbc352f2015-10-27 19:24:36 -0700138 @Override
139 public Map<String, ConnectPoint> getNodeToPort() {
140 return nodeToPort;
141 }
142
Pingping Linffa27d32015-04-30 14:41:03 -0700143 // TODO handle the case: the number of public IP addresses is not enough
144 // for 1:1 mapping from public IP to private IP.
145 @Override
Pingping Lin99d0b202015-05-05 14:08:48 -0700146 public synchronized IpAddress getAvailablePublicIpAddress(IpAddress
147 privateIpAddress) {
Pingping Linffa27d32015-04-30 14:41:03 -0700148 // If there is already a mapping entry for the private IP address,
149 // then fetch the public IP address in the mapping entry and return it.
150 IpAddress publicIpAddress = ipAddressMap.get(privateIpAddress);
151 if (publicIpAddress != null) {
152 return publicIpAddress;
153 }
154 // There is no mapping for the private IP address.
155 Iterator<Entry<IpPrefix, Boolean>> prefixes =
156 localPublicIpPrefixes.entrySet().iterator();
157 while (prefixes.hasNext()) {
158 Entry<IpPrefix, Boolean> prefix = prefixes.next();
159 if (!prefix.getValue()) {
160 continue;
161 }
162
163 if (prefix.getKey().prefixLength() == 32) {
164 updateIpPrefixStatus(prefix.getKey(), false);
165 publicIpAddress = prefix.getKey().address();
166 ipAddressMap.put(privateIpAddress, publicIpAddress);
167 return publicIpAddress;
168 }
169
170 int prefixLen = prefix.getKey().prefixLength();
171 int availableIpNum = (int) Math.pow(2,
172 IpPrefix.MAX_INET_MASK_LENGTH - prefixLen) - 1;
173 for (int i = 1; i <= availableIpNum; i++) {
174 publicIpAddress =
175 increaseIpAddress(prefix.getKey().address(), i);
176 if (publicIpAddress == null) {
177 return null;
178 }
179 if (ipAddressMap.values().contains(publicIpAddress)) {
180 continue;
181 } else if (i == availableIpNum) {
182 // All the IP addresses are assigned out
183 // Update this IP prefix status to false
184 // Note: in this version we do not consider the
185 // IP recycling issue.
186 updateIpPrefixStatus(prefix.getKey(), false);
187 ipAddressMap.put(privateIpAddress, publicIpAddress);
188 return publicIpAddress;
189 } else {
190 ipAddressMap.put(privateIpAddress, publicIpAddress);
191 return publicIpAddress;
192 }
193 }
194 }
195 return null;
196 }
197
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700198 @Override
199 public IpAddress getAssignedPublicIpAddress(IpAddress privateIpAddress) {
200 return ipAddressMap.get(privateIpAddress);
201 }
202
Pingping Linbe126562015-05-06 17:57:10 -0700203 @Override
204 public boolean isAssignedPublicIpAddress(IpAddress ipAddress) {
205 return ipAddressMap.containsValue(ipAddress);
206 }
207
Pingping Lind2afaf22015-06-02 10:46:29 -0700208 @Override
Pingping Lin53ae34f2015-06-09 10:07:08 -0700209 public synchronized IpAddress recycleAssignedPublicIpAddress(IpAddress
Pingping Lind2afaf22015-06-02 10:46:29 -0700210 privateIpAddress) {
211 IpAddress publicIpAddress = ipAddressMap.remove(privateIpAddress);
212 if (publicIpAddress == null) {
213 return null;
214 }
215
216 Iterator<Entry<IpPrefix, Boolean>> prefixes =
217 localPublicIpPrefixes.entrySet().iterator();
218 while (prefixes.hasNext()) {
219 Entry<IpPrefix, Boolean> prefixEntry = prefixes.next();
220 if (prefixEntry.getKey().contains(publicIpAddress)
221 && !prefixEntry.getValue()) {
222 updateIpPrefixStatus(prefixEntry.getKey(), true);
223 }
224 }
Pingping Linde77ee52015-06-03 17:16:07 -0700225 log.info("[DELETE] Private IP to Public IP mapping: {} --> {}",
226 privateIpAddress, publicIpAddress);
Pingping Lind2afaf22015-06-02 10:46:29 -0700227 return publicIpAddress;
228 }
229
Pingping Linde77ee52015-06-03 17:16:07 -0700230 @Override
231 public Map<IpAddress, IpAddress> getIpAddressMappings() {
232 return Collections.unmodifiableMap(ipAddressMap);
233 }
234
Pingping Lin53ae34f2015-06-09 10:07:08 -0700235 @Override
236 public synchronized boolean assignSpecifiedPublicIp(IpAddress publicIpAddress,
237 IpAddress privateIpAddress) {
238
239 // Judge whether this public IP address is in our public IP
240 // prefix/address list.
241 boolean isPublicIpExist = false;
242 for (Entry<IpPrefix, Boolean> prefix: localPublicIpPrefixes.entrySet()) {
243 if (prefix.getKey().contains(publicIpAddress)) {
244 isPublicIpExist = true;
245
246 // Judge whether this public IP address is already assigned
247 if (!prefix.getValue() ||
248 isAssignedPublicIpAddress(publicIpAddress)) {
249 log.info("The public IP address {} is already assigned, "
250 + "and not available.", publicIpAddress);
251 return false;
252 }
253
254 // The public IP address is still available
255 // Store the mapping from private IP address to public IP address
256 ipAddressMap.put(privateIpAddress, publicIpAddress);
257
258 // Update the prefix status
259 if (prefix.getKey().prefixLength() == 32) {
260 updateIpPrefixStatus(prefix.getKey(), false);
261 return true;
262 }
263
264 // Judge whether the prefix of this public IP address is used
265 // up, if so, update the IP prefix status.
266 int prefixLen = prefix.getKey().prefixLength();
267 int availableIpNum = (int) Math.pow(2,
268 IpPrefix.MAX_INET_MASK_LENGTH - prefixLen) - 1;
269 int usedIpNum = 0;
270 for (Entry<IpAddress, IpAddress> ipAddressMapEntry:
271 ipAddressMap.entrySet()) {
272 if (prefix.getKey().contains(ipAddressMapEntry.getValue())) {
273 usedIpNum = usedIpNum + 1;
274 }
275 }
276 if (usedIpNum == availableIpNum) {
277 updateIpPrefixStatus(prefix.getKey(), false);
278 }
279
280 return true;
281 }
282 }
283 if (!isPublicIpExist) {
284 log.info("The public IP address {} retrieved from XOS mapping does "
285 + "not exist", publicIpAddress);
286 }
287 return false;
288 }
289
Pingping Linffa27d32015-04-30 14:41:03 -0700290 /**
291 * Generates a new IP address base on a given IP address plus a number to
292 * increase.
293 *
294 * @param ipAddress the IP address to increase
295 * @param num the number for ipAddress to add
296 * @return the new IP address after increase
297 */
298 private IpAddress increaseIpAddress(IpAddress ipAddress, int num) {
299 if (ipAddress.isIp6()) {
300 log.info("vBNG currently does not handle IPv6");
301 return null;
302 }
303 return IpAddress.valueOf(ipAddress.getIp4Address().toInt() + num);
304 }
305
306 /**
307 * Updates the IP prefix status in the local public IP prefix table.
308 *
309 * @param ipPprefix the IP prefix to update
310 * @param b the new value for the IP prefix
311 */
312 private void updateIpPrefixStatus(IpPrefix ipPprefix, boolean b) {
313 localPublicIpPrefixes.replace(ipPprefix, b);
314 }
315
316}