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