Committed by
Ray Milkey
[Falcon] Split FlowObjectives programming component out of
BgpRouter so it can be used by other applications. Change-Id: I37a5467e17e677fe44c35704c60429499e0d42ad
Showing
3 changed files
with
422 additions
and
307 deletions
... | @@ -38,12 +38,6 @@ | ... | @@ -38,12 +38,6 @@ |
38 | 38 | ||
39 | <dependency> | 39 | <dependency> |
40 | <groupId>org.onosproject</groupId> | 40 | <groupId>org.onosproject</groupId> |
41 | - <artifactId>onos-app-routing</artifactId> | ||
42 | - <version>${project.version}</version> | ||
43 | - </dependency> | ||
44 | - | ||
45 | - <dependency> | ||
46 | - <groupId>org.onosproject</groupId> | ||
47 | <artifactId>onlab-misc</artifactId> | 41 | <artifactId>onlab-misc</artifactId> |
48 | </dependency> | 42 | </dependency> |
49 | 43 | ... | ... |
... | @@ -15,65 +15,28 @@ | ... | @@ -15,65 +15,28 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.bgprouter; | 16 | package org.onosproject.bgprouter; |
17 | 17 | ||
18 | -import com.google.common.collect.ConcurrentHashMultiset; | ||
19 | -import com.google.common.collect.HashMultimap; | ||
20 | -import com.google.common.collect.Maps; | ||
21 | -import com.google.common.collect.Multimap; | ||
22 | -import com.google.common.collect.Multiset; | ||
23 | import org.apache.felix.scr.annotations.Activate; | 18 | import org.apache.felix.scr.annotations.Activate; |
24 | import org.apache.felix.scr.annotations.Component; | 19 | import org.apache.felix.scr.annotations.Component; |
25 | import org.apache.felix.scr.annotations.Deactivate; | 20 | import org.apache.felix.scr.annotations.Deactivate; |
26 | import org.apache.felix.scr.annotations.Reference; | 21 | import org.apache.felix.scr.annotations.Reference; |
27 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 22 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
28 | -import org.onlab.packet.Ethernet; | ||
29 | -import org.onlab.packet.IpAddress; | ||
30 | -import org.onlab.packet.IpPrefix; | ||
31 | import org.onosproject.core.ApplicationId; | 23 | import org.onosproject.core.ApplicationId; |
32 | import org.onosproject.core.CoreService; | 24 | import org.onosproject.core.CoreService; |
33 | -import org.onosproject.net.config.NetworkConfigService; | ||
34 | -import org.onosproject.incubator.net.intf.Interface; | ||
35 | import org.onosproject.incubator.net.intf.InterfaceService; | 25 | import org.onosproject.incubator.net.intf.InterfaceService; |
36 | import org.onosproject.net.DeviceId; | 26 | import org.onosproject.net.DeviceId; |
27 | +import org.onosproject.net.config.NetworkConfigService; | ||
37 | import org.onosproject.net.device.DeviceEvent; | 28 | import org.onosproject.net.device.DeviceEvent; |
38 | import org.onosproject.net.device.DeviceListener; | 29 | import org.onosproject.net.device.DeviceListener; |
39 | import org.onosproject.net.device.DeviceService; | 30 | import org.onosproject.net.device.DeviceService; |
40 | -import org.onosproject.net.flow.DefaultTrafficSelector; | ||
41 | -import org.onosproject.net.flow.DefaultTrafficTreatment; | ||
42 | -import org.onosproject.net.flow.TrafficSelector; | ||
43 | -import org.onosproject.net.flow.TrafficTreatment; | ||
44 | -import org.onosproject.net.flow.criteria.Criteria; | ||
45 | -import org.onosproject.net.flowobjective.DefaultFilteringObjective; | ||
46 | -import org.onosproject.net.flowobjective.DefaultForwardingObjective; | ||
47 | -import org.onosproject.net.flowobjective.DefaultNextObjective; | ||
48 | -import org.onosproject.net.flowobjective.FilteringObjective; | ||
49 | import org.onosproject.net.flowobjective.FlowObjectiveService; | 31 | import org.onosproject.net.flowobjective.FlowObjectiveService; |
50 | -import org.onosproject.net.flowobjective.ForwardingObjective; | ||
51 | -import org.onosproject.net.flowobjective.NextObjective; | ||
52 | -import org.onosproject.net.flowobjective.Objective; | ||
53 | -import org.onosproject.net.flowobjective.ObjectiveContext; | ||
54 | -import org.onosproject.net.flowobjective.ObjectiveError; | ||
55 | import org.onosproject.net.packet.PacketService; | 32 | import org.onosproject.net.packet.PacketService; |
56 | -import org.onosproject.routing.FibEntry; | ||
57 | -import org.onosproject.routing.FibListener; | ||
58 | -import org.onosproject.routing.FibUpdate; | ||
59 | import org.onosproject.routing.RoutingService; | 33 | import org.onosproject.routing.RoutingService; |
60 | import org.onosproject.routing.config.BgpConfig; | 34 | import org.onosproject.routing.config.BgpConfig; |
35 | +import org.onosproject.routing.config.RoutingConfigurationService; | ||
61 | import org.slf4j.Logger; | 36 | import org.slf4j.Logger; |
62 | import org.slf4j.LoggerFactory; | 37 | import org.slf4j.LoggerFactory; |
63 | 38 | ||
64 | -import java.util.Collection; | ||
65 | -import java.util.HashMap; | ||
66 | -import java.util.Map; | ||
67 | import java.util.Optional; | 39 | import java.util.Optional; |
68 | -import java.util.Set; | ||
69 | - | ||
70 | -/* For test only - will be removed before Cardinal release | ||
71 | -import org.onlab.packet.Ip4Address; | ||
72 | -import org.onlab.packet.Ip4Prefix; | ||
73 | -import org.onlab.packet.MacAddress; | ||
74 | -import java.util.Collections; | ||
75 | -import static org.onlab.util.Tools.delay; | ||
76 | -*/ | ||
77 | 40 | ||
78 | /** | 41 | /** |
79 | * BgpRouter component. | 42 | * BgpRouter component. |
... | @@ -83,16 +46,16 @@ public class BgpRouter { | ... | @@ -83,16 +46,16 @@ public class BgpRouter { |
83 | 46 | ||
84 | private static final Logger log = LoggerFactory.getLogger(BgpRouter.class); | 47 | private static final Logger log = LoggerFactory.getLogger(BgpRouter.class); |
85 | 48 | ||
86 | - private static final String BGP_ROUTER_APP = "org.onosproject.bgprouter"; | 49 | + public static final String BGP_ROUTER_APP = "org.onosproject.bgprouter"; |
87 | - | ||
88 | - private static final int PRIORITY_OFFSET = 100; | ||
89 | - private static final int PRIORITY_MULTIPLIER = 5; | ||
90 | 50 | ||
91 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 51 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
92 | protected CoreService coreService; | 52 | protected CoreService coreService; |
93 | 53 | ||
54 | + // We depend on the routing configuration being available before starting | ||
55 | + // up. When we have dynamic configuration support this will no longer be | ||
56 | + // necessary. | ||
94 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 57 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
95 | - protected RoutingService routingService; | 58 | + protected RoutingConfigurationService routingConfigurationService; |
96 | 59 | ||
97 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 60 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
98 | protected InterfaceService interfaceService; | 61 | protected InterfaceService interfaceService; |
... | @@ -111,21 +74,6 @@ public class BgpRouter { | ... | @@ -111,21 +74,6 @@ public class BgpRouter { |
111 | 74 | ||
112 | private ApplicationId appId; | 75 | private ApplicationId appId; |
113 | 76 | ||
114 | - // Reference count for how many times a next hop is used by a route | ||
115 | - private final Multiset<IpAddress> nextHopsCount = ConcurrentHashMultiset.create(); | ||
116 | - | ||
117 | - // Mapping from prefix to its current next hop | ||
118 | - private final Map<IpPrefix, IpAddress> prefixToNextHop = Maps.newHashMap(); | ||
119 | - | ||
120 | - // Mapping from next hop IP to next hop object containing group info | ||
121 | - private final Map<IpAddress, Integer> nextHops = Maps.newHashMap(); | ||
122 | - | ||
123 | - // Stores FIB updates that are waiting for groups to be set up | ||
124 | - private final Multimap<NextHopGroupKey, FibEntry> pendingUpdates = HashMultimap.create(); | ||
125 | - | ||
126 | - // Device id of data-plane switch - should be learned from config | ||
127 | - private DeviceId deviceId; | ||
128 | - | ||
129 | // Device id of control-plane switch (OVS) connected to BGP Speaker - should be | 77 | // Device id of control-plane switch (OVS) connected to BGP Speaker - should be |
130 | // learned from config | 78 | // learned from config |
131 | private DeviceId ctrlDeviceId; | 79 | private DeviceId ctrlDeviceId; |
... | @@ -159,18 +107,13 @@ public class BgpRouter { | ... | @@ -159,18 +107,13 @@ public class BgpRouter { |
159 | flowObjectiveService); | 107 | flowObjectiveService); |
160 | 108 | ||
161 | icmpHandler = new IcmpHandler(interfaceService, packetService); | 109 | icmpHandler = new IcmpHandler(interfaceService, packetService); |
110 | + | ||
162 | deviceListener = new InnerDeviceListener(); | 111 | deviceListener = new InnerDeviceListener(); |
163 | - routingService.addFibListener(new InternalFibListener()); | ||
164 | - routingService.start(); | ||
165 | deviceService.addListener(deviceListener); | 112 | deviceService.addListener(deviceListener); |
113 | + | ||
166 | connectivityManager.start(); | 114 | connectivityManager.start(); |
167 | icmpHandler.start(); | 115 | icmpHandler.start(); |
168 | 116 | ||
169 | - // Initialize devices now if they are already connected | ||
170 | - if (deviceService.isAvailable(deviceId)) { | ||
171 | - processIntfFilters(true, interfaceService.getInterfaces()); | ||
172 | - } | ||
173 | - | ||
174 | if (deviceService.isAvailable(ctrlDeviceId)) { | 117 | if (deviceService.isAvailable(ctrlDeviceId)) { |
175 | connectivityManager.notifySwitchAvailable(); | 118 | connectivityManager.notifySwitchAvailable(); |
176 | } | 119 | } |
... | @@ -180,11 +123,10 @@ public class BgpRouter { | ... | @@ -180,11 +123,10 @@ public class BgpRouter { |
180 | 123 | ||
181 | @Deactivate | 124 | @Deactivate |
182 | protected void deactivate() { | 125 | protected void deactivate() { |
183 | - routingService.stop(); | ||
184 | connectivityManager.stop(); | 126 | connectivityManager.stop(); |
185 | icmpHandler.stop(); | 127 | icmpHandler.stop(); |
186 | deviceService.removeListener(deviceListener); | 128 | deviceService.removeListener(deviceListener); |
187 | - //processIntfFilters(false, configService.getInterfaces()); //TODO necessary? | 129 | + |
188 | log.info("BgpRouter stopped"); | 130 | log.info("BgpRouter stopped"); |
189 | } | 131 | } |
190 | 132 | ||
... | @@ -199,223 +141,9 @@ public class BgpRouter { | ... | @@ -199,223 +141,9 @@ public class BgpRouter { |
199 | 141 | ||
200 | ctrlDeviceId = bgpSpeaker.get().connectPoint().deviceId(); | 142 | ctrlDeviceId = bgpSpeaker.get().connectPoint().deviceId(); |
201 | 143 | ||
202 | - Optional<IpAddress> peerAddress = | ||
203 | - bgpSpeaker.get().peers().stream().findAny(); | ||
204 | - | ||
205 | - if (!peerAddress.isPresent()) { | ||
206 | - log.error("BGP speaker must have peers configured"); | ||
207 | - return; | ||
208 | - } | ||
209 | - | ||
210 | - Interface intf = interfaceService.getMatchingInterface(peerAddress.get()); | ||
211 | - | ||
212 | - if (intf == null) { | ||
213 | - log.error("No interface found for peer"); | ||
214 | - return; | ||
215 | - } | ||
216 | - | ||
217 | - // Assume all peers are configured on the same device - this is required | ||
218 | - // by the BGP router | ||
219 | - deviceId = intf.connectPoint().deviceId(); | ||
220 | - | ||
221 | - log.info("Router dpid: {}", deviceId); | ||
222 | log.info("Control Plane OVS dpid: {}", ctrlDeviceId); | 144 | log.info("Control Plane OVS dpid: {}", ctrlDeviceId); |
223 | } | 145 | } |
224 | 146 | ||
225 | - private void updateFibEntry(Collection<FibUpdate> updates) { | ||
226 | - Map<FibEntry, Integer> toInstall = new HashMap<>(updates.size()); | ||
227 | - | ||
228 | - for (FibUpdate update : updates) { | ||
229 | - FibEntry entry = update.entry(); | ||
230 | - | ||
231 | - addNextHop(entry); | ||
232 | - | ||
233 | - Integer nextId; | ||
234 | - synchronized (pendingUpdates) { | ||
235 | - nextId = nextHops.get(entry.nextHopIp()); | ||
236 | - } | ||
237 | - | ||
238 | - toInstall.put(update.entry(), nextId); | ||
239 | - } | ||
240 | - | ||
241 | - installFlows(toInstall); | ||
242 | - } | ||
243 | - | ||
244 | - private void installFlows(Map<FibEntry, Integer> entriesToInstall) { | ||
245 | - | ||
246 | - for (Map.Entry<FibEntry, Integer> entry : entriesToInstall.entrySet()) { | ||
247 | - FibEntry fibEntry = entry.getKey(); | ||
248 | - Integer nextId = entry.getValue(); | ||
249 | - | ||
250 | - flowObjectiveService.forward(deviceId, | ||
251 | - generateRibForwardingObj(fibEntry.prefix(), nextId).add()); | ||
252 | - log.trace("Sending forwarding objective {} -> nextId:{}", fibEntry, nextId); | ||
253 | - } | ||
254 | - | ||
255 | - } | ||
256 | - | ||
257 | - private synchronized void deleteFibEntry(Collection<FibUpdate> withdraws) { | ||
258 | - | ||
259 | - for (FibUpdate update : withdraws) { | ||
260 | - FibEntry entry = update.entry(); | ||
261 | - //Integer nextId = nextHops.get(entry.nextHopIp()); | ||
262 | - | ||
263 | - /* Group group = deleteNextHop(entry.prefix()); | ||
264 | - if (group == null) { | ||
265 | - log.warn("Group not found when deleting {}", entry); | ||
266 | - return; | ||
267 | - }*/ | ||
268 | - | ||
269 | - flowObjectiveService.forward(deviceId, | ||
270 | - generateRibForwardingObj(entry.prefix(), null).remove()); | ||
271 | - | ||
272 | - } | ||
273 | - | ||
274 | - } | ||
275 | - | ||
276 | - private ForwardingObjective.Builder generateRibForwardingObj(IpPrefix prefix, | ||
277 | - Integer nextId) { | ||
278 | - TrafficSelector selector = DefaultTrafficSelector.builder() | ||
279 | - .matchEthType(Ethernet.TYPE_IPV4) | ||
280 | - .matchIPDst(prefix) | ||
281 | - .build(); | ||
282 | - | ||
283 | - int priority = prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET; | ||
284 | - | ||
285 | - ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective.builder() | ||
286 | - .fromApp(appId) | ||
287 | - .makePermanent() | ||
288 | - .withSelector(selector) | ||
289 | - .withPriority(priority) | ||
290 | - .withFlag(ForwardingObjective.Flag.SPECIFIC); | ||
291 | - | ||
292 | - if (nextId == null) { | ||
293 | - // Route withdraws are not specified with next hops. Generating | ||
294 | - // dummy treatment as there is no equivalent nextId info. | ||
295 | - fwdBuilder.withTreatment(DefaultTrafficTreatment.builder().build()); | ||
296 | - } else { | ||
297 | - fwdBuilder.nextStep(nextId); | ||
298 | - } | ||
299 | - return fwdBuilder; | ||
300 | - } | ||
301 | - | ||
302 | - private synchronized void addNextHop(FibEntry entry) { | ||
303 | - prefixToNextHop.put(entry.prefix(), entry.nextHopIp()); | ||
304 | - if (nextHopsCount.count(entry.nextHopIp()) == 0) { | ||
305 | - // There was no next hop in the multiset | ||
306 | - | ||
307 | - Interface egressIntf = interfaceService.getMatchingInterface(entry.nextHopIp()); | ||
308 | - if (egressIntf == null) { | ||
309 | - log.warn("no egress interface found for {}", entry); | ||
310 | - return; | ||
311 | - } | ||
312 | - | ||
313 | - NextHopGroupKey groupKey = new NextHopGroupKey(entry.nextHopIp()); | ||
314 | - | ||
315 | - NextHop nextHop = new NextHop(entry.nextHopIp(), entry.nextHopMac(), groupKey); | ||
316 | - | ||
317 | - TrafficTreatment treatment = DefaultTrafficTreatment.builder() | ||
318 | - .setEthSrc(egressIntf.mac()) | ||
319 | - .setEthDst(nextHop.mac()) | ||
320 | - .pushVlan() | ||
321 | - .setVlanId(egressIntf.vlan()) | ||
322 | - .setVlanPcp((byte) 0) | ||
323 | - .setOutput(egressIntf.connectPoint().port()) | ||
324 | - .build(); | ||
325 | - | ||
326 | - int nextId = flowObjectiveService.allocateNextId(); | ||
327 | - | ||
328 | - NextObjective nextObjective = DefaultNextObjective.builder() | ||
329 | - .withId(nextId) | ||
330 | - .addTreatment(treatment) | ||
331 | - .withType(NextObjective.Type.SIMPLE) | ||
332 | - .fromApp(appId) | ||
333 | - .add(); // TODO add callbacks | ||
334 | - | ||
335 | - flowObjectiveService.next(deviceId, nextObjective); | ||
336 | - | ||
337 | - nextHops.put(nextHop.ip(), nextId); | ||
338 | - | ||
339 | - } | ||
340 | - | ||
341 | - nextHopsCount.add(entry.nextHopIp()); | ||
342 | - } | ||
343 | - | ||
344 | - /*private synchronized Group deleteNextHop(IpPrefix prefix) { | ||
345 | - IpAddress nextHopIp = prefixToNextHop.remove(prefix); | ||
346 | - NextHop nextHop = nextHops.get(nextHopIp); | ||
347 | - if (nextHop == null) { | ||
348 | - log.warn("No next hop found when removing prefix {}", prefix); | ||
349 | - return null; | ||
350 | - } | ||
351 | - | ||
352 | - Group group = groupService.getGroup(deviceId, | ||
353 | - new DefaultGroupKey(appKryo. | ||
354 | - serialize(nextHop.group()))); | ||
355 | - | ||
356 | - // FIXME disabling group deletes for now until we verify the logic is OK | ||
357 | - if (nextHopsCount.remove(nextHopIp, 1) <= 1) { | ||
358 | - // There was one or less next hops, so there are now none | ||
359 | - | ||
360 | - log.debug("removing group for next hop {}", nextHop); | ||
361 | - | ||
362 | - nextHops.remove(nextHopIp); | ||
363 | - | ||
364 | - groupService.removeGroup(deviceId, | ||
365 | - new DefaultGroupKey(appKryo.build().serialize(nextHop.group())), | ||
366 | - appId); | ||
367 | - } | ||
368 | - | ||
369 | - return group; | ||
370 | - }*/ | ||
371 | - | ||
372 | - private class InternalFibListener implements FibListener { | ||
373 | - | ||
374 | - @Override | ||
375 | - public void update(Collection<FibUpdate> updates, | ||
376 | - Collection<FibUpdate> withdraws) { | ||
377 | - BgpRouter.this.deleteFibEntry(withdraws); | ||
378 | - BgpRouter.this.updateFibEntry(updates); | ||
379 | - } | ||
380 | - } | ||
381 | - | ||
382 | - private void processIntfFilters(boolean install, Set<Interface> intfs) { | ||
383 | - log.info("Processing {} router interfaces", intfs.size()); | ||
384 | - for (Interface intf : intfs) { | ||
385 | - if (!intf.connectPoint().deviceId().equals(deviceId)) { | ||
386 | - // Ignore interfaces if they are not on the router switch | ||
387 | - continue; | ||
388 | - } | ||
389 | - | ||
390 | - FilteringObjective.Builder fob = DefaultFilteringObjective.builder(); | ||
391 | - fob.withKey(Criteria.matchInPort(intf.connectPoint().port())) | ||
392 | - .addCondition(Criteria.matchEthDst(intf.mac())) | ||
393 | - .addCondition(Criteria.matchVlanId(intf.vlan())); | ||
394 | - intf.ipAddresses().stream() | ||
395 | - .forEach(ipaddr -> fob.addCondition( | ||
396 | - Criteria.matchIPDst( | ||
397 | - IpPrefix.valueOf(ipaddr.ipAddress(), 32)))); | ||
398 | - fob.permit().fromApp(appId); | ||
399 | - flowObjectiveService.filter( | ||
400 | - deviceId, | ||
401 | - fob.add(new ObjectiveContext() { | ||
402 | - @Override | ||
403 | - public void onSuccess(Objective objective) { | ||
404 | - log.info("Successfully installed interface based " | ||
405 | - + "filtering objectives for intf {}", intf); | ||
406 | - } | ||
407 | - | ||
408 | - @Override | ||
409 | - public void onError(Objective objective, | ||
410 | - ObjectiveError error) { | ||
411 | - log.error("Failed to install interface filters for intf {}: {}", | ||
412 | - intf, error); | ||
413 | - // TODO something more than just logging | ||
414 | - } | ||
415 | - })); | ||
416 | - } | ||
417 | - } | ||
418 | - | ||
419 | // Triggers driver setup when a device is (re)detected. | 147 | // Triggers driver setup when a device is (re)detected. |
420 | private class InnerDeviceListener implements DeviceListener { | 148 | private class InnerDeviceListener implements DeviceListener { |
421 | @Override | 149 | @Override |
... | @@ -425,41 +153,23 @@ public class BgpRouter { | ... | @@ -425,41 +153,23 @@ public class BgpRouter { |
425 | case DEVICE_AVAILABILITY_CHANGED: | 153 | case DEVICE_AVAILABILITY_CHANGED: |
426 | if (deviceService.isAvailable(event.subject().id())) { | 154 | if (deviceService.isAvailable(event.subject().id())) { |
427 | log.info("Device connected {}", event.subject().id()); | 155 | log.info("Device connected {}", event.subject().id()); |
428 | - if (event.subject().id().equals(deviceId)) { | ||
429 | - processIntfFilters(true, interfaceService.getInterfaces()); | ||
430 | - | ||
431 | - /* For test only - will be removed before Cardinal release | ||
432 | - delay(1000); | ||
433 | - FibEntry fibEntry = new FibEntry(Ip4Prefix.valueOf("10.1.0.0/16"), | ||
434 | - Ip4Address.valueOf("192.168.10.1"), | ||
435 | - MacAddress.valueOf("DE:AD:BE:EF:FE:ED")); | ||
436 | - FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry); | ||
437 | - updateFibEntry(Collections.singletonList(fibUpdate)); | ||
438 | - */ | ||
439 | - } | ||
440 | 156 | ||
441 | if (event.subject().id().equals(ctrlDeviceId)) { | 157 | if (event.subject().id().equals(ctrlDeviceId)) { |
442 | connectivityManager.notifySwitchAvailable(); | 158 | connectivityManager.notifySwitchAvailable(); |
443 | } | 159 | } |
444 | } | 160 | } |
445 | break; | 161 | break; |
446 | - | ||
447 | // TODO other cases | 162 | // TODO other cases |
448 | case DEVICE_UPDATED: | 163 | case DEVICE_UPDATED: |
449 | - break; | ||
450 | case DEVICE_REMOVED: | 164 | case DEVICE_REMOVED: |
451 | - break; | ||
452 | case DEVICE_SUSPENDED: | 165 | case DEVICE_SUSPENDED: |
453 | - break; | ||
454 | case PORT_ADDED: | 166 | case PORT_ADDED: |
455 | - break; | ||
456 | case PORT_UPDATED: | 167 | case PORT_UPDATED: |
457 | - break; | ||
458 | case PORT_REMOVED: | 168 | case PORT_REMOVED: |
459 | - break; | ||
460 | default: | 169 | default: |
461 | break; | 170 | break; |
462 | } | 171 | } |
463 | } | 172 | } |
464 | } | 173 | } |
174 | + | ||
465 | } | 175 | } | ... | ... |
1 | +/* | ||
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 | + | ||
17 | +package org.onosproject.bgprouter; | ||
18 | + | ||
19 | +import com.google.common.collect.ConcurrentHashMultiset; | ||
20 | +import com.google.common.collect.HashMultimap; | ||
21 | +import com.google.common.collect.Maps; | ||
22 | +import com.google.common.collect.Multimap; | ||
23 | +import com.google.common.collect.Multiset; | ||
24 | +import org.apache.felix.scr.annotations.Activate; | ||
25 | +import org.apache.felix.scr.annotations.Component; | ||
26 | +import org.apache.felix.scr.annotations.Deactivate; | ||
27 | +import org.apache.felix.scr.annotations.Reference; | ||
28 | +import org.apache.felix.scr.annotations.ReferenceCardinality; | ||
29 | +import org.onlab.packet.Ethernet; | ||
30 | +import org.onlab.packet.IpAddress; | ||
31 | +import org.onlab.packet.IpPrefix; | ||
32 | +import org.onosproject.core.ApplicationId; | ||
33 | +import org.onosproject.core.CoreService; | ||
34 | +import org.onosproject.incubator.net.intf.Interface; | ||
35 | +import org.onosproject.incubator.net.intf.InterfaceService; | ||
36 | +import org.onosproject.net.DeviceId; | ||
37 | +import org.onosproject.net.config.NetworkConfigService; | ||
38 | +import org.onosproject.net.device.DeviceEvent; | ||
39 | +import org.onosproject.net.device.DeviceListener; | ||
40 | +import org.onosproject.net.device.DeviceService; | ||
41 | +import org.onosproject.net.flow.DefaultTrafficSelector; | ||
42 | +import org.onosproject.net.flow.DefaultTrafficTreatment; | ||
43 | +import org.onosproject.net.flow.TrafficSelector; | ||
44 | +import org.onosproject.net.flow.TrafficTreatment; | ||
45 | +import org.onosproject.net.flow.criteria.Criteria; | ||
46 | +import org.onosproject.net.flowobjective.DefaultFilteringObjective; | ||
47 | +import org.onosproject.net.flowobjective.DefaultForwardingObjective; | ||
48 | +import org.onosproject.net.flowobjective.DefaultNextObjective; | ||
49 | +import org.onosproject.net.flowobjective.FilteringObjective; | ||
50 | +import org.onosproject.net.flowobjective.FlowObjectiveService; | ||
51 | +import org.onosproject.net.flowobjective.ForwardingObjective; | ||
52 | +import org.onosproject.net.flowobjective.NextObjective; | ||
53 | +import org.onosproject.net.flowobjective.Objective; | ||
54 | +import org.onosproject.net.flowobjective.ObjectiveContext; | ||
55 | +import org.onosproject.net.flowobjective.ObjectiveError; | ||
56 | +import org.onosproject.routing.FibEntry; | ||
57 | +import org.onosproject.routing.FibListener; | ||
58 | +import org.onosproject.routing.FibUpdate; | ||
59 | +import org.onosproject.routing.RoutingService; | ||
60 | +import org.onosproject.routing.config.BgpConfig; | ||
61 | +import org.slf4j.Logger; | ||
62 | +import org.slf4j.LoggerFactory; | ||
63 | + | ||
64 | +import java.util.Collection; | ||
65 | +import java.util.HashMap; | ||
66 | +import java.util.Map; | ||
67 | +import java.util.Optional; | ||
68 | +import java.util.Set; | ||
69 | + | ||
70 | +/** | ||
71 | + * Programs routes to a single OpenFlow switch. | ||
72 | + */ | ||
73 | +@Component(immediate = true) | ||
74 | +public class SingleSwitchRouter { | ||
75 | + | ||
76 | + private final Logger log = LoggerFactory.getLogger(getClass()); | ||
77 | + | ||
78 | + private static final int PRIORITY_OFFSET = 100; | ||
79 | + private static final int PRIORITY_MULTIPLIER = 5; | ||
80 | + | ||
81 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
82 | + protected CoreService coreService; | ||
83 | + | ||
84 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
85 | + protected RoutingService routingService; | ||
86 | + | ||
87 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
88 | + protected InterfaceService interfaceService; | ||
89 | + | ||
90 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
91 | + protected NetworkConfigService networkConfigService; | ||
92 | + | ||
93 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
94 | + protected FlowObjectiveService flowObjectiveService; | ||
95 | + | ||
96 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
97 | + protected DeviceService deviceService; | ||
98 | + | ||
99 | + private InnerDeviceListener deviceListener; | ||
100 | + | ||
101 | + // Device id of data-plane switch - should be learned from config | ||
102 | + private DeviceId deviceId; | ||
103 | + | ||
104 | + private ApplicationId appId; | ||
105 | + | ||
106 | + // Reference count for how many times a next hop is used by a route | ||
107 | + private final Multiset<IpAddress> nextHopsCount = ConcurrentHashMultiset.create(); | ||
108 | + | ||
109 | + // Mapping from prefix to its current next hop | ||
110 | + private final Map<IpPrefix, IpAddress> prefixToNextHop = Maps.newHashMap(); | ||
111 | + | ||
112 | + // Mapping from next hop IP to next hop object containing group info | ||
113 | + private final Map<IpAddress, Integer> nextHops = Maps.newHashMap(); | ||
114 | + | ||
115 | + // Stores FIB updates that are waiting for groups to be set up | ||
116 | + private final Multimap<NextHopGroupKey, FibEntry> pendingUpdates = HashMultimap.create(); | ||
117 | + | ||
118 | + | ||
119 | + @Activate | ||
120 | + protected void activate() { | ||
121 | + ApplicationId routerAppId = coreService.getAppId(RoutingService.ROUTER_APP_ID); | ||
122 | + BgpConfig bgpConfig = | ||
123 | + networkConfigService.getConfig(routerAppId, RoutingService.CONFIG_CLASS); | ||
124 | + | ||
125 | + if (bgpConfig == null) { | ||
126 | + log.error("No BgpConfig found"); | ||
127 | + return; | ||
128 | + } | ||
129 | + | ||
130 | + getDeviceConfiguration(bgpConfig); | ||
131 | + | ||
132 | + appId = coreService.getAppId(BgpRouter.BGP_ROUTER_APP); | ||
133 | + | ||
134 | + deviceListener = new InnerDeviceListener(); | ||
135 | + deviceService.addListener(deviceListener); | ||
136 | + | ||
137 | + routingService.addFibListener(new InternalFibListener()); | ||
138 | + routingService.start(); | ||
139 | + | ||
140 | + // Initialize devices now if they are already connected | ||
141 | + if (deviceService.isAvailable(deviceId)) { | ||
142 | + processIntfFilters(true, interfaceService.getInterfaces()); | ||
143 | + } | ||
144 | + | ||
145 | + log.info("Started"); | ||
146 | + } | ||
147 | + | ||
148 | + @Deactivate | ||
149 | + protected void deactivate() { | ||
150 | + routingService.stop(); | ||
151 | + | ||
152 | + deviceService.removeListener(deviceListener); | ||
153 | + | ||
154 | + //processIntfFilters(false, configService.getInterfaces()); //TODO necessary? | ||
155 | + | ||
156 | + log.info("Stopped"); | ||
157 | + } | ||
158 | + | ||
159 | + private void getDeviceConfiguration(BgpConfig bgpConfig) { | ||
160 | + Optional<BgpConfig.BgpSpeakerConfig> bgpSpeaker = | ||
161 | + bgpConfig.bgpSpeakers().stream().findAny(); | ||
162 | + | ||
163 | + if (!bgpSpeaker.isPresent()) { | ||
164 | + log.error("BGP speaker configuration not found"); | ||
165 | + return; | ||
166 | + } | ||
167 | + | ||
168 | + Optional<IpAddress> peerAddress = | ||
169 | + bgpSpeaker.get().peers().stream().findAny(); | ||
170 | + | ||
171 | + if (!peerAddress.isPresent()) { | ||
172 | + log.error("BGP speaker must have peers configured"); | ||
173 | + return; | ||
174 | + } | ||
175 | + | ||
176 | + Interface intf = interfaceService.getMatchingInterface(peerAddress.get()); | ||
177 | + | ||
178 | + if (intf == null) { | ||
179 | + log.error("No interface found for peer"); | ||
180 | + return; | ||
181 | + } | ||
182 | + | ||
183 | + // Assume all peers are configured on the same device - this is required | ||
184 | + // by the BGP router | ||
185 | + deviceId = intf.connectPoint().deviceId(); | ||
186 | + | ||
187 | + log.info("Router dpid: {}", deviceId); | ||
188 | + } | ||
189 | + | ||
190 | + private void updateFibEntry(Collection<FibUpdate> updates) { | ||
191 | + Map<FibEntry, Integer> toInstall = new HashMap<>(updates.size()); | ||
192 | + | ||
193 | + for (FibUpdate update : updates) { | ||
194 | + FibEntry entry = update.entry(); | ||
195 | + | ||
196 | + addNextHop(entry); | ||
197 | + | ||
198 | + Integer nextId; | ||
199 | + synchronized (pendingUpdates) { | ||
200 | + nextId = nextHops.get(entry.nextHopIp()); | ||
201 | + } | ||
202 | + | ||
203 | + toInstall.put(update.entry(), nextId); | ||
204 | + } | ||
205 | + | ||
206 | + installFlows(toInstall); | ||
207 | + } | ||
208 | + | ||
209 | + private void installFlows(Map<FibEntry, Integer> entriesToInstall) { | ||
210 | + | ||
211 | + for (Map.Entry<FibEntry, Integer> entry : entriesToInstall.entrySet()) { | ||
212 | + FibEntry fibEntry = entry.getKey(); | ||
213 | + Integer nextId = entry.getValue(); | ||
214 | + | ||
215 | + flowObjectiveService.forward(deviceId, | ||
216 | + generateRibForwardingObj(fibEntry.prefix(), nextId).add()); | ||
217 | + log.trace("Sending forwarding objective {} -> nextId:{}", fibEntry, nextId); | ||
218 | + } | ||
219 | + | ||
220 | + } | ||
221 | + | ||
222 | + private synchronized void deleteFibEntry(Collection<FibUpdate> withdraws) { | ||
223 | + | ||
224 | + for (FibUpdate update : withdraws) { | ||
225 | + FibEntry entry = update.entry(); | ||
226 | + //Integer nextId = nextHops.get(entry.nextHopIp()); | ||
227 | + | ||
228 | + /* Group group = deleteNextHop(entry.prefix()); | ||
229 | + if (group == null) { | ||
230 | + log.warn("Group not found when deleting {}", entry); | ||
231 | + return; | ||
232 | + }*/ | ||
233 | + | ||
234 | + flowObjectiveService.forward(deviceId, | ||
235 | + generateRibForwardingObj(entry.prefix(), null).remove()); | ||
236 | + | ||
237 | + } | ||
238 | + | ||
239 | + } | ||
240 | + | ||
241 | + private ForwardingObjective.Builder generateRibForwardingObj(IpPrefix prefix, | ||
242 | + Integer nextId) { | ||
243 | + TrafficSelector selector = DefaultTrafficSelector.builder() | ||
244 | + .matchEthType(Ethernet.TYPE_IPV4) | ||
245 | + .matchIPDst(prefix) | ||
246 | + .build(); | ||
247 | + | ||
248 | + int priority = prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET; | ||
249 | + | ||
250 | + ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective.builder() | ||
251 | + .fromApp(appId) | ||
252 | + .makePermanent() | ||
253 | + .withSelector(selector) | ||
254 | + .withPriority(priority) | ||
255 | + .withFlag(ForwardingObjective.Flag.SPECIFIC); | ||
256 | + | ||
257 | + if (nextId == null) { | ||
258 | + // Route withdraws are not specified with next hops. Generating | ||
259 | + // dummy treatment as there is no equivalent nextId info. | ||
260 | + fwdBuilder.withTreatment(DefaultTrafficTreatment.builder().build()); | ||
261 | + } else { | ||
262 | + fwdBuilder.nextStep(nextId); | ||
263 | + } | ||
264 | + return fwdBuilder; | ||
265 | + } | ||
266 | + | ||
267 | + private synchronized void addNextHop(FibEntry entry) { | ||
268 | + prefixToNextHop.put(entry.prefix(), entry.nextHopIp()); | ||
269 | + if (nextHopsCount.count(entry.nextHopIp()) == 0) { | ||
270 | + // There was no next hop in the multiset | ||
271 | + | ||
272 | + Interface egressIntf = interfaceService.getMatchingInterface(entry.nextHopIp()); | ||
273 | + if (egressIntf == null) { | ||
274 | + log.warn("no egress interface found for {}", entry); | ||
275 | + return; | ||
276 | + } | ||
277 | + | ||
278 | + NextHopGroupKey groupKey = new NextHopGroupKey(entry.nextHopIp()); | ||
279 | + | ||
280 | + NextHop nextHop = new NextHop(entry.nextHopIp(), entry.nextHopMac(), groupKey); | ||
281 | + | ||
282 | + TrafficTreatment treatment = DefaultTrafficTreatment.builder() | ||
283 | + .setEthSrc(egressIntf.mac()) | ||
284 | + .setEthDst(nextHop.mac()) | ||
285 | + .pushVlan() | ||
286 | + .setVlanId(egressIntf.vlan()) | ||
287 | + .setVlanPcp((byte) 0) | ||
288 | + .setOutput(egressIntf.connectPoint().port()) | ||
289 | + .build(); | ||
290 | + | ||
291 | + int nextId = flowObjectiveService.allocateNextId(); | ||
292 | + | ||
293 | + NextObjective nextObjective = DefaultNextObjective.builder() | ||
294 | + .withId(nextId) | ||
295 | + .addTreatment(treatment) | ||
296 | + .withType(NextObjective.Type.SIMPLE) | ||
297 | + .fromApp(appId) | ||
298 | + .add(); // TODO add callbacks | ||
299 | + | ||
300 | + flowObjectiveService.next(deviceId, nextObjective); | ||
301 | + | ||
302 | + nextHops.put(nextHop.ip(), nextId); | ||
303 | + | ||
304 | + } | ||
305 | + | ||
306 | + nextHopsCount.add(entry.nextHopIp()); | ||
307 | + } | ||
308 | + | ||
309 | + /*private synchronized Group deleteNextHop(IpPrefix prefix) { | ||
310 | + IpAddress nextHopIp = prefixToNextHop.remove(prefix); | ||
311 | + NextHop nextHop = nextHops.get(nextHopIp); | ||
312 | + if (nextHop == null) { | ||
313 | + log.warn("No next hop found when removing prefix {}", prefix); | ||
314 | + return null; | ||
315 | + } | ||
316 | + | ||
317 | + Group group = groupService.getGroup(deviceId, | ||
318 | + new DefaultGroupKey(appKryo. | ||
319 | + serialize(nextHop.group()))); | ||
320 | + | ||
321 | + // FIXME disabling group deletes for now until we verify the logic is OK | ||
322 | + if (nextHopsCount.remove(nextHopIp, 1) <= 1) { | ||
323 | + // There was one or less next hops, so there are now none | ||
324 | + | ||
325 | + log.debug("removing group for next hop {}", nextHop); | ||
326 | + | ||
327 | + nextHops.remove(nextHopIp); | ||
328 | + | ||
329 | + groupService.removeGroup(deviceId, | ||
330 | + new DefaultGroupKey(appKryo.build().serialize(nextHop.group())), | ||
331 | + appId); | ||
332 | + } | ||
333 | + | ||
334 | + return group; | ||
335 | + }*/ | ||
336 | + | ||
337 | + private void processIntfFilters(boolean install, Set<Interface> intfs) { | ||
338 | + log.info("Processing {} router interfaces", intfs.size()); | ||
339 | + for (Interface intf : intfs) { | ||
340 | + if (!intf.connectPoint().deviceId().equals(deviceId)) { | ||
341 | + // Ignore interfaces if they are not on the router switch | ||
342 | + continue; | ||
343 | + } | ||
344 | + | ||
345 | + FilteringObjective.Builder fob = DefaultFilteringObjective.builder(); | ||
346 | + fob.withKey(Criteria.matchInPort(intf.connectPoint().port())) | ||
347 | + .addCondition(Criteria.matchEthDst(intf.mac())) | ||
348 | + .addCondition(Criteria.matchVlanId(intf.vlan())); | ||
349 | + intf.ipAddresses().stream() | ||
350 | + .forEach(ipaddr -> fob.addCondition( | ||
351 | + Criteria.matchIPDst( | ||
352 | + IpPrefix.valueOf(ipaddr.ipAddress(), 32)))); | ||
353 | + fob.permit().fromApp(appId); | ||
354 | + flowObjectiveService.filter( | ||
355 | + deviceId, | ||
356 | + fob.add(new ObjectiveContext() { | ||
357 | + @Override | ||
358 | + public void onSuccess(Objective objective) { | ||
359 | + log.info("Successfully installed interface based " | ||
360 | + + "filtering objectives for intf {}", intf); | ||
361 | + } | ||
362 | + | ||
363 | + @Override | ||
364 | + public void onError(Objective objective, | ||
365 | + ObjectiveError error) { | ||
366 | + log.error("Failed to install interface filters for intf {}: {}", | ||
367 | + intf, error); | ||
368 | + // TODO something more than just logging | ||
369 | + } | ||
370 | + })); | ||
371 | + } | ||
372 | + } | ||
373 | + | ||
374 | + private class InternalFibListener implements FibListener { | ||
375 | + | ||
376 | + @Override | ||
377 | + public void update(Collection<FibUpdate> updates, | ||
378 | + Collection<FibUpdate> withdraws) { | ||
379 | + SingleSwitchRouter.this.deleteFibEntry(withdraws); | ||
380 | + SingleSwitchRouter.this.updateFibEntry(updates); | ||
381 | + } | ||
382 | + } | ||
383 | + | ||
384 | + | ||
385 | + // Triggers driver setup when a device is (re)detected. | ||
386 | + private class InnerDeviceListener implements DeviceListener { | ||
387 | + @Override | ||
388 | + public void event(DeviceEvent event) { | ||
389 | + switch (event.type()) { | ||
390 | + case DEVICE_ADDED: | ||
391 | + case DEVICE_AVAILABILITY_CHANGED: | ||
392 | + if (deviceService.isAvailable(event.subject().id())) { | ||
393 | + log.info("Device connected {}", event.subject().id()); | ||
394 | + if (event.subject().id().equals(deviceId)) { | ||
395 | + processIntfFilters(true, interfaceService.getInterfaces()); | ||
396 | + } | ||
397 | + } | ||
398 | + break; | ||
399 | + // TODO other cases | ||
400 | + case DEVICE_UPDATED: | ||
401 | + case DEVICE_REMOVED: | ||
402 | + case DEVICE_SUSPENDED: | ||
403 | + case PORT_ADDED: | ||
404 | + case PORT_UPDATED: | ||
405 | + case PORT_REMOVED: | ||
406 | + default: | ||
407 | + break; | ||
408 | + } | ||
409 | + } | ||
410 | + } | ||
411 | +} |
-
Please register or login to post a comment