blob: ee9958cffcefbd941dc70fb8c52fe893eecc1c7a [file] [log] [blame]
Jonathan Hartdf207092015-12-10 11:19:25 -08001/*
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
Jonathan Hartc22e8472015-11-17 18:25:45 -080017package org.onosproject.routing.impl;
Jonathan Hartdf207092015-12-10 11:19:25 -080018
19import com.google.common.collect.ConcurrentHashMultiset;
20import com.google.common.collect.HashMultimap;
21import com.google.common.collect.Maps;
22import com.google.common.collect.Multimap;
23import com.google.common.collect.Multiset;
24import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import org.onlab.packet.Ethernet;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.IpPrefix;
Jonathan Hartca47cd72015-12-13 12:31:09 -080032import org.onlab.packet.VlanId;
Jonathan Hartdf207092015-12-10 11:19:25 -080033import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
35import org.onosproject.incubator.net.intf.Interface;
36import org.onosproject.incubator.net.intf.InterfaceService;
Saurav Das49cb5a12016-01-16 22:54:07 -080037import org.onosproject.net.ConnectPoint;
Jonathan Hartdf207092015-12-10 11:19:25 -080038import org.onosproject.net.DeviceId;
Jonathan Hartb3fa42c2016-01-13 09:50:43 -080039import org.onosproject.net.config.NetworkConfigEvent;
40import org.onosproject.net.config.NetworkConfigListener;
Jonathan Hartdf207092015-12-10 11:19:25 -080041import org.onosproject.net.config.NetworkConfigService;
42import org.onosproject.net.device.DeviceEvent;
43import org.onosproject.net.device.DeviceListener;
44import org.onosproject.net.device.DeviceService;
45import org.onosproject.net.flow.DefaultTrafficSelector;
46import org.onosproject.net.flow.DefaultTrafficTreatment;
47import org.onosproject.net.flow.TrafficSelector;
48import org.onosproject.net.flow.TrafficTreatment;
49import org.onosproject.net.flow.criteria.Criteria;
50import org.onosproject.net.flowobjective.DefaultFilteringObjective;
51import org.onosproject.net.flowobjective.DefaultForwardingObjective;
52import org.onosproject.net.flowobjective.DefaultNextObjective;
53import org.onosproject.net.flowobjective.FilteringObjective;
54import org.onosproject.net.flowobjective.FlowObjectiveService;
55import org.onosproject.net.flowobjective.ForwardingObjective;
56import org.onosproject.net.flowobjective.NextObjective;
57import org.onosproject.net.flowobjective.Objective;
58import org.onosproject.net.flowobjective.ObjectiveContext;
59import org.onosproject.net.flowobjective.ObjectiveError;
60import org.onosproject.routing.FibEntry;
61import org.onosproject.routing.FibListener;
62import org.onosproject.routing.FibUpdate;
63import org.onosproject.routing.RoutingService;
Jonathan Hartb3fa42c2016-01-13 09:50:43 -080064import org.onosproject.routing.config.RouterConfig;
Jonathan Hartdf207092015-12-10 11:19:25 -080065import org.slf4j.Logger;
66import org.slf4j.LoggerFactory;
67
68import java.util.Collection;
69import java.util.HashMap;
70import java.util.Map;
Jonathan Hartdf207092015-12-10 11:19:25 -080071import java.util.Set;
72
73/**
74 * Programs routes to a single OpenFlow switch.
75 */
Jonathan Hartc22e8472015-11-17 18:25:45 -080076@Component(immediate = true, enabled = false)
77public class SingleSwitchFibInstaller {
Jonathan Hartdf207092015-12-10 11:19:25 -080078
79 private final Logger log = LoggerFactory.getLogger(getClass());
80
81 private static final int PRIORITY_OFFSET = 100;
82 private static final int PRIORITY_MULTIPLIER = 5;
83
Saurav Das49cb5a12016-01-16 22:54:07 -080084 public static final short ASSIGNED_VLAN = 4094;
85
Jonathan Hartdf207092015-12-10 11:19:25 -080086 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected CoreService coreService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected RoutingService routingService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected InterfaceService interfaceService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected NetworkConfigService networkConfigService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected FlowObjectiveService flowObjectiveService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected DeviceService deviceService;
103
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800104 private InternalDeviceListener deviceListener;
Jonathan Hartdf207092015-12-10 11:19:25 -0800105
106 // Device id of data-plane switch - should be learned from config
107 private DeviceId deviceId;
108
Saurav Das49cb5a12016-01-16 22:54:07 -0800109 private ConnectPoint controlPlaneConnectPoint;
110
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800111 private ApplicationId routerAppId;
Jonathan Hartdf207092015-12-10 11:19:25 -0800112
113 // Reference count for how many times a next hop is used by a route
114 private final Multiset<IpAddress> nextHopsCount = ConcurrentHashMultiset.create();
115
116 // Mapping from prefix to its current next hop
117 private final Map<IpPrefix, IpAddress> prefixToNextHop = Maps.newHashMap();
118
119 // Mapping from next hop IP to next hop object containing group info
120 private final Map<IpAddress, Integer> nextHops = Maps.newHashMap();
121
122 // Stores FIB updates that are waiting for groups to be set up
123 private final Multimap<NextHopGroupKey, FibEntry> pendingUpdates = HashMultimap.create();
124
125
126 @Activate
127 protected void activate() {
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800128 routerAppId = coreService.registerApplication(RoutingService.ROUTER_APP_ID);
Jonathan Hartdf207092015-12-10 11:19:25 -0800129
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800130 deviceListener = new InternalDeviceListener();
Jonathan Hartdf207092015-12-10 11:19:25 -0800131 deviceService.addListener(deviceListener);
132
133 routingService.addFibListener(new InternalFibListener());
134 routingService.start();
135
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800136 updateConfig();
Jonathan Hartdf207092015-12-10 11:19:25 -0800137
138 log.info("Started");
139 }
140
141 @Deactivate
142 protected void deactivate() {
143 routingService.stop();
144
145 deviceService.removeListener(deviceListener);
146
147 //processIntfFilters(false, configService.getInterfaces()); //TODO necessary?
148
149 log.info("Stopped");
150 }
151
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800152 private void updateConfig() {
153 RouterConfig routerConfig =
154 networkConfigService.getConfig(routerAppId, RoutingService.ROUTER_CONFIG_CLASS);
Jonathan Hartdf207092015-12-10 11:19:25 -0800155
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800156 if (routerConfig == null) {
157 log.info("Router config not available");
Jonathan Hartdf207092015-12-10 11:19:25 -0800158 return;
159 }
Saurav Das49cb5a12016-01-16 22:54:07 -0800160 controlPlaneConnectPoint = routerConfig.getControlPlaneConnectPoint();
161 log.info("Control Plane Connect Point: {}", controlPlaneConnectPoint);
Jonathan Hartdf207092015-12-10 11:19:25 -0800162
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800163 deviceId = routerConfig.getControlPlaneConnectPoint().deviceId();
Jonathan Hartdf207092015-12-10 11:19:25 -0800164
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800165 log.info("Router device ID is {}", deviceId);
166
167 updateDevice();
168 }
169
170 private void updateDevice() {
171 if (deviceId != null && deviceService.isAvailable(deviceId)) {
172 processIntfFilters(true, interfaceService.getInterfaces());
Jonathan Hartdf207092015-12-10 11:19:25 -0800173 }
Jonathan Hartdf207092015-12-10 11:19:25 -0800174 }
175
176 private void updateFibEntry(Collection<FibUpdate> updates) {
177 Map<FibEntry, Integer> toInstall = new HashMap<>(updates.size());
178
179 for (FibUpdate update : updates) {
180 FibEntry entry = update.entry();
181
182 addNextHop(entry);
183
184 Integer nextId;
185 synchronized (pendingUpdates) {
186 nextId = nextHops.get(entry.nextHopIp());
187 }
188
189 toInstall.put(update.entry(), nextId);
190 }
191
192 installFlows(toInstall);
193 }
194
195 private void installFlows(Map<FibEntry, Integer> entriesToInstall) {
196
197 for (Map.Entry<FibEntry, Integer> entry : entriesToInstall.entrySet()) {
198 FibEntry fibEntry = entry.getKey();
199 Integer nextId = entry.getValue();
200
201 flowObjectiveService.forward(deviceId,
202 generateRibForwardingObj(fibEntry.prefix(), nextId).add());
203 log.trace("Sending forwarding objective {} -> nextId:{}", fibEntry, nextId);
204 }
205
206 }
207
208 private synchronized void deleteFibEntry(Collection<FibUpdate> withdraws) {
209
210 for (FibUpdate update : withdraws) {
211 FibEntry entry = update.entry();
212 //Integer nextId = nextHops.get(entry.nextHopIp());
213
214 /* Group group = deleteNextHop(entry.prefix());
215 if (group == null) {
216 log.warn("Group not found when deleting {}", entry);
217 return;
218 }*/
219
220 flowObjectiveService.forward(deviceId,
221 generateRibForwardingObj(entry.prefix(), null).remove());
222
223 }
224
225 }
226
227 private ForwardingObjective.Builder generateRibForwardingObj(IpPrefix prefix,
228 Integer nextId) {
229 TrafficSelector selector = DefaultTrafficSelector.builder()
230 .matchEthType(Ethernet.TYPE_IPV4)
231 .matchIPDst(prefix)
232 .build();
233
234 int priority = prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
235
236 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective.builder()
Saurav Das49cb5a12016-01-16 22:54:07 -0800237 .fromApp(routerAppId)
Jonathan Hartdf207092015-12-10 11:19:25 -0800238 .makePermanent()
239 .withSelector(selector)
240 .withPriority(priority)
241 .withFlag(ForwardingObjective.Flag.SPECIFIC);
242
243 if (nextId == null) {
244 // Route withdraws are not specified with next hops. Generating
245 // dummy treatment as there is no equivalent nextId info.
246 fwdBuilder.withTreatment(DefaultTrafficTreatment.builder().build());
247 } else {
248 fwdBuilder.nextStep(nextId);
249 }
250 return fwdBuilder;
251 }
252
253 private synchronized void addNextHop(FibEntry entry) {
254 prefixToNextHop.put(entry.prefix(), entry.nextHopIp());
255 if (nextHopsCount.count(entry.nextHopIp()) == 0) {
256 // There was no next hop in the multiset
257
258 Interface egressIntf = interfaceService.getMatchingInterface(entry.nextHopIp());
259 if (egressIntf == null) {
260 log.warn("no egress interface found for {}", entry);
261 return;
262 }
263
264 NextHopGroupKey groupKey = new NextHopGroupKey(entry.nextHopIp());
265
266 NextHop nextHop = new NextHop(entry.nextHopIp(), entry.nextHopMac(), groupKey);
267
Jonathan Hartca47cd72015-12-13 12:31:09 -0800268 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
Jonathan Hartdf207092015-12-10 11:19:25 -0800269 .setEthSrc(egressIntf.mac())
Jonathan Hartca47cd72015-12-13 12:31:09 -0800270 .setEthDst(nextHop.mac());
271
Saurav Das49cb5a12016-01-16 22:54:07 -0800272 TrafficSelector.Builder metabuilder = null;
Jonathan Hartca47cd72015-12-13 12:31:09 -0800273 if (!egressIntf.vlan().equals(VlanId.NONE)) {
274 treatment.pushVlan()
275 .setVlanId(egressIntf.vlan())
276 .setVlanPcp((byte) 0);
Saurav Das49cb5a12016-01-16 22:54:07 -0800277 } else {
278 // untagged outgoing port may require internal vlan in some pipelines
279 metabuilder = DefaultTrafficSelector.builder();
280 metabuilder.matchVlanId(VlanId.vlanId(ASSIGNED_VLAN));
Jonathan Hartca47cd72015-12-13 12:31:09 -0800281 }
282
283 treatment.setOutput(egressIntf.connectPoint().port());
Jonathan Hartdf207092015-12-10 11:19:25 -0800284
285 int nextId = flowObjectiveService.allocateNextId();
Saurav Das49cb5a12016-01-16 22:54:07 -0800286 NextObjective.Builder nextBuilder = DefaultNextObjective.builder()
Jonathan Hartdf207092015-12-10 11:19:25 -0800287 .withId(nextId)
Jonathan Hartca47cd72015-12-13 12:31:09 -0800288 .addTreatment(treatment.build())
Jonathan Hartdf207092015-12-10 11:19:25 -0800289 .withType(NextObjective.Type.SIMPLE)
Saurav Das49cb5a12016-01-16 22:54:07 -0800290 .fromApp(routerAppId);
291 if (metabuilder != null) {
292 nextBuilder.withMeta(metabuilder.build());
293 }
Jonathan Hartdf207092015-12-10 11:19:25 -0800294
Saurav Das49cb5a12016-01-16 22:54:07 -0800295 NextObjective nextObjective = nextBuilder.add(); // TODO add callbacks
Jonathan Hartdf207092015-12-10 11:19:25 -0800296 flowObjectiveService.next(deviceId, nextObjective);
297
298 nextHops.put(nextHop.ip(), nextId);
299
300 }
301
302 nextHopsCount.add(entry.nextHopIp());
303 }
304
305 /*private synchronized Group deleteNextHop(IpPrefix prefix) {
306 IpAddress nextHopIp = prefixToNextHop.remove(prefix);
307 NextHop nextHop = nextHops.get(nextHopIp);
308 if (nextHop == null) {
309 log.warn("No next hop found when removing prefix {}", prefix);
310 return null;
311 }
312
313 Group group = groupService.getGroup(deviceId,
314 new DefaultGroupKey(appKryo.
315 serialize(nextHop.group())));
316
317 // FIXME disabling group deletes for now until we verify the logic is OK
318 if (nextHopsCount.remove(nextHopIp, 1) <= 1) {
319 // There was one or less next hops, so there are now none
320
321 log.debug("removing group for next hop {}", nextHop);
322
323 nextHops.remove(nextHopIp);
324
325 groupService.removeGroup(deviceId,
326 new DefaultGroupKey(appKryo.build().serialize(nextHop.group())),
327 appId);
328 }
329
330 return group;
331 }*/
332
333 private void processIntfFilters(boolean install, Set<Interface> intfs) {
334 log.info("Processing {} router interfaces", intfs.size());
335 for (Interface intf : intfs) {
336 if (!intf.connectPoint().deviceId().equals(deviceId)) {
337 // Ignore interfaces if they are not on the router switch
338 continue;
339 }
340
341 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
Saurav Das49cb5a12016-01-16 22:54:07 -0800342 // first add filter for the interface
Jonathan Hartdf207092015-12-10 11:19:25 -0800343 fob.withKey(Criteria.matchInPort(intf.connectPoint().port()))
Saurav Das49cb5a12016-01-16 22:54:07 -0800344 .addCondition(Criteria.matchEthDst(intf.mac()))
345 .addCondition(Criteria.matchVlanId(intf.vlan()));
Jonathan Hart6344f572015-12-15 08:26:25 -0800346 fob.withPriority(PRIORITY_OFFSET);
Saurav Das49cb5a12016-01-16 22:54:07 -0800347 if (intf.vlan() == VlanId.NONE) {
348 TrafficTreatment tt = DefaultTrafficTreatment.builder()
349 .pushVlan().setVlanId(VlanId.vlanId(ASSIGNED_VLAN)).build();
350 fob.withMeta(tt);
351 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800352
Saurav Das49cb5a12016-01-16 22:54:07 -0800353 fob.permit().fromApp(routerAppId);
354 sendFilteringObjective(install, fob, intf);
355 if (controlPlaneConnectPoint != null) {
356 // then add the same mac/vlan filters for control-plane connect point
357 fob.withKey(Criteria.matchInPort(controlPlaneConnectPoint.port()));
358 sendFilteringObjective(install, fob, intf);
359 }
Jonathan Hartdf207092015-12-10 11:19:25 -0800360 }
361 }
362
Saurav Das49cb5a12016-01-16 22:54:07 -0800363 private void sendFilteringObjective(boolean install, FilteringObjective.Builder fob,
364 Interface intf) {
365 flowObjectiveService.filter(
366 deviceId,
367 fob.add(new ObjectiveContext() {
368 @Override
369 public void onSuccess(Objective objective) {
370 log.info("Successfully installed interface based "
371 + "filtering objectives for intf {}", intf);
372 }
373
374 @Override
375 public void onError(Objective objective,
376 ObjectiveError error) {
377 log.error("Failed to install interface filters for intf {}: {}",
378 intf, error);
379 // TODO something more than just logging
380 }
381 }));
382 }
383
Jonathan Hartdf207092015-12-10 11:19:25 -0800384 private class InternalFibListener implements FibListener {
385
386 @Override
387 public void update(Collection<FibUpdate> updates,
388 Collection<FibUpdate> withdraws) {
Jonathan Hartc22e8472015-11-17 18:25:45 -0800389 SingleSwitchFibInstaller.this.deleteFibEntry(withdraws);
390 SingleSwitchFibInstaller.this.updateFibEntry(updates);
Jonathan Hartdf207092015-12-10 11:19:25 -0800391 }
392 }
393
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800394 /**
395 * Listener for device events used to trigger driver setup when a device is
396 * (re)detected.
397 */
398 private class InternalDeviceListener implements DeviceListener {
Jonathan Hartdf207092015-12-10 11:19:25 -0800399 @Override
400 public void event(DeviceEvent event) {
401 switch (event.type()) {
402 case DEVICE_ADDED:
403 case DEVICE_AVAILABILITY_CHANGED:
404 if (deviceService.isAvailable(event.subject().id())) {
405 log.info("Device connected {}", event.subject().id());
406 if (event.subject().id().equals(deviceId)) {
407 processIntfFilters(true, interfaceService.getInterfaces());
408 }
409 }
410 break;
411 // TODO other cases
412 case DEVICE_UPDATED:
413 case DEVICE_REMOVED:
414 case DEVICE_SUSPENDED:
415 case PORT_ADDED:
416 case PORT_UPDATED:
417 case PORT_REMOVED:
418 default:
419 break;
420 }
421 }
422 }
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800423
424 /**
425 * Listener for network config events.
426 */
427 private class InternalNetworkConfigListener implements NetworkConfigListener {
428 @Override
429 public void event(NetworkConfigEvent event) {
430 if (event.subject().equals(RoutingService.ROUTER_CONFIG_CLASS)) {
431 switch (event.type()) {
432 case CONFIG_ADDED:
433 case CONFIG_UPDATED:
434 updateConfig();
435 break;
436 case CONFIG_REGISTERED:
437 case CONFIG_UNREGISTERED:
438 case CONFIG_REMOVED:
439 default:
440 break;
441 }
442 }
443 }
444 }
Jonathan Hartdf207092015-12-10 11:19:25 -0800445}