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