blob: f440c5d53037ed08d3bea1367502685e7c30f5d5 [file] [log] [blame]
Pingping Linffa27d32015-04-30 14:41:03 -07001/*
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 */
16package org.onosproject.virtualbng;
17
18import static com.google.common.base.Preconditions.checkNotNull;
19
Pingping Lin4e0c73d2015-05-06 15:41:10 -070020import com.google.common.collect.Sets;
21
22import java.util.Iterator;
Pingping Linffa27d32015-04-30 14:41:03 -070023import java.util.Map;
Pingping Lin4e0c73d2015-05-06 15:41:10 -070024import java.util.Set;
Pingping Linffa27d32015-04-30 14:41:03 -070025import java.util.concurrent.ConcurrentHashMap;
26
27import org.apache.felix.scr.annotations.Activate;
28import org.apache.felix.scr.annotations.Component;
29import org.apache.felix.scr.annotations.Deactivate;
30import org.apache.felix.scr.annotations.Reference;
31import org.apache.felix.scr.annotations.ReferenceCardinality;
32import org.apache.felix.scr.annotations.Service;
33import org.onlab.packet.Ethernet;
34import org.onlab.packet.IpAddress;
35import org.onlab.packet.IpPrefix;
36import org.onlab.packet.MacAddress;
37import org.onosproject.core.ApplicationId;
38import org.onosproject.core.CoreService;
39import org.onosproject.net.ConnectPoint;
40import org.onosproject.net.Host;
41import org.onosproject.net.flow.DefaultTrafficSelector;
42import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.TrafficSelector;
44import org.onosproject.net.flow.TrafficTreatment;
Pingping Lin4e0c73d2015-05-06 15:41:10 -070045import org.onosproject.net.host.HostEvent;
46import org.onosproject.net.host.HostListener;
Pingping Linffa27d32015-04-30 14:41:03 -070047import org.onosproject.net.host.HostService;
48import org.onosproject.net.intent.IntentService;
49import org.onosproject.net.intent.Key;
50import org.onosproject.net.intent.PointToPointIntent;
51import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
54/**
55 * This is a virtual Broadband Network Gateway (BNG) application. It mainly
56 * has 3 functions:
57 * (1) assigns and replies a public IP address to a REST request with a private
58 * IP address
59 * (2) maintains the mapping from the private IP address to the public IP address
60 * (3) installs point to point intents for the host configured with private IP
61 * address to access Internet
62 */
63@Component(immediate = true)
64@Service
65public class VbngManager implements VbngService {
66
67 private static final String APP_NAME = "org.onosproject.virtualbng";
68
69 private final Logger log = LoggerFactory.getLogger(getClass());
70
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected CoreService coreService;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected HostService hostService;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected IntentService intentService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected VbngConfigurationService vbngConfigurationService;
82
83 private ApplicationId appId;
84 private Map<IpAddress, PointToPointIntent> p2pIntentsFromHost;
85 private Map<IpAddress, PointToPointIntent> p2pIntentsToHost;
86
Pingping Lin4e0c73d2015-05-06 15:41:10 -070087 // This set stores all the private IP addresses we failed to create vBNGs
88 // for the first time.
89 private Set<IpAddress> privateIpAddressSet;
90
91 private HostListener hostListener;
92 private IpAddress nextHopIpAddress;
93
94
Pingping Linffa27d32015-04-30 14:41:03 -070095 @Activate
96 public void activate() {
97 appId = coreService.registerApplication(APP_NAME);
98 p2pIntentsFromHost = new ConcurrentHashMap<>();
99 p2pIntentsToHost = new ConcurrentHashMap<>();
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700100 privateIpAddressSet = Sets.newConcurrentHashSet();
101
102 nextHopIpAddress = vbngConfigurationService.getNextHopIpAddress();
103 hostListener = new InternalHostListener();
104 hostService.addListener(hostListener);
105
Pingping Linffa27d32015-04-30 14:41:03 -0700106 log.info("vBNG Started");
107 }
108
109 @Deactivate
110 public void deactivate() {
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700111 hostService.removeListener(hostListener);
Pingping Linffa27d32015-04-30 14:41:03 -0700112 log.info("vBNG Stopped");
113 }
114
115 @Override
116 public IpAddress createVbng(IpAddress privateIpAddress) {
117
Pingping Linffa27d32015-04-30 14:41:03 -0700118 IpAddress publicIpAddress =
119 vbngConfigurationService.getAvailablePublicIpAddress(
120 privateIpAddress);
121 if (publicIpAddress == null) {
122 log.info("Did not find an available public IP address to use.");
123 return null;
124 }
125 log.info("Private IP to Public IP mapping: {} --> {}",
126 privateIpAddress, publicIpAddress);
127
128 // Setup paths between the host configured with private IP and
129 // next hop
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700130 if (!setupForwardingPaths(privateIpAddress, publicIpAddress)) {
131 privateIpAddressSet.add(privateIpAddress);
132 }
Pingping Linffa27d32015-04-30 14:41:03 -0700133 return publicIpAddress;
134 }
135
136 /**
137 * Sets up forwarding paths in both two directions between host configured
138 * with private IP and next hop.
139 *
140 * @param privateIp the private IP address of a local host
141 * @param publicIp the public IP address assigned for the private IP address
Pingping Linffa27d32015-04-30 14:41:03 -0700142 */
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700143 private boolean setupForwardingPaths(IpAddress privateIp, IpAddress publicIp) {
Pingping Linffa27d32015-04-30 14:41:03 -0700144 checkNotNull(privateIp);
145 checkNotNull(publicIp);
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700146
147 if (nextHopIpAddress == null) {
148 log.warn("Did not find next hop IP address");
149 return false;
150 }
Pingping Linffa27d32015-04-30 14:41:03 -0700151
152 // If there are already intents for private IP address in the system,
153 // we will do nothing and directly return.
154 if (p2pIntentsFromHost.containsKey(privateIp)
155 && p2pIntentsToHost.containsKey(privateIp)) {
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700156 return true;
Pingping Linffa27d32015-04-30 14:41:03 -0700157 }
158
159 Host localHost = null;
160 Host nextHopHost = null;
161 if (!hostService.getHostsByIp(nextHopIpAddress).isEmpty()) {
162 nextHopHost = hostService.getHostsByIp(nextHopIpAddress)
163 .iterator().next();
164 } else {
Pingping Linffa27d32015-04-30 14:41:03 -0700165 hostService.startMonitoringIp(nextHopIpAddress);
166 if (hostService.getHostsByIp(privateIp).isEmpty()) {
167 hostService.startMonitoringIp(privateIp);
168 }
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700169 return false;
Pingping Linffa27d32015-04-30 14:41:03 -0700170 }
171
172 if (!hostService.getHostsByIp(privateIp).isEmpty()) {
173 localHost =
174 hostService.getHostsByIp(privateIp).iterator().next();
175 } else {
Pingping Linffa27d32015-04-30 14:41:03 -0700176 hostService.startMonitoringIp(privateIp);
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700177 return false;
Pingping Linffa27d32015-04-30 14:41:03 -0700178 }
179
180 ConnectPoint nextHopConnectPoint =
181 new ConnectPoint(nextHopHost.location().elementId(),
182 nextHopHost.location().port());
183 ConnectPoint localHostConnectPoint =
184 new ConnectPoint(localHost.location().elementId(),
185 localHost.location().port());
186
187 // Generate and install intent for traffic from host configured with
188 // private IP
189 if (!p2pIntentsFromHost.containsKey(privateIp)) {
190 PointToPointIntent toNextHopIntent
191 = srcMatchIntentGenerator(privateIp,
192 publicIp,
193 nextHopHost.mac(),
194 nextHopConnectPoint,
195 localHostConnectPoint
196 );
197 p2pIntentsFromHost.put(privateIp, toNextHopIntent);
198 intentService.submit(toNextHopIntent);
199 }
200
201 // Generate and install intent for traffic to host configured with
202 // private IP
203 if (!p2pIntentsToHost.containsKey(privateIp)) {
204 PointToPointIntent toLocalHostIntent
205 = dstMatchIntentGenerator(publicIp,
206 privateIp,
207 localHost.mac(),
208 localHostConnectPoint,
209 nextHopConnectPoint);
210 p2pIntentsToHost.put(nextHopIpAddress, toLocalHostIntent);
211 intentService.submit(toLocalHostIntent);
212 }
213
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700214 return true;
215 }
216
217 /**
218 * Listener for host events.
219 */
220 private class InternalHostListener implements HostListener {
221 @Override
222 public void event(HostEvent event) {
223 log.debug("Received HostEvent {}", event);
224
225 Host host = event.subject();
226 if (event.type() != HostEvent.Type.HOST_ADDED) {
227 return;
228 }
229
230 for (IpAddress ipAddress: host.ipAddresses()) {
231 if (privateIpAddressSet.contains(ipAddress)) {
232 createVbngAgain(ipAddress);
233 }
234
235 if (nextHopIpAddress != null &&
236 ipAddress.equals(nextHopIpAddress)) {
237 Iterator<IpAddress> ipAddresses =
238 privateIpAddressSet.iterator();
239 while (ipAddresses.hasNext()) {
240 IpAddress privateIpAddress = ipAddresses.next();
241 createVbngAgain(privateIpAddress);
242 }
243 }
244 }
245 }
246 }
247
248 /**
249 * Tries to create vBNG again after receiving a host event if the IP
250 * address of the host is a private IP address or the next hop IP
251 * address.
252 *
253 * @param privateIpAddress the private IP address
254 */
255 private void createVbngAgain(IpAddress privateIpAddress) {
256 IpAddress publicIpAddress = vbngConfigurationService
257 .getAssignedPublicIpAddress(privateIpAddress);
258 if (publicIpAddress == null) {
259 // We only need to handle the private IP addresses for which we
260 // already returned the REST replies with assigned public IP
261 // addresses. If a private IP addresses does not have an assigned
262 // public IP address, we should not get it an available public IP
263 // address here, and we should delete it in the unhandled private
264 // IP address set.
265 privateIpAddressSet.remove(privateIpAddress);
266 return;
267 }
268 if (setupForwardingPaths(privateIpAddress, publicIpAddress)) {
269 // At this moment it is still possible to fail to create a vBNG,
270 // because creating a vBNG needs two hosts, one is the local host
271 // configured with private IP address, the other is the next hop
272 // host.
273 privateIpAddressSet.remove(privateIpAddress);
274 }
Pingping Linffa27d32015-04-30 14:41:03 -0700275 }
276
277 /**
278 * PointToPointIntent Generator.
279 * <p>
280 * The intent will match the source IP address in packet, rewrite the
281 * source IP address, and rewrite the destination MAC address.
282 * </p>
283 *
284 * @param srcIpAddress the source IP address in packet to match
285 * @param newSrcIpAddress the new source IP address to set
286 * @param dstMacAddress the destination MAC address to set
287 * @param dstConnectPoint the egress point
288 * @param srcConnectPoint the ingress point
289 * @return a PointToPointIntent
290 */
291 private PointToPointIntent srcMatchIntentGenerator(
292 IpAddress srcIpAddress,
293 IpAddress newSrcIpAddress,
294 MacAddress dstMacAddress,
295 ConnectPoint dstConnectPoint,
296 ConnectPoint srcConnectPoint) {
297 checkNotNull(srcIpAddress);
298 checkNotNull(newSrcIpAddress);
299 checkNotNull(dstMacAddress);
300 checkNotNull(dstConnectPoint);
301 checkNotNull(srcConnectPoint);
302
303 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
304 selector.matchEthType(Ethernet.TYPE_IPV4);
305 selector.matchIPSrc(IpPrefix.valueOf(srcIpAddress,
306 IpPrefix.MAX_INET_MASK_LENGTH));
307
308 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
309 treatment.setEthDst(dstMacAddress);
310 treatment.setIpSrc(newSrcIpAddress);
311
312 Key key = Key.of(srcIpAddress.toString() + "MatchSrc", appId);
313 PointToPointIntent intent = PointToPointIntent.builder()
314 .appId(appId)
315 .key(key)
316 .selector(selector.build())
317 .treatment(treatment.build())
318 .egressPoint(dstConnectPoint)
319 .ingressPoint(srcConnectPoint)
320 .build();
321
322 log.info("Generated a PointToPointIntent for traffic from local host "
323 + ": {}", intent);
324 return intent;
325 }
326
327 /**
328 * PointToPointIntent Generator.
329 * <p>
330 * The intent will match the destination IP address in packet, rewrite the
331 * destination IP address, and rewrite the destination MAC address.
332 * </p>
333 *
334 * @param dstIpAddress the destination IP address in packet to match
335 * @param newDstIpAddress the new destination IP address to set
336 * @param dstMacAddress the destination MAC address to set
337 * @param dstConnectPoint the egress point
338 * @param srcConnectPoint the ingress point
339 * @return a PointToPointIntent
340 */
341 private PointToPointIntent dstMatchIntentGenerator(
342 IpAddress dstIpAddress,
343 IpAddress newDstIpAddress,
344 MacAddress dstMacAddress,
345 ConnectPoint dstConnectPoint,
346 ConnectPoint srcConnectPoint) {
347 checkNotNull(dstIpAddress);
348 checkNotNull(newDstIpAddress);
349 checkNotNull(dstMacAddress);
350 checkNotNull(dstConnectPoint);
351 checkNotNull(srcConnectPoint);
352
353 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
354 selector.matchEthType(Ethernet.TYPE_IPV4);
355 selector.matchIPDst(IpPrefix.valueOf(dstIpAddress,
356 IpPrefix.MAX_INET_MASK_LENGTH));
357
358 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
359 treatment.setEthDst(dstMacAddress);
360 treatment.setIpDst(newDstIpAddress);
361
362 Key key = Key.of(newDstIpAddress.toString() + "MatchDst", appId);
363 PointToPointIntent intent = PointToPointIntent.builder()
364 .appId(appId)
365 .key(key)
366 .selector(selector.build())
367 .treatment(treatment.build())
368 .egressPoint(dstConnectPoint)
369 .ingressPoint(srcConnectPoint)
370 .build();
371 log.info("Generated a PointToPointIntent for traffic to local host "
372 + ": {}", intent);
373
374 return intent;
375 }
Pingping Linffa27d32015-04-30 14:41:03 -0700376}