blob: 775cff4cc391b37f310a7e8787d1a0cffbe61362 [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 Lindead2052015-06-08 16:07:23 -070020import com.google.common.collect.Maps;
Pingping Lin4e0c73d2015-05-06 15:41:10 -070021
Pingping Linffa27d32015-04-30 14:41:03 -070022import java.util.Map;
Pingping Lindead2052015-06-08 16:07:23 -070023import java.util.Map.Entry;
Pingping Linffa27d32015-04-30 14:41:03 -070024import java.util.concurrent.ConcurrentHashMap;
25
26import org.apache.felix.scr.annotations.Activate;
27import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Deactivate;
29import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
31import org.apache.felix.scr.annotations.Service;
32import org.onlab.packet.Ethernet;
33import org.onlab.packet.IpAddress;
34import org.onlab.packet.IpPrefix;
35import org.onlab.packet.MacAddress;
36import org.onosproject.core.ApplicationId;
37import org.onosproject.core.CoreService;
38import org.onosproject.net.ConnectPoint;
Pingping Lindead2052015-06-08 16:07:23 -070039import org.onosproject.net.DeviceId;
Pingping Linffa27d32015-04-30 14:41:03 -070040import org.onosproject.net.Host;
Pingping Lindead2052015-06-08 16:07:23 -070041import org.onosproject.net.PortNumber;
Pingping Linffa27d32015-04-30 14:41:03 -070042import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
44import org.onosproject.net.flow.TrafficSelector;
45import org.onosproject.net.flow.TrafficTreatment;
Pingping Lin4e0c73d2015-05-06 15:41:10 -070046import org.onosproject.net.host.HostEvent;
47import org.onosproject.net.host.HostListener;
Pingping Linffa27d32015-04-30 14:41:03 -070048import org.onosproject.net.host.HostService;
49import org.onosproject.net.intent.IntentService;
50import org.onosproject.net.intent.Key;
51import org.onosproject.net.intent.PointToPointIntent;
52import org.slf4j.Logger;
53import org.slf4j.LoggerFactory;
54
55/**
56 * This is a virtual Broadband Network Gateway (BNG) application. It mainly
57 * has 3 functions:
58 * (1) assigns and replies a public IP address to a REST request with a private
59 * IP address
60 * (2) maintains the mapping from the private IP address to the public IP address
61 * (3) installs point to point intents for the host configured with private IP
62 * address to access Internet
63 */
64@Component(immediate = true)
65@Service
66public class VbngManager implements VbngService {
67
68 private static final String APP_NAME = "org.onosproject.virtualbng";
69
70 private final Logger log = LoggerFactory.getLogger(getClass());
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected CoreService coreService;
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected HostService hostService;
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected IntentService intentService;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected VbngConfigurationService vbngConfigurationService;
83
84 private ApplicationId appId;
85 private Map<IpAddress, PointToPointIntent> p2pIntentsFromHost;
86 private Map<IpAddress, PointToPointIntent> p2pIntentsToHost;
87
Pingping Lindead2052015-06-08 16:07:23 -070088 // This map stores the mapping from the private IP addresses to VcpeHost.
89 // The IP addresses in this map are all the private IP addresses we failed
90 // to create vBNGs due to the next hop host was not in ONOS.
91 private Map<IpAddress, VcpeHost> privateIpAddressMap;
92
93 // Store the mapping from hostname to connect point
94 private Map<String, ConnectPoint> nodeToPort;
Pingping Lin4e0c73d2015-05-06 15:41:10 -070095
96 private HostListener hostListener;
97 private IpAddress nextHopIpAddress;
98
Pingping Lindead2052015-06-08 16:07:23 -070099 private static final DeviceId FABRIC_DEVICE_ID =
100 DeviceId.deviceId("of:8f0e486e73000187");
101
Pingping Linffa27d32015-04-30 14:41:03 -0700102 @Activate
103 public void activate() {
104 appId = coreService.registerApplication(APP_NAME);
105 p2pIntentsFromHost = new ConcurrentHashMap<>();
106 p2pIntentsToHost = new ConcurrentHashMap<>();
Pingping Lindead2052015-06-08 16:07:23 -0700107 privateIpAddressMap = new ConcurrentHashMap<>();
108
109 setupMap();
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700110
111 nextHopIpAddress = vbngConfigurationService.getNextHopIpAddress();
112 hostListener = new InternalHostListener();
113 hostService.addListener(hostListener);
114
Pingping Linffa27d32015-04-30 14:41:03 -0700115 log.info("vBNG Started");
116 }
117
118 @Deactivate
119 public void deactivate() {
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700120 hostService.removeListener(hostListener);
Pingping Linffa27d32015-04-30 14:41:03 -0700121 log.info("vBNG Stopped");
122 }
123
Pingping Lindead2052015-06-08 16:07:23 -0700124 /**
125 * Sets up mapping from hostname to connect point.
126 */
127 private void setupMap() {
128 nodeToPort = Maps.newHashMap();
129
130 nodeToPort.put("cordcompute01.onlab.us",
131 new ConnectPoint(FABRIC_DEVICE_ID,
132 PortNumber.portNumber(48)));
133
134 nodeToPort.put("cordcompute02.onlab.us",
135 new ConnectPoint(FABRIC_DEVICE_ID,
136 PortNumber.portNumber(47)));
137 }
138
Pingping Linffa27d32015-04-30 14:41:03 -0700139 @Override
Pingping Lindead2052015-06-08 16:07:23 -0700140 public IpAddress createVbng(IpAddress privateIpAddress,
141 MacAddress hostMacAddress,
142 String hostName) {
Pingping Linffa27d32015-04-30 14:41:03 -0700143
Pingping Linffa27d32015-04-30 14:41:03 -0700144 IpAddress publicIpAddress =
145 vbngConfigurationService.getAvailablePublicIpAddress(
146 privateIpAddress);
147 if (publicIpAddress == null) {
148 log.info("Did not find an available public IP address to use.");
149 return null;
150 }
Pingping Linde77ee52015-06-03 17:16:07 -0700151 log.info("[ADD] Private IP to Public IP mapping: {} --> {}",
Pingping Linffa27d32015-04-30 14:41:03 -0700152 privateIpAddress, publicIpAddress);
153
154 // Setup paths between the host configured with private IP and
155 // next hop
Pingping Lindead2052015-06-08 16:07:23 -0700156 if (!setupForwardingPaths(privateIpAddress, publicIpAddress,
157 hostMacAddress, hostName)) {
158 privateIpAddressMap.put(privateIpAddress,
159 new VcpeHost(hostMacAddress, hostName));
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700160 }
Pingping Linffa27d32015-04-30 14:41:03 -0700161 return publicIpAddress;
162 }
163
Pingping Lind2afaf22015-06-02 10:46:29 -0700164 @Override
165 public IpAddress deleteVbng(IpAddress privateIpAddress) {
166 // Recycle the public IP address assigned to this private IP address.
167 // Recycling will also delete the mapping entry from the private IP
168 // address to public IP address.
169 IpAddress assignedPublicIpAddress = vbngConfigurationService
170 .recycleAssignedPublicIpAddress(privateIpAddress);
171 if (assignedPublicIpAddress == null) {
172 return null;
173 }
174
Pingping Lindead2052015-06-08 16:07:23 -0700175 // Remove the private IP address from privateIpAddressMap
176 privateIpAddressMap.remove(privateIpAddress);
Pingping Lind2afaf22015-06-02 10:46:29 -0700177
178 // Remove intents
179 removeForwardingPaths(privateIpAddress);
180
181 return assignedPublicIpAddress;
182 }
183
184 /**
185 * Removes the forwarding paths in both two directions between host
186 * configured with private IP and next hop.
187 *
188 * @param privateIp the private IP address of a local host
189 */
190 private void removeForwardingPaths(IpAddress privateIp) {
191 PointToPointIntent toNextHopIntent =
192 p2pIntentsFromHost.remove(privateIp);
193 if (toNextHopIntent != null) {
194 intentService.withdraw(toNextHopIntent);
195 //intentService.purge(toNextHopIntent);
196 }
197 PointToPointIntent toLocalHostIntent =
198 p2pIntentsToHost.remove(privateIp);
199 if (toLocalHostIntent != null) {
200 intentService.withdraw(toLocalHostIntent);
201 //intentService.purge(toLocalHostIntent);
202 }
203 }
204
Pingping Linffa27d32015-04-30 14:41:03 -0700205 /**
206 * Sets up forwarding paths in both two directions between host configured
207 * with private IP and next hop.
208 *
209 * @param privateIp the private IP address of a local host
210 * @param publicIp the public IP address assigned for the private IP address
Pingping Lindead2052015-06-08 16:07:23 -0700211 * @param hostMacAddress the MAC address for the IP address
212 * @param hostName the host name for the IP address
Pingping Linffa27d32015-04-30 14:41:03 -0700213 */
Pingping Lindead2052015-06-08 16:07:23 -0700214 private boolean setupForwardingPaths(IpAddress privateIp,
215 IpAddress publicIp,
216 MacAddress hostMacAddress,
217 String hostName) {
Pingping Linffa27d32015-04-30 14:41:03 -0700218 checkNotNull(privateIp);
219 checkNotNull(publicIp);
Pingping Lindead2052015-06-08 16:07:23 -0700220 checkNotNull(hostMacAddress);
221 checkNotNull(hostName);
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700222
223 if (nextHopIpAddress == null) {
224 log.warn("Did not find next hop IP address");
225 return false;
226 }
Pingping Linffa27d32015-04-30 14:41:03 -0700227
228 // If there are already intents for private IP address in the system,
229 // we will do nothing and directly return.
230 if (p2pIntentsFromHost.containsKey(privateIp)
231 && p2pIntentsToHost.containsKey(privateIp)) {
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700232 return true;
Pingping Linffa27d32015-04-30 14:41:03 -0700233 }
234
Pingping Linffa27d32015-04-30 14:41:03 -0700235 Host nextHopHost = null;
236 if (!hostService.getHostsByIp(nextHopIpAddress).isEmpty()) {
237 nextHopHost = hostService.getHostsByIp(nextHopIpAddress)
238 .iterator().next();
239 } else {
Pingping Linffa27d32015-04-30 14:41:03 -0700240 hostService.startMonitoringIp(nextHopIpAddress);
241 if (hostService.getHostsByIp(privateIp).isEmpty()) {
242 hostService.startMonitoringIp(privateIp);
243 }
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700244 return false;
Pingping Linffa27d32015-04-30 14:41:03 -0700245 }
246
Pingping Linffa27d32015-04-30 14:41:03 -0700247 ConnectPoint nextHopConnectPoint =
248 new ConnectPoint(nextHopHost.location().elementId(),
249 nextHopHost.location().port());
Pingping Lindead2052015-06-08 16:07:23 -0700250 ConnectPoint localHostConnectPoint = nodeToPort.get(hostName);
Pingping Linffa27d32015-04-30 14:41:03 -0700251
252 // Generate and install intent for traffic from host configured with
253 // private IP
254 if (!p2pIntentsFromHost.containsKey(privateIp)) {
255 PointToPointIntent toNextHopIntent
256 = srcMatchIntentGenerator(privateIp,
257 publicIp,
258 nextHopHost.mac(),
259 nextHopConnectPoint,
260 localHostConnectPoint
261 );
262 p2pIntentsFromHost.put(privateIp, toNextHopIntent);
263 intentService.submit(toNextHopIntent);
264 }
265
266 // Generate and install intent for traffic to host configured with
267 // private IP
268 if (!p2pIntentsToHost.containsKey(privateIp)) {
269 PointToPointIntent toLocalHostIntent
270 = dstMatchIntentGenerator(publicIp,
271 privateIp,
Pingping Lindead2052015-06-08 16:07:23 -0700272 hostMacAddress,
Pingping Linffa27d32015-04-30 14:41:03 -0700273 localHostConnectPoint,
274 nextHopConnectPoint);
Pingping Lind2afaf22015-06-02 10:46:29 -0700275 p2pIntentsToHost.put(privateIp, toLocalHostIntent);
Pingping Linffa27d32015-04-30 14:41:03 -0700276 intentService.submit(toLocalHostIntent);
277 }
278
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700279 return true;
280 }
281
282 /**
283 * Listener for host events.
284 */
285 private class InternalHostListener implements HostListener {
286 @Override
287 public void event(HostEvent event) {
288 log.debug("Received HostEvent {}", event);
289
290 Host host = event.subject();
291 if (event.type() != HostEvent.Type.HOST_ADDED) {
292 return;
293 }
294
295 for (IpAddress ipAddress: host.ipAddresses()) {
Pingping Lindead2052015-06-08 16:07:23 -0700296 // The POST method from XOS gives us MAC and host name, so we
297 // do not need to do anything after receive a vCPE host event
298 // for now.
299 /*if (privateIpAddressSet.contains(ipAddress)) {
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700300 createVbngAgain(ipAddress);
Pingping Lindead2052015-06-08 16:07:23 -0700301 }*/
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700302
303 if (nextHopIpAddress != null &&
304 ipAddress.equals(nextHopIpAddress)) {
Pingping Lindead2052015-06-08 16:07:23 -0700305
306 for (Entry<IpAddress, VcpeHost> entry:
307 privateIpAddressMap.entrySet()) {
308 createVbngAgain(entry.getKey());
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700309 }
Pingping Lindead2052015-06-08 16:07:23 -0700310
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700311 }
312 }
313 }
314 }
315
316 /**
317 * Tries to create vBNG again after receiving a host event if the IP
Pingping Lindead2052015-06-08 16:07:23 -0700318 * address of the host is the next hop IP address.
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700319 *
320 * @param privateIpAddress the private IP address
321 */
322 private void createVbngAgain(IpAddress privateIpAddress) {
323 IpAddress publicIpAddress = vbngConfigurationService
324 .getAssignedPublicIpAddress(privateIpAddress);
325 if (publicIpAddress == null) {
326 // We only need to handle the private IP addresses for which we
327 // already returned the REST replies with assigned public IP
328 // addresses. If a private IP addresses does not have an assigned
329 // public IP address, we should not get it an available public IP
330 // address here, and we should delete it in the unhandled private
Pingping Lindead2052015-06-08 16:07:23 -0700331 // IP address map.
332 privateIpAddressMap.remove(privateIpAddress);
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700333 return;
334 }
Pingping Lindead2052015-06-08 16:07:23 -0700335 VcpeHost vcpeHost = privateIpAddressMap.get(privateIpAddress);
336 if (setupForwardingPaths(privateIpAddress, publicIpAddress,
337 vcpeHost.macAddress, vcpeHost.hostName)) {
338 privateIpAddressMap.remove(privateIpAddress);
Pingping Lin4e0c73d2015-05-06 15:41:10 -0700339 }
Pingping Linffa27d32015-04-30 14:41:03 -0700340 }
341
342 /**
343 * PointToPointIntent Generator.
344 * <p>
345 * The intent will match the source IP address in packet, rewrite the
346 * source IP address, and rewrite the destination MAC address.
347 * </p>
348 *
349 * @param srcIpAddress the source IP address in packet to match
350 * @param newSrcIpAddress the new source IP address to set
351 * @param dstMacAddress the destination MAC address to set
352 * @param dstConnectPoint the egress point
353 * @param srcConnectPoint the ingress point
354 * @return a PointToPointIntent
355 */
356 private PointToPointIntent srcMatchIntentGenerator(
357 IpAddress srcIpAddress,
358 IpAddress newSrcIpAddress,
359 MacAddress dstMacAddress,
360 ConnectPoint dstConnectPoint,
361 ConnectPoint srcConnectPoint) {
362 checkNotNull(srcIpAddress);
363 checkNotNull(newSrcIpAddress);
364 checkNotNull(dstMacAddress);
365 checkNotNull(dstConnectPoint);
366 checkNotNull(srcConnectPoint);
367
368 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
369 selector.matchEthType(Ethernet.TYPE_IPV4);
370 selector.matchIPSrc(IpPrefix.valueOf(srcIpAddress,
371 IpPrefix.MAX_INET_MASK_LENGTH));
372
373 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
374 treatment.setEthDst(dstMacAddress);
375 treatment.setIpSrc(newSrcIpAddress);
376
377 Key key = Key.of(srcIpAddress.toString() + "MatchSrc", appId);
378 PointToPointIntent intent = PointToPointIntent.builder()
379 .appId(appId)
380 .key(key)
381 .selector(selector.build())
382 .treatment(treatment.build())
383 .egressPoint(dstConnectPoint)
384 .ingressPoint(srcConnectPoint)
385 .build();
386
387 log.info("Generated a PointToPointIntent for traffic from local host "
388 + ": {}", intent);
389 return intent;
390 }
391
392 /**
393 * PointToPointIntent Generator.
394 * <p>
395 * The intent will match the destination IP address in packet, rewrite the
396 * destination IP address, and rewrite the destination MAC address.
397 * </p>
398 *
399 * @param dstIpAddress the destination IP address in packet to match
400 * @param newDstIpAddress the new destination IP address to set
401 * @param dstMacAddress the destination MAC address to set
402 * @param dstConnectPoint the egress point
403 * @param srcConnectPoint the ingress point
404 * @return a PointToPointIntent
405 */
406 private PointToPointIntent dstMatchIntentGenerator(
407 IpAddress dstIpAddress,
408 IpAddress newDstIpAddress,
409 MacAddress dstMacAddress,
410 ConnectPoint dstConnectPoint,
411 ConnectPoint srcConnectPoint) {
412 checkNotNull(dstIpAddress);
413 checkNotNull(newDstIpAddress);
414 checkNotNull(dstMacAddress);
415 checkNotNull(dstConnectPoint);
416 checkNotNull(srcConnectPoint);
417
418 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
419 selector.matchEthType(Ethernet.TYPE_IPV4);
420 selector.matchIPDst(IpPrefix.valueOf(dstIpAddress,
421 IpPrefix.MAX_INET_MASK_LENGTH));
422
423 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
424 treatment.setEthDst(dstMacAddress);
425 treatment.setIpDst(newDstIpAddress);
426
427 Key key = Key.of(newDstIpAddress.toString() + "MatchDst", appId);
428 PointToPointIntent intent = PointToPointIntent.builder()
429 .appId(appId)
430 .key(key)
431 .selector(selector.build())
432 .treatment(treatment.build())
433 .egressPoint(dstConnectPoint)
434 .ingressPoint(srcConnectPoint)
435 .build();
436 log.info("Generated a PointToPointIntent for traffic to local host "
437 + ": {}", intent);
438
439 return intent;
440 }
Pingping Lindead2052015-06-08 16:07:23 -0700441
442 /**
443 * Constructor to store the a vCPE host info.
444 */
445 private class VcpeHost {
446 MacAddress macAddress;
447 String hostName;
448 public VcpeHost(MacAddress macAddress, String hostName) {
449 this.macAddress = macAddress;
450 this.hostName = hostName;
451 }
452 }
Pingping Linffa27d32015-04-30 14:41:03 -0700453}