Jonathan Hart
Committed by Ray Milkey

[Falcon] Split FlowObjectives programming component out of

BgpRouter so it can be used
by other applications.

Change-Id: I37a5467e17e677fe44c35704c60429499e0d42ad
...@@ -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 +}