blob: ac5f148ed711db62ffa7de3b3d39e486ecba4167 [file] [log] [blame]
Jonathan Hart9a426f82015-09-03 15:43:13 +02001/*
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
17package org.onosproject.reactive.routing;
18
Pingping Lin9a445c82016-04-07 11:40:29 -070019import static com.google.common.base.Preconditions.checkNotNull;
20
Jonathan Hart9a426f82015-09-03 15:43:13 +020021import com.google.common.collect.ImmutableList;
22import com.google.common.collect.Maps;
Pingping Lin9a445c82016-04-07 11:40:29 -070023
24import java.util.Collections;
25import java.util.HashSet;
26import java.util.Map;
27import java.util.Set;
28import java.util.stream.Collectors;
29
Jonathan Hart9a426f82015-09-03 15:43:13 +020030import org.onlab.packet.Ethernet;
31import org.onlab.packet.IpAddress;
32import org.onlab.packet.IpPrefix;
33import org.onlab.packet.MacAddress;
34import org.onlab.packet.VlanId;
35import org.onosproject.core.ApplicationId;
36import org.onosproject.incubator.net.intf.Interface;
37import org.onosproject.incubator.net.intf.InterfaceService;
38import org.onosproject.net.ConnectPoint;
39import org.onosproject.net.Host;
40import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.TrafficTreatment;
44import org.onosproject.net.host.HostService;
45import org.onosproject.net.intent.Constraint;
46import org.onosproject.net.intent.Key;
47import org.onosproject.net.intent.MultiPointToSinglePointIntent;
48import org.onosproject.net.intent.constraint.PartialFailureConstraint;
49import org.onosproject.routing.IntentRequestListener;
50import org.onosproject.routing.IntentSynchronizationService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020051import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
Jonathan Hart9a426f82015-09-03 15:43:13 +020054/**
55 * FIB component for reactive routing intents.
56 */
57public class ReactiveRoutingFib implements IntentRequestListener {
58
59 private static final int PRIORITY_OFFSET = 100;
60 private static final int PRIORITY_MULTIPLIER = 5;
61 protected static final ImmutableList<Constraint> CONSTRAINTS
62 = ImmutableList.of(new PartialFailureConstraint());
63
64 private final Logger log = LoggerFactory.getLogger(getClass());
65
66 private final ApplicationId appId;
67 private final HostService hostService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020068 private final InterfaceService interfaceService;
69 private final IntentSynchronizationService intentSynchronizer;
70
71 private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
72
73 /**
74 * Class constructor.
75 *
76 * @param appId application ID to use to generate intents
77 * @param hostService host service
Jonathan Hart9a426f82015-09-03 15:43:13 +020078 * @param interfaceService interface service
79 * @param intentSynchronizer intent synchronization service
80 */
81 public ReactiveRoutingFib(ApplicationId appId, HostService hostService,
Jonathan Hart9a426f82015-09-03 15:43:13 +020082 InterfaceService interfaceService,
83 IntentSynchronizationService intentSynchronizer) {
84 this.appId = appId;
85 this.hostService = hostService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020086 this.interfaceService = interfaceService;
87 this.intentSynchronizer = intentSynchronizer;
88
89 routeIntents = Maps.newConcurrentMap();
90 }
91
92 @Override
93 public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) {
94 checkNotNull(hostIpAddress);
Jonathan Hart9a426f82015-09-03 15:43:13 +020095
96 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
97
98 if (hostIpAddress.isIp4()) {
99 selector.matchEthType(Ethernet.TYPE_IPV4);
100 } else {
101 selector.matchEthType(Ethernet.TYPE_IPV6);
102 }
103
104 // Match the destination IP prefix at the first hop
105 IpPrefix ipPrefix = hostIpAddress.toIpPrefix();
106 selector.matchIPDst(ipPrefix);
107
108 // Rewrite the destination MAC address
109 MacAddress hostMac = null;
110 ConnectPoint egressPoint = null;
111 for (Host host : hostService.getHostsByIp(hostIpAddress)) {
112 if (host.mac() != null) {
113 hostMac = host.mac();
114 egressPoint = host.location();
115 break;
116 }
117 }
118 if (hostMac == null) {
119 hostService.startMonitoringIp(hostIpAddress);
120 return;
121 }
122
123 TrafficTreatment.Builder treatment =
124 DefaultTrafficTreatment.builder().setEthDst(hostMac);
125 Key key = Key.of(ipPrefix.toString(), appId);
126 int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER
127 + PRIORITY_OFFSET;
Pingping Lin9a445c82016-04-07 11:40:29 -0700128
129 Set<ConnectPoint> interfaceConnectPoints =
130 interfaceService.getInterfaces().stream()
131 .map(intf -> intf.connectPoint()).collect(Collectors.toSet());
132
133 if (interfaceConnectPoints.isEmpty()) {
134 log.error("The interface connect points are empty!");
135 return;
136 }
137
138 Set<ConnectPoint> ingressPoints = new HashSet<>();
139
140 for (ConnectPoint connectPoint : interfaceConnectPoints) {
141 if (!connectPoint.equals(egressPoint)) {
142 ingressPoints.add(connectPoint);
143 }
144 }
145
Jonathan Hart9a426f82015-09-03 15:43:13 +0200146 MultiPointToSinglePointIntent intent =
147 MultiPointToSinglePointIntent.builder()
148 .appId(appId)
149 .key(key)
150 .selector(selector.build())
151 .treatment(treatment.build())
152 .ingressPoints(ingressPoints)
153 .egressPoint(egressPoint)
154 .priority(priority)
155 .constraints(CONSTRAINTS)
156 .build();
157
158 log.trace("Generates ConnectivityInternetToHost intent {}", intent);
159 submitReactiveIntent(ipPrefix, intent);
160 }
161
162 @Override
163 public void setUpConnectivityHostToInternet(IpAddress hostIp, IpPrefix prefix,
164 IpAddress nextHopIpAddress) {
165 // Find the attachment point (egress interface) of the next hop
Pingping Lin9a445c82016-04-07 11:40:29 -0700166 Interface egressInterface =
167 interfaceService.getMatchingInterface(nextHopIpAddress);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200168 if (egressInterface == null) {
169 log.warn("No outgoing interface found for {}",
170 nextHopIpAddress);
171 return;
172 }
173
174 Set<Host> hosts = hostService.getHostsByIp(nextHopIpAddress);
175 if (hosts.isEmpty()) {
176 log.warn("No host found for next hop IP address");
177 return;
178 }
179 MacAddress nextHopMacAddress = null;
180 for (Host host : hosts) {
181 nextHopMacAddress = host.mac();
182 break;
183 }
184
185 hosts = hostService.getHostsByIp(hostIp);
186 if (hosts.isEmpty()) {
187 log.warn("No host found for host IP address");
188 return;
189 }
190 Host host = hosts.stream().findFirst().get();
191 ConnectPoint ingressPoint = host.location();
192
193 // Generate the intent itself
194 ConnectPoint egressPort = egressInterface.connectPoint();
195 log.debug("Generating intent for prefix {}, next hop mac {}",
196 prefix, nextHopMacAddress);
197
198 // Match the destination IP prefix at the first hop
199 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
200 if (prefix.isIp4()) {
201 selector.matchEthType(Ethernet.TYPE_IPV4);
202 selector.matchIPDst(prefix);
203 } else {
204 selector.matchEthType(Ethernet.TYPE_IPV6);
205 selector.matchIPv6Dst(prefix);
206 }
207
208 // Rewrite the destination MAC address
209 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
210 .setEthDst(nextHopMacAddress);
211 if (!egressInterface.vlan().equals(VlanId.NONE)) {
212 treatment.setVlanId(egressInterface.vlan());
213 // If we set VLAN ID, we have to make sure a VLAN tag exists.
214 // TODO support no VLAN -> VLAN routing
215 selector.matchVlanId(VlanId.ANY);
216 }
217
218 int priority = prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
219 Key key = Key.of(prefix.toString() + "-reactive", appId);
220 MultiPointToSinglePointIntent intent = MultiPointToSinglePointIntent.builder()
221 .appId(appId)
222 .key(key)
223 .selector(selector.build())
224 .treatment(treatment.build())
225 .ingressPoints(Collections.singleton(ingressPoint))
226 .egressPoint(egressPort)
227 .priority(priority)
228 .constraints(CONSTRAINTS)
229 .build();
230
231 submitReactiveIntent(prefix, intent);
232 }
233
234 @Override
235 public void setUpConnectivityHostToHost(IpAddress dstIpAddress,
236 IpAddress srcIpAddress,
237 MacAddress srcMacAddress,
238 ConnectPoint srcConnectPoint) {
239 checkNotNull(dstIpAddress);
240 checkNotNull(srcIpAddress);
241 checkNotNull(srcMacAddress);
242 checkNotNull(srcConnectPoint);
243
244 IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix();
245 IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
246 ConnectPoint dstConnectPoint = null;
247 MacAddress dstMacAddress = null;
248
249 for (Host host : hostService.getHostsByIp(dstIpAddress)) {
250 if (host.mac() != null) {
251 dstMacAddress = host.mac();
252 dstConnectPoint = host.location();
253 break;
254 }
255 }
256 if (dstMacAddress == null) {
257 hostService.startMonitoringIp(dstIpAddress);
258 return;
259 }
260
261 //
262 // Handle intent from source host to destination host
263 //
264 MultiPointToSinglePointIntent srcToDstIntent =
265 hostToHostIntentGenerator(dstIpAddress, dstConnectPoint,
266 dstMacAddress, srcConnectPoint);
267 submitReactiveIntent(dstIpPrefix, srcToDstIntent);
268
269 //
270 // Handle intent from destination host to source host
271 //
272
273 // Since we proactively handle the intent from destination host to
274 // source host, we should check whether there is an exiting intent
275 // first.
276 if (mp2pIntentExists(srcIpPrefix)) {
277 updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint);
278 return;
279 } else {
280 // There is no existing intent, create a new one.
281 MultiPointToSinglePointIntent dstToSrcIntent =
282 hostToHostIntentGenerator(srcIpAddress, srcConnectPoint,
283 srcMacAddress, dstConnectPoint);
284 submitReactiveIntent(srcIpPrefix, dstToSrcIntent);
285 }
286 }
287
288 /**
289 * Generates MultiPointToSinglePointIntent for both source host and
290 * destination host located in local SDN network.
291 *
292 * @param dstIpAddress the destination IP address
293 * @param dstConnectPoint the destination host connect point
294 * @param dstMacAddress the MAC address of destination host
295 * @param srcConnectPoint the connect point where packet-in from
296 * @return the generated MultiPointToSinglePointIntent
297 */
298 private MultiPointToSinglePointIntent hostToHostIntentGenerator(
299 IpAddress dstIpAddress,
300 ConnectPoint dstConnectPoint,
301 MacAddress dstMacAddress,
302 ConnectPoint srcConnectPoint) {
303 checkNotNull(dstIpAddress);
304 checkNotNull(dstConnectPoint);
305 checkNotNull(dstMacAddress);
306 checkNotNull(srcConnectPoint);
307
308 Set<ConnectPoint> ingressPoints = new HashSet<>();
309 ingressPoints.add(srcConnectPoint);
310 IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
311
312 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
313 if (dstIpAddress.isIp4()) {
314 selector.matchEthType(Ethernet.TYPE_IPV4);
315 selector.matchIPDst(dstIpPrefix);
316 } else {
317 selector.matchEthType(Ethernet.TYPE_IPV6);
318 selector.matchIPv6Dst(dstIpPrefix);
319 }
320
321 // Rewrite the destination MAC address
322 TrafficTreatment.Builder treatment =
323 DefaultTrafficTreatment.builder().setEthDst(dstMacAddress);
324
325 Key key = Key.of(dstIpPrefix.toString(), appId);
326 int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER
327 + PRIORITY_OFFSET;
328 MultiPointToSinglePointIntent intent =
329 MultiPointToSinglePointIntent.builder()
330 .appId(appId)
331 .key(key)
332 .selector(selector.build())
333 .treatment(treatment.build())
334 .ingressPoints(ingressPoints)
335 .egressPoint(dstConnectPoint)
336 .priority(priority)
337 .constraints(CONSTRAINTS)
338 .build();
339
340 log.trace("Generates ConnectivityHostToHost = {} ", intent);
341 return intent;
342 }
343
344 @Override
345 public void updateExistingMp2pIntent(IpPrefix ipPrefix,
346 ConnectPoint ingressConnectPoint) {
347 checkNotNull(ipPrefix);
348 checkNotNull(ingressConnectPoint);
349
350 MultiPointToSinglePointIntent existingIntent =
351 getExistingMp2pIntent(ipPrefix);
352 if (existingIntent != null) {
353 Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints();
354 // Add host connect point into ingressPoints of the existing intent
355 if (ingressPoints.add(ingressConnectPoint)) {
356 MultiPointToSinglePointIntent updatedMp2pIntent =
357 MultiPointToSinglePointIntent.builder()
358 .appId(appId)
359 .key(existingIntent.key())
360 .selector(existingIntent.selector())
361 .treatment(existingIntent.treatment())
362 .ingressPoints(ingressPoints)
363 .egressPoint(existingIntent.egressPoint())
364 .priority(existingIntent.priority())
365 .constraints(CONSTRAINTS)
366 .build();
367
368 log.trace("Update an existing MultiPointToSinglePointIntent "
369 + "to new intent = {} ", updatedMp2pIntent);
370 submitReactiveIntent(ipPrefix, updatedMp2pIntent);
371 }
372 // If adding ingressConnectPoint to ingressPoints failed, it
373 // because between the time interval from checking existing intent
374 // to generating new intent, onos updated this intent due to other
375 // packet-in and the new intent also includes the
376 // ingressConnectPoint. This will not affect reactive routing.
377 }
378 }
379
380 /**
381 * Submits a reactive intent to the intent synchronizer.
382 *
383 * @param ipPrefix IP prefix of the intent
384 * @param intent intent to submit
385 */
386 void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) {
387 routeIntents.put(ipPrefix, intent);
388
389 intentSynchronizer.submit(intent);
390 }
391
392 /**
393 * Gets the existing MultiPointToSinglePointIntent from memory for a given
394 * IP prefix.
395 *
396 * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent
397 * @return the MultiPointToSinglePointIntent if found, otherwise null
398 */
399 private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix ipPrefix) {
400 checkNotNull(ipPrefix);
401 return routeIntents.get(ipPrefix);
402 }
403
404 @Override
405 public boolean mp2pIntentExists(IpPrefix ipPrefix) {
406 checkNotNull(ipPrefix);
407 return routeIntents.get(ipPrefix) != null;
408 }
409}