d27d6904f08e2e1f877452622cd8bfef5fef00a6
[onosfw.git] /
1 /*
2  * Copyright 2015 Open Networking Laboratory
3  *
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  */
16 package org.onosproject.virtualbng;
17
18 import com.fasterxml.jackson.databind.ObjectMapper;
19
20 import java.io.File;
21 import java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.util.Collections;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.concurrent.ConcurrentHashMap;
28
29 import org.apache.felix.scr.annotations.Activate;
30 import org.apache.felix.scr.annotations.Component;
31 import org.apache.felix.scr.annotations.Deactivate;
32 import org.apache.felix.scr.annotations.Service;
33 import org.onlab.packet.IpAddress;
34 import org.onlab.packet.IpPrefix;
35 import org.onlab.packet.MacAddress;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * Implementation of ConfigurationService which reads virtual BNG
41  * configuration from a file.
42  */
43 @Component(immediate = true)
44 @Service
45 public 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;
63     private MacAddress macOfPublicIpAddresses;
64     private IpAddress xosIpAddress;
65     private int xosRestPort;
66
67     @Activate
68     public void activate() {
69         readConfiguration();
70         log.info("vBNG configuration service started");
71     }
72
73     @Deactivate
74     public void deactivate() {
75         log.info("vBNG configuration service stopped");
76     }
77
78     /**
79      * Instructs the configuration reader to read the configuration from the
80      * file.
81      */
82     public void readConfiguration() {
83         readConfiguration(configFileName);
84     }
85
86     /**
87      * Reads virtual BNG information contained in configuration file.
88      *
89      * @param configFilename the name of the configuration file for the virtual
90      * BNG application
91      */
92     private void readConfiguration(String configFilename) {
93         File configFile = new File(CONFIG_DIR, configFilename);
94         ObjectMapper mapper = new ObjectMapper();
95
96         try {
97             log.info("Loading config: {}", configFile.getAbsolutePath());
98             VbngConfiguration config = mapper.readValue(configFile,
99                                                     VbngConfiguration.class);
100             for (IpPrefix prefix : config.getLocalPublicIpPrefixes()) {
101                 localPublicIpPrefixes.put(prefix, true);
102             }
103             nextHopIpAddress = config.getNextHopIpAddress();
104             macOfPublicIpAddresses = config.getPublicFacingMac();
105             xosIpAddress = config.getXosIpAddress();
106             xosRestPort = config.getXosRestPort();
107
108         } catch (FileNotFoundException e) {
109             log.warn("Configuration file not found: {}", configFileName);
110         } catch (IOException e) {
111             log.error("Error loading configuration", e);
112         }
113     }
114
115     @Override
116     public IpAddress getNextHopIpAddress() {
117         return nextHopIpAddress;
118     }
119
120     @Override
121     public MacAddress getPublicFacingMac() {
122         return macOfPublicIpAddresses;
123     }
124
125     @Override
126     public IpAddress getXosIpAddress() {
127         return xosIpAddress;
128     }
129
130     @Override
131     public int getXosRestPort() {
132         return xosRestPort;
133     }
134
135     // TODO handle the case: the number of public IP addresses is not enough
136     // for 1:1 mapping from public IP to private IP.
137     @Override
138     public synchronized IpAddress getAvailablePublicIpAddress(IpAddress
139                                                            privateIpAddress) {
140         // If there is already a mapping entry for the private IP address,
141         // then fetch the public IP address in the mapping entry and return it.
142         IpAddress publicIpAddress = ipAddressMap.get(privateIpAddress);
143         if (publicIpAddress != null) {
144             return publicIpAddress;
145         }
146         // There is no mapping for the private IP address.
147         Iterator<Entry<IpPrefix, Boolean>> prefixes =
148                 localPublicIpPrefixes.entrySet().iterator();
149         while (prefixes.hasNext()) {
150             Entry<IpPrefix, Boolean> prefix = prefixes.next();
151             if (!prefix.getValue()) {
152                 continue;
153             }
154
155             if (prefix.getKey().prefixLength() == 32) {
156                 updateIpPrefixStatus(prefix.getKey(), false);
157                 publicIpAddress = prefix.getKey().address();
158                 ipAddressMap.put(privateIpAddress, publicIpAddress);
159                 return publicIpAddress;
160             }
161
162             int prefixLen = prefix.getKey().prefixLength();
163             int availableIpNum = (int) Math.pow(2,
164                     IpPrefix.MAX_INET_MASK_LENGTH - prefixLen) - 1;
165             for (int i = 1; i <= availableIpNum; i++) {
166                 publicIpAddress =
167                         increaseIpAddress(prefix.getKey().address(), i);
168                 if (publicIpAddress == null) {
169                     return null;
170                 }
171                 if (ipAddressMap.values().contains(publicIpAddress)) {
172                     continue;
173                 } else if (i == availableIpNum) {
174                     // All the IP addresses are assigned out
175                     // Update this IP prefix status to false
176                     // Note: in this version we do not consider the
177                     // IP recycling issue.
178                     updateIpPrefixStatus(prefix.getKey(), false);
179                     ipAddressMap.put(privateIpAddress, publicIpAddress);
180                     return publicIpAddress;
181                 } else {
182                     ipAddressMap.put(privateIpAddress, publicIpAddress);
183                     return publicIpAddress;
184                 }
185             }
186         }
187         return null;
188     }
189
190     @Override
191     public IpAddress getAssignedPublicIpAddress(IpAddress privateIpAddress) {
192         return ipAddressMap.get(privateIpAddress);
193     }
194
195     @Override
196     public boolean isAssignedPublicIpAddress(IpAddress ipAddress) {
197         return ipAddressMap.containsValue(ipAddress);
198     }
199
200     @Override
201     public synchronized IpAddress recycleAssignedPublicIpAddress(IpAddress
202                                                     privateIpAddress) {
203         IpAddress publicIpAddress = ipAddressMap.remove(privateIpAddress);
204         if (publicIpAddress == null) {
205             return null;
206         }
207
208         Iterator<Entry<IpPrefix, Boolean>> prefixes =
209                 localPublicIpPrefixes.entrySet().iterator();
210         while (prefixes.hasNext()) {
211             Entry<IpPrefix, Boolean> prefixEntry = prefixes.next();
212             if (prefixEntry.getKey().contains(publicIpAddress)
213                     && !prefixEntry.getValue()) {
214                 updateIpPrefixStatus(prefixEntry.getKey(), true);
215             }
216         }
217         log.info("[DELETE] Private IP to Public IP mapping: {} --> {}",
218                  privateIpAddress, publicIpAddress);
219         return publicIpAddress;
220     }
221
222     @Override
223     public Map<IpAddress, IpAddress> getIpAddressMappings() {
224         return Collections.unmodifiableMap(ipAddressMap);
225     }
226
227     @Override
228     public synchronized boolean assignSpecifiedPublicIp(IpAddress publicIpAddress,
229                                   IpAddress privateIpAddress) {
230
231         // Judge whether this public IP address is in our public IP
232         // prefix/address list.
233         boolean isPublicIpExist = false;
234         for (Entry<IpPrefix, Boolean> prefix: localPublicIpPrefixes.entrySet()) {
235             if (prefix.getKey().contains(publicIpAddress)) {
236                 isPublicIpExist = true;
237
238                 // Judge whether this public IP address is already assigned
239                 if (!prefix.getValue() ||
240                         isAssignedPublicIpAddress(publicIpAddress)) {
241                     log.info("The public IP address {} is already assigned, "
242                             + "and not available.", publicIpAddress);
243                     return false;
244                 }
245
246                 // The public IP address is still available
247                 // Store the mapping from private IP address to public IP address
248                 ipAddressMap.put(privateIpAddress, publicIpAddress);
249
250                 // Update the prefix status
251                 if (prefix.getKey().prefixLength() == 32) {
252                     updateIpPrefixStatus(prefix.getKey(), false);
253                     return true;
254                 }
255
256                 // Judge whether the prefix of this public IP address is used
257                 // up, if so, update the IP prefix status.
258                 int prefixLen = prefix.getKey().prefixLength();
259                 int availableIpNum = (int) Math.pow(2,
260                         IpPrefix.MAX_INET_MASK_LENGTH - prefixLen) - 1;
261                 int usedIpNum = 0;
262                 for (Entry<IpAddress, IpAddress> ipAddressMapEntry:
263                     ipAddressMap.entrySet()) {
264                     if (prefix.getKey().contains(ipAddressMapEntry.getValue())) {
265                         usedIpNum = usedIpNum + 1;
266                     }
267                 }
268                 if (usedIpNum == availableIpNum) {
269                     updateIpPrefixStatus(prefix.getKey(), false);
270                 }
271
272                 return true;
273             }
274         }
275         if (!isPublicIpExist) {
276             log.info("The public IP address {} retrieved from XOS mapping does "
277                     + "not exist", publicIpAddress);
278         }
279         return false;
280     }
281
282     /**
283      * Generates a new IP address base on a given IP address plus a number to
284      * increase.
285      *
286      * @param ipAddress the IP address to increase
287      * @param num the number for ipAddress to add
288      * @return the new IP address after increase
289      */
290     private IpAddress increaseIpAddress(IpAddress ipAddress, int num) {
291         if (ipAddress.isIp6()) {
292             log.info("vBNG currently does not handle IPv6");
293             return null;
294         }
295         return IpAddress.valueOf(ipAddress.getIp4Address().toInt() + num);
296     }
297
298     /**
299      * Updates the IP prefix status in the local public IP prefix table.
300      *
301      * @param ipPprefix the IP prefix to update
302      * @param b the new value for the IP prefix
303      */
304     private void updateIpPrefixStatus(IpPrefix ipPprefix, boolean b) {
305             localPublicIpPrefixes.replace(ipPprefix, b);
306     }
307
308 }