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