blob: 24dd468ae2f3d2f79e46ed99732a9e65950d953f [file] [log] [blame]
Jonathan Hartdf207092015-12-10 11:19:25 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Jonathan Hartdf207092015-12-10 11:19:25 -08003 *
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;
Jonathan Hartdf207092015-12-10 11:19:25 -080020import com.google.common.collect.Maps;
Jonathan Hartdf207092015-12-10 11:19:25 -080021import com.google.common.collect.Multiset;
22import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
Jonathan Hartb9401902016-02-02 18:46:01 -080025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
Jonathan Hartdf207092015-12-10 11:19:25 -080027import 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;
Charles Chand55e84d2016-03-30 17:54:24 -070032import org.onlab.packet.MacAddress;
Jonathan Hartca47cd72015-12-13 12:31:09 -080033import org.onlab.packet.VlanId;
Jonathan Hartb9401902016-02-02 18:46:01 -080034import org.onlab.util.Tools;
gaurava2e61a52016-05-05 03:39:31 +053035import org.onosproject.app.ApplicationService;
Jonathan Hartb9401902016-02-02 18:46:01 -080036import org.onosproject.cfg.ComponentConfigService;
Jonathan Hartdf207092015-12-10 11:19:25 -080037import org.onosproject.core.ApplicationId;
38import org.onosproject.core.CoreService;
Charles Chand55e84d2016-03-30 17:54:24 -070039import org.onosproject.incubator.net.config.basics.McastConfig;
Jonathan Hartdf207092015-12-10 11:19:25 -080040import org.onosproject.incubator.net.intf.Interface;
gaurav164cf6d2016-03-25 21:43:04 +053041import org.onosproject.incubator.net.intf.InterfaceEvent;
42import org.onosproject.incubator.net.intf.InterfaceListener;
Jonathan Hartdf207092015-12-10 11:19:25 -080043import org.onosproject.incubator.net.intf.InterfaceService;
Jonathan Harta2eb9ff2016-04-13 21:27:06 -070044import org.onosproject.incubator.net.routing.ResolvedRoute;
45import org.onosproject.incubator.net.routing.RouteEvent;
46import org.onosproject.incubator.net.routing.RouteListener;
47import org.onosproject.incubator.net.routing.RouteService;
Saurav Das49cb5a12016-01-16 22:54:07 -080048import org.onosproject.net.ConnectPoint;
Jonathan Hartdf207092015-12-10 11:19:25 -080049import org.onosproject.net.DeviceId;
Charles Chand55e84d2016-03-30 17:54:24 -070050import org.onosproject.net.config.ConfigFactory;
Jonathan Hartb3fa42c2016-01-13 09:50:43 -080051import org.onosproject.net.config.NetworkConfigEvent;
52import org.onosproject.net.config.NetworkConfigListener;
Charles Chand55e84d2016-03-30 17:54:24 -070053import org.onosproject.net.config.NetworkConfigRegistry;
Jonathan Hartdf207092015-12-10 11:19:25 -080054import org.onosproject.net.config.NetworkConfigService;
Charles Chand55e84d2016-03-30 17:54:24 -070055import org.onosproject.net.config.basics.SubjectFactories;
Jonathan Hartdf207092015-12-10 11:19:25 -080056import org.onosproject.net.device.DeviceEvent;
57import org.onosproject.net.device.DeviceListener;
58import org.onosproject.net.device.DeviceService;
59import org.onosproject.net.flow.DefaultTrafficSelector;
60import org.onosproject.net.flow.DefaultTrafficTreatment;
61import org.onosproject.net.flow.TrafficSelector;
62import org.onosproject.net.flow.TrafficTreatment;
63import org.onosproject.net.flow.criteria.Criteria;
64import org.onosproject.net.flowobjective.DefaultFilteringObjective;
65import org.onosproject.net.flowobjective.DefaultForwardingObjective;
66import org.onosproject.net.flowobjective.DefaultNextObjective;
Jonathan Harta2eb9ff2016-04-13 21:27:06 -070067import org.onosproject.net.flowobjective.DefaultObjectiveContext;
Jonathan Hartdf207092015-12-10 11:19:25 -080068import org.onosproject.net.flowobjective.FilteringObjective;
69import org.onosproject.net.flowobjective.FlowObjectiveService;
70import org.onosproject.net.flowobjective.ForwardingObjective;
71import org.onosproject.net.flowobjective.NextObjective;
Jonathan Hartdf207092015-12-10 11:19:25 -080072import org.onosproject.net.flowobjective.ObjectiveContext;
Jonathan Hartdf207092015-12-10 11:19:25 -080073import org.onosproject.routing.RoutingService;
Jonathan Hartb3fa42c2016-01-13 09:50:43 -080074import org.onosproject.routing.config.RouterConfig;
Jonathan Hartb9401902016-02-02 18:46:01 -080075import org.osgi.service.component.ComponentContext;
Jonathan Hartdf207092015-12-10 11:19:25 -080076import org.slf4j.Logger;
77import org.slf4j.LoggerFactory;
78
Jonathan Hartb9401902016-02-02 18:46:01 -080079import java.util.Dictionary;
Jonathan Hart883fd372016-02-10 14:36:15 -080080import java.util.List;
Jonathan Hartdf207092015-12-10 11:19:25 -080081import java.util.Map;
Jonathan Hartdf207092015-12-10 11:19:25 -080082import java.util.Set;
Jonathan Hart883fd372016-02-10 14:36:15 -080083import java.util.stream.Collectors;
Jonathan Hartdf207092015-12-10 11:19:25 -080084
85/**
86 * Programs routes to a single OpenFlow switch.
87 */
Jonathan Hartc22e8472015-11-17 18:25:45 -080088@Component(immediate = true, enabled = false)
89public class SingleSwitchFibInstaller {
Jonathan Hartdf207092015-12-10 11:19:25 -080090
91 private final Logger log = LoggerFactory.getLogger(getClass());
gaurava2e61a52016-05-05 03:39:31 +053092 private static final String APP_NAME = "org.onosproject.vrouter";
Jonathan Hartdf207092015-12-10 11:19:25 -080093
94 private static final int PRIORITY_OFFSET = 100;
95 private static final int PRIORITY_MULTIPLIER = 5;
96
Charles Chan03a73e02016-10-24 14:52:01 -070097 // FIXME: This should be eliminated when we have an API in SR that
98 // programs the fabric switches for VR
Saurav Das49cb5a12016-01-16 22:54:07 -080099 public static final short ASSIGNED_VLAN = 4094;
100
Jonathan Hartdf207092015-12-10 11:19:25 -0800101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected CoreService coreService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700105 protected RouteService routeService;
Jonathan Hartdf207092015-12-10 11:19:25 -0800106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected InterfaceService interfaceService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected NetworkConfigService networkConfigService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Charles Chand55e84d2016-03-30 17:54:24 -0700114 protected NetworkConfigRegistry networkConfigRegistry;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hartb9401902016-02-02 18:46:01 -0800117 protected ComponentConfigService componentConfigService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hartdf207092015-12-10 11:19:25 -0800120 protected FlowObjectiveService flowObjectiveService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected DeviceService deviceService;
124
gaurava2e61a52016-05-05 03:39:31 +0530125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected ApplicationService applicationService;
127
Jonathan Hartb9401902016-02-02 18:46:01 -0800128 @Property(name = "routeToNextHop", boolValue = false,
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530129 label = "Install a /32 or /128 route to each next hop")
Jonathan Hartb9401902016-02-02 18:46:01 -0800130 private boolean routeToNextHop = false;
131
Jonathan Hartdf207092015-12-10 11:19:25 -0800132 // Device id of data-plane switch - should be learned from config
133 private DeviceId deviceId;
134
Saurav Das49cb5a12016-01-16 22:54:07 -0800135 private ConnectPoint controlPlaneConnectPoint;
136
Jonathan Hart883fd372016-02-10 14:36:15 -0800137 private List<String> interfaces;
138
Charles Chand55e84d2016-03-30 17:54:24 -0700139 private ApplicationId coreAppId;
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800140 private ApplicationId routerAppId;
gaurava2e61a52016-05-05 03:39:31 +0530141 private ApplicationId vrouterAppId;
Jonathan Hartdf207092015-12-10 11:19:25 -0800142
143 // Reference count for how many times a next hop is used by a route
144 private final Multiset<IpAddress> nextHopsCount = ConcurrentHashMultiset.create();
145
146 // Mapping from prefix to its current next hop
147 private final Map<IpPrefix, IpAddress> prefixToNextHop = Maps.newHashMap();
148
149 // Mapping from next hop IP to next hop object containing group info
150 private final Map<IpAddress, Integer> nextHops = Maps.newHashMap();
151
Jonathan Hart25bd6682016-06-03 10:45:47 -0700152 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
153 private final InternalInterfaceListener internalInterfaceList = new InternalInterfaceListener();
154 private final InternalRouteListener routeListener = new InternalRouteListener();
155 private final InternalNetworkConfigListener configListener = new InternalNetworkConfigListener();
Jonathan Hartdf207092015-12-10 11:19:25 -0800156
Charles Chand55e84d2016-03-30 17:54:24 -0700157 private ConfigFactory<ApplicationId, McastConfig> mcastConfigFactory =
158 new ConfigFactory<ApplicationId, McastConfig>(SubjectFactories.APP_SUBJECT_FACTORY,
159 McastConfig.class, "multicast") {
160 @Override
161 public McastConfig createConfig() {
162 return new McastConfig();
163 }
164 };
165
Jonathan Hartdf207092015-12-10 11:19:25 -0800166 @Activate
Jonathan Hartb9401902016-02-02 18:46:01 -0800167 protected void activate(ComponentContext context) {
Jonathan Hartb9401902016-02-02 18:46:01 -0800168 componentConfigService.registerProperties(getClass());
Jonathan Hart9ad777f2016-02-19 12:44:36 -0800169 modified(context);
170
Charles Chand55e84d2016-03-30 17:54:24 -0700171 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
Jonathan Hart9ad777f2016-02-19 12:44:36 -0800172 routerAppId = coreService.registerApplication(RoutingService.ROUTER_APP_ID);
gaurava2e61a52016-05-05 03:39:31 +0530173 vrouterAppId = coreService.registerApplication(APP_NAME);
Jonathan Hartb9401902016-02-02 18:46:01 -0800174
Charles Chand55e84d2016-03-30 17:54:24 -0700175 networkConfigRegistry.registerConfigFactory(mcastConfigFactory);
176
Jonathan Hart25bd6682016-06-03 10:45:47 -0700177 networkConfigService.addListener(configListener);
Jonathan Hartdf207092015-12-10 11:19:25 -0800178 deviceService.addListener(deviceListener);
gaurav164cf6d2016-03-25 21:43:04 +0530179 interfaceService.addListener(internalInterfaceList);
180
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800181 updateConfig();
Jonathan Hartdf207092015-12-10 11:19:25 -0800182
Charles Chan00d8b5f2016-12-04 17:17:39 -0800183 // FIXME: There can be an issue when this component is deactivated before vRouter.
184 // This will be addressed in CORD-710.
Jonathan Hartf8035d32016-06-16 16:23:26 -0700185 applicationService.registerDeactivateHook(vrouterAppId, () -> cleanUp());
gaurava2e61a52016-05-05 03:39:31 +0530186
Jonathan Hartdf207092015-12-10 11:19:25 -0800187 log.info("Started");
188 }
189
190 @Deactivate
191 protected void deactivate() {
Charles Chan00d8b5f2016-12-04 17:17:39 -0800192 // FIXME: This will also remove flows when an instance goes down.
193 // This is a temporary solution and should be addressed in CORD-710.
194 cleanUp();
195
Jonathan Hartdf207092015-12-10 11:19:25 -0800196 deviceService.removeListener(deviceListener);
gaurav164cf6d2016-03-25 21:43:04 +0530197 interfaceService.removeListener(internalInterfaceList);
Jonathan Hart25bd6682016-06-03 10:45:47 -0700198 networkConfigService.removeListener(configListener);
gaurav164cf6d2016-03-25 21:43:04 +0530199
Jonathan Hartb9401902016-02-02 18:46:01 -0800200 componentConfigService.unregisterProperties(getClass(), false);
201
Jonathan Hartdf207092015-12-10 11:19:25 -0800202 log.info("Stopped");
203 }
204
Jonathan Hartb9401902016-02-02 18:46:01 -0800205 @Modified
206 protected void modified(ComponentContext context) {
207 Dictionary<?, ?> properties = context.getProperties();
208 if (properties == null) {
209 return;
210 }
211
212 String strRouteToNextHop = Tools.get(properties, "routeToNextHop");
213 routeToNextHop = Boolean.parseBoolean(strRouteToNextHop);
214
215 log.info("routeToNextHop set to {}", routeToNextHop);
216 }
217
gaurava2e61a52016-05-05 03:39:31 +0530218 //remove filtering objectives and routes before deactivate.
219 private void cleanUp() {
gaurav25118892016-08-24 05:35:03 +0530220 //remove the route listener
221 routeService.removeListener(routeListener);
222
gaurava2e61a52016-05-05 03:39:31 +0530223 //clean up the routes.
224 for (Map.Entry<IpPrefix, IpAddress> routes: prefixToNextHop.entrySet()) {
Charles Chan8fe9f4c2016-10-24 16:46:25 -0700225 deleteRoute(new ResolvedRoute(routes.getKey(), null, null, null));
gaurava2e61a52016-05-05 03:39:31 +0530226 }
gaurav25118892016-08-24 05:35:03 +0530227
gaurava2e61a52016-05-05 03:39:31 +0530228 //clean up the filtering objective for interfaces.
229 Set<Interface> intfs = getInterfaces();
gaurav25118892016-08-24 05:35:03 +0530230 if (!intfs.isEmpty()) {
231 processIntfFilters(false, intfs);
232 }
gaurava2e61a52016-05-05 03:39:31 +0530233 }
234
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800235 private void updateConfig() {
236 RouterConfig routerConfig =
237 networkConfigService.getConfig(routerAppId, RoutingService.ROUTER_CONFIG_CLASS);
Jonathan Hartdf207092015-12-10 11:19:25 -0800238
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800239 if (routerConfig == null) {
240 log.info("Router config not available");
Jonathan Hartdf207092015-12-10 11:19:25 -0800241 return;
242 }
Saurav Das49cb5a12016-01-16 22:54:07 -0800243 controlPlaneConnectPoint = routerConfig.getControlPlaneConnectPoint();
244 log.info("Control Plane Connect Point: {}", controlPlaneConnectPoint);
Jonathan Hartdf207092015-12-10 11:19:25 -0800245
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800246 deviceId = routerConfig.getControlPlaneConnectPoint().deviceId();
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800247 log.info("Router device ID is {}", deviceId);
248
Jonathan Hart883fd372016-02-10 14:36:15 -0800249 interfaces = routerConfig.getInterfaces();
250 log.info("Using interfaces: {}", interfaces.isEmpty() ? "all" : interfaces);
251
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700252 routeService.addListener(routeListener);
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800253 updateDevice();
254 }
255
gaurav25118892016-08-24 05:35:03 +0530256 //remove the filtering objective for interfaces which are no longer part of vRouter config.
257 private void removeFilteringObjectives(NetworkConfigEvent event) {
258 RouterConfig prevRouterConfig = (RouterConfig) event.prevConfig().get();
259 List<String> prevInterfaces = prevRouterConfig.getInterfaces();
260
261 Set<Interface> previntfs = filterInterfaces(prevInterfaces);
262 //if previous interface list is empty it means filtering objectives are
263 //installed for all the interfaces.
264 if (previntfs.isEmpty() && !interfaces.isEmpty()) {
265 Set<Interface> allIntfs = interfaceService.getInterfaces();
266 for (Interface allIntf : allIntfs) {
267 if (!interfaces.contains(allIntf.name())) {
268 processIntfFilter(false, allIntf);
269 }
270 }
271 return;
272 }
273
274 //remove the filtering objective for the interfaces which are not
275 //part of updated interfaces list.
276 for (Interface prevIntf : previntfs) {
277 if (!interfaces.contains(prevIntf.name())) {
278 processIntfFilter(false, prevIntf);
279 }
280 }
281 }
282
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800283 private void updateDevice() {
284 if (deviceId != null && deviceService.isAvailable(deviceId)) {
gaurava2e61a52016-05-05 03:39:31 +0530285 Set<Interface> intfs = getInterfaces();
Jonathan Hart883fd372016-02-10 14:36:15 -0800286 processIntfFilters(true, intfs);
Jonathan Hartdf207092015-12-10 11:19:25 -0800287 }
Jonathan Hartdf207092015-12-10 11:19:25 -0800288 }
289
gaurava2e61a52016-05-05 03:39:31 +0530290 private Set<Interface> getInterfaces() {
291 Set<Interface> intfs;
Charles Chan14ed0b62016-12-03 22:40:36 -0800292 if (interfaces == null || interfaces.isEmpty()) {
gaurava2e61a52016-05-05 03:39:31 +0530293 intfs = interfaceService.getInterfaces();
294 } else {
295 // TODO need to fix by making interface names globally unique
gaurav25118892016-08-24 05:35:03 +0530296 intfs = filterInterfaces(interfaces);
gaurava2e61a52016-05-05 03:39:31 +0530297 }
298 return intfs;
299 }
300
gaurav25118892016-08-24 05:35:03 +0530301 private Set<Interface> filterInterfaces(List<String> interfaces) {
Charles Chan00d8b5f2016-12-04 17:17:39 -0800302 return interfaceService.getInterfaces().stream()
gaurav25118892016-08-24 05:35:03 +0530303 .filter(intf -> intf.connectPoint().deviceId().equals(deviceId))
304 .filter(intf -> interfaces.contains(intf.name()))
305 .collect(Collectors.toSet());
gaurav25118892016-08-24 05:35:03 +0530306 }
307
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700308 private void updateRoute(ResolvedRoute route) {
309 addNextHop(route);
Jonathan Hartdf207092015-12-10 11:19:25 -0800310
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700311 Integer nextId;
312 synchronized (this) {
313 nextId = nextHops.get(route.nextHop());
Jonathan Hartdf207092015-12-10 11:19:25 -0800314 }
315
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700316 flowObjectiveService.forward(deviceId,
317 generateRibForwardingObj(route.prefix(), nextId).add());
318 log.trace("Sending forwarding objective {} -> nextId:{}", route, nextId);
Jonathan Hartdf207092015-12-10 11:19:25 -0800319 }
320
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700321 private synchronized void deleteRoute(ResolvedRoute route) {
322 //Integer nextId = nextHops.get(route.nextHop());
Jonathan Hartdf207092015-12-10 11:19:25 -0800323
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700324 /* Group group = deleteNextHop(route.prefix());
325 if (group == null) {
326 log.warn("Group not found when deleting {}", route);
327 return;
328 }*/
Jonathan Hartdf207092015-12-10 11:19:25 -0800329
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700330 flowObjectiveService.forward(deviceId,
331 generateRibForwardingObj(route.prefix(), null).remove());
Jonathan Hartdf207092015-12-10 11:19:25 -0800332 }
333
334 private ForwardingObjective.Builder generateRibForwardingObj(IpPrefix prefix,
335 Integer nextId) {
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530336 TrafficSelector selector = buildIpSelectorFromIpPrefix(prefix).build();
Jonathan Hartdf207092015-12-10 11:19:25 -0800337 int priority = prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
338
339 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective.builder()
Saurav Das49cb5a12016-01-16 22:54:07 -0800340 .fromApp(routerAppId)
Jonathan Hartdf207092015-12-10 11:19:25 -0800341 .makePermanent()
342 .withSelector(selector)
343 .withPriority(priority)
344 .withFlag(ForwardingObjective.Flag.SPECIFIC);
345
346 if (nextId == null) {
347 // Route withdraws are not specified with next hops. Generating
348 // dummy treatment as there is no equivalent nextId info.
349 fwdBuilder.withTreatment(DefaultTrafficTreatment.builder().build());
350 } else {
351 fwdBuilder.nextStep(nextId);
352 }
353 return fwdBuilder;
354 }
355
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530356 /**
357 * Method to build IPv4 or IPv6 selector.
358 *
359 * @param prefixToMatch the prefix to match
360 * @return the traffic selector builder
361 */
362 private TrafficSelector.Builder buildIpSelectorFromIpPrefix(IpPrefix prefixToMatch) {
363 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
364 // If the prefix is IPv4
365 if (prefixToMatch.isIp4()) {
366 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
367 selectorBuilder.matchIPDst(prefixToMatch);
368 return selectorBuilder;
369 }
370 // If the prefix is IPv6
371 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
372 selectorBuilder.matchIPv6Dst(prefixToMatch);
373 return selectorBuilder;
374 }
375
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700376 private synchronized void addNextHop(ResolvedRoute route) {
377 prefixToNextHop.put(route.prefix(), route.nextHop());
378 if (nextHopsCount.count(route.nextHop()) == 0) {
Jonathan Hartdf207092015-12-10 11:19:25 -0800379 // There was no next hop in the multiset
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700380 Interface egressIntf = interfaceService.getMatchingInterface(route.nextHop());
Jonathan Hartdf207092015-12-10 11:19:25 -0800381 if (egressIntf == null) {
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700382 log.warn("no egress interface found for {}", route);
Jonathan Hartdf207092015-12-10 11:19:25 -0800383 return;
384 }
385
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700386 NextHopGroupKey groupKey = new NextHopGroupKey(route.nextHop());
Jonathan Hartdf207092015-12-10 11:19:25 -0800387
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700388 NextHop nextHop = new NextHop(route.nextHop(), route.nextHopMac(), groupKey);
Jonathan Hartdf207092015-12-10 11:19:25 -0800389
Jonathan Hartca47cd72015-12-13 12:31:09 -0800390 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
Jonathan Hartdf207092015-12-10 11:19:25 -0800391 .setEthSrc(egressIntf.mac())
Jonathan Hartca47cd72015-12-13 12:31:09 -0800392 .setEthDst(nextHop.mac());
393
Saurav Das49cb5a12016-01-16 22:54:07 -0800394 TrafficSelector.Builder metabuilder = null;
Jonathan Hartca47cd72015-12-13 12:31:09 -0800395 if (!egressIntf.vlan().equals(VlanId.NONE)) {
396 treatment.pushVlan()
397 .setVlanId(egressIntf.vlan())
398 .setVlanPcp((byte) 0);
Saurav Das49cb5a12016-01-16 22:54:07 -0800399 } else {
400 // untagged outgoing port may require internal vlan in some pipelines
401 metabuilder = DefaultTrafficSelector.builder();
402 metabuilder.matchVlanId(VlanId.vlanId(ASSIGNED_VLAN));
Jonathan Hartca47cd72015-12-13 12:31:09 -0800403 }
404
405 treatment.setOutput(egressIntf.connectPoint().port());
Jonathan Hartdf207092015-12-10 11:19:25 -0800406
407 int nextId = flowObjectiveService.allocateNextId();
Saurav Das49cb5a12016-01-16 22:54:07 -0800408 NextObjective.Builder nextBuilder = DefaultNextObjective.builder()
Jonathan Hartdf207092015-12-10 11:19:25 -0800409 .withId(nextId)
Jonathan Hartca47cd72015-12-13 12:31:09 -0800410 .addTreatment(treatment.build())
Jonathan Hartdf207092015-12-10 11:19:25 -0800411 .withType(NextObjective.Type.SIMPLE)
Saurav Das49cb5a12016-01-16 22:54:07 -0800412 .fromApp(routerAppId);
413 if (metabuilder != null) {
414 nextBuilder.withMeta(metabuilder.build());
415 }
Jonathan Hartdf207092015-12-10 11:19:25 -0800416
Saurav Das49cb5a12016-01-16 22:54:07 -0800417 NextObjective nextObjective = nextBuilder.add(); // TODO add callbacks
Jonathan Hartdf207092015-12-10 11:19:25 -0800418 flowObjectiveService.next(deviceId, nextObjective);
419
420 nextHops.put(nextHop.ip(), nextId);
421
Jonathan Hartb9401902016-02-02 18:46:01 -0800422 if (routeToNextHop) {
423 // Install route to next hop
424 ForwardingObjective fob =
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700425 generateRibForwardingObj(IpPrefix.valueOf(route.nextHop(), 32), nextId).add();
Jonathan Hartb9401902016-02-02 18:46:01 -0800426 flowObjectiveService.forward(deviceId, fob);
427 }
Jonathan Hartdf207092015-12-10 11:19:25 -0800428 }
429
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700430 nextHopsCount.add(route.nextHop());
Jonathan Hartdf207092015-12-10 11:19:25 -0800431 }
432
433 /*private synchronized Group deleteNextHop(IpPrefix prefix) {
434 IpAddress nextHopIp = prefixToNextHop.remove(prefix);
435 NextHop nextHop = nextHops.get(nextHopIp);
436 if (nextHop == null) {
437 log.warn("No next hop found when removing prefix {}", prefix);
438 return null;
439 }
440
441 Group group = groupService.getGroup(deviceId,
442 new DefaultGroupKey(appKryo.
443 serialize(nextHop.group())));
444
445 // FIXME disabling group deletes for now until we verify the logic is OK
446 if (nextHopsCount.remove(nextHopIp, 1) <= 1) {
447 // There was one or less next hops, so there are now none
448
449 log.debug("removing group for next hop {}", nextHop);
450
451 nextHops.remove(nextHopIp);
452
453 groupService.removeGroup(deviceId,
454 new DefaultGroupKey(appKryo.build().serialize(nextHop.group())),
455 appId);
456 }
457
458 return group;
459 }*/
460
461 private void processIntfFilters(boolean install, Set<Interface> intfs) {
462 log.info("Processing {} router interfaces", intfs.size());
463 for (Interface intf : intfs) {
464 if (!intf.connectPoint().deviceId().equals(deviceId)) {
465 // Ignore interfaces if they are not on the router switch
466 continue;
467 }
468
gaurav164cf6d2016-03-25 21:43:04 +0530469 createFilteringObjective(install, intf);
Charles Chand55e84d2016-03-30 17:54:24 -0700470 createMcastFilteringObjective(install, intf);
gaurav164cf6d2016-03-25 21:43:04 +0530471 }
472 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800473
gaurav164cf6d2016-03-25 21:43:04 +0530474 //process filtering objective for interface add/remove.
475 private void processIntfFilter(boolean install, Interface intf) {
476
477 if (!intf.connectPoint().deviceId().equals(deviceId)) {
478 // Ignore interfaces if they are not on the router switch
479 return;
480 }
gaurav25118892016-08-24 05:35:03 +0530481 if (!interfaces.contains(intf.name()) && install) {
482 return;
483 }
gaurav164cf6d2016-03-25 21:43:04 +0530484
485 createFilteringObjective(install, intf);
Charles Chand55e84d2016-03-30 17:54:24 -0700486 createMcastFilteringObjective(install, intf);
gaurav164cf6d2016-03-25 21:43:04 +0530487 }
488
489 //create filtering objective for interface
490 private void createFilteringObjective(boolean install, Interface intf) {
Charles Chand55e84d2016-03-30 17:54:24 -0700491 VlanId assignedVlan = (egressVlan().equals(VlanId.NONE)) ?
492 VlanId.vlanId(ASSIGNED_VLAN) :
493 egressVlan();
gaurav164cf6d2016-03-25 21:43:04 +0530494
495 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
496 // first add filter for the interface
497 fob.withKey(Criteria.matchInPort(intf.connectPoint().port()))
498 .addCondition(Criteria.matchEthDst(intf.mac()))
499 .addCondition(Criteria.matchVlanId(intf.vlan()));
500 fob.withPriority(PRIORITY_OFFSET);
501 if (intf.vlan() == VlanId.NONE) {
502 TrafficTreatment tt = DefaultTrafficTreatment.builder()
Charles Chand55e84d2016-03-30 17:54:24 -0700503 .pushVlan().setVlanId(assignedVlan).build();
gaurav164cf6d2016-03-25 21:43:04 +0530504 fob.withMeta(tt);
505 }
gaurav164cf6d2016-03-25 21:43:04 +0530506 fob.permit().fromApp(routerAppId);
507 sendFilteringObjective(install, fob, intf);
Charles Chand55e84d2016-03-30 17:54:24 -0700508
gaurav164cf6d2016-03-25 21:43:04 +0530509 if (controlPlaneConnectPoint != null) {
510 // then add the same mac/vlan filters for control-plane connect point
511 fob.withKey(Criteria.matchInPort(controlPlaneConnectPoint.port()));
Saurav Das49cb5a12016-01-16 22:54:07 -0800512 sendFilteringObjective(install, fob, intf);
Jonathan Hartdf207092015-12-10 11:19:25 -0800513 }
514 }
515
Charles Chand55e84d2016-03-30 17:54:24 -0700516 //create filtering objective for multicast traffic
517 private void createMcastFilteringObjective(boolean install, Interface intf) {
518 VlanId assignedVlan = (egressVlan().equals(VlanId.NONE)) ?
519 VlanId.vlanId(ASSIGNED_VLAN) :
520 egressVlan();
521
522 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
523 // first add filter for the interface
524 fob.withKey(Criteria.matchInPort(intf.connectPoint().port()))
525 .addCondition(Criteria.matchEthDstMasked(MacAddress.IPV4_MULTICAST,
526 MacAddress.IPV4_MULTICAST_MASK))
527 .addCondition(Criteria.matchVlanId(ingressVlan()));
528 fob.withPriority(PRIORITY_OFFSET);
529 TrafficTreatment tt = DefaultTrafficTreatment.builder()
530 .pushVlan().setVlanId(assignedVlan).build();
531 fob.withMeta(tt);
532
533 fob.permit().fromApp(routerAppId);
534 sendFilteringObjective(install, fob, intf);
535 }
536
Saurav Das49cb5a12016-01-16 22:54:07 -0800537 private void sendFilteringObjective(boolean install, FilteringObjective.Builder fob,
538 Interface intf) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800539
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700540 ObjectiveContext context = new DefaultObjectiveContext(
541 (objective) -> log.info("Installed filter for interface {}", intf),
542 (objective, error) ->
543 log.error("Failed to install filter for interface {}: {}", intf, error));
gaurav164cf6d2016-03-25 21:43:04 +0530544
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700545 FilteringObjective filter = install ? fob.add(context) : fob.remove(context);
546
547 flowObjectiveService.filter(deviceId, filter);
Saurav Das49cb5a12016-01-16 22:54:07 -0800548 }
549
Charles Chand55e84d2016-03-30 17:54:24 -0700550 private VlanId ingressVlan() {
551 McastConfig mcastConfig =
552 networkConfigService.getConfig(coreAppId, McastConfig.class);
553 return (mcastConfig != null) ? mcastConfig.ingressVlan() : VlanId.NONE;
554 }
555
556 private VlanId egressVlan() {
557 McastConfig mcastConfig =
558 networkConfigService.getConfig(coreAppId, McastConfig.class);
559 return (mcastConfig != null) ? mcastConfig.egressVlan() : VlanId.NONE;
560 }
561
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700562 private class InternalRouteListener implements RouteListener {
Jonathan Hartdf207092015-12-10 11:19:25 -0800563 @Override
Jonathan Harta2eb9ff2016-04-13 21:27:06 -0700564 public void event(RouteEvent event) {
565 ResolvedRoute route = event.subject();
566 switch (event.type()) {
567 case ROUTE_ADDED:
568 case ROUTE_UPDATED:
569 updateRoute(route);
570 break;
571 case ROUTE_REMOVED:
572 deleteRoute(route);
573 break;
574 default:
575 break;
576 }
Jonathan Hartdf207092015-12-10 11:19:25 -0800577 }
578 }
579
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800580 /**
581 * Listener for device events used to trigger driver setup when a device is
582 * (re)detected.
583 */
584 private class InternalDeviceListener implements DeviceListener {
Jonathan Hartdf207092015-12-10 11:19:25 -0800585 @Override
586 public void event(DeviceEvent event) {
587 switch (event.type()) {
588 case DEVICE_ADDED:
589 case DEVICE_AVAILABILITY_CHANGED:
590 if (deviceService.isAvailable(event.subject().id())) {
591 log.info("Device connected {}", event.subject().id());
592 if (event.subject().id().equals(deviceId)) {
Charles Chanf555a732016-02-15 15:37:15 -0800593 updateDevice();
Jonathan Hartdf207092015-12-10 11:19:25 -0800594 }
595 }
596 break;
597 // TODO other cases
598 case DEVICE_UPDATED:
599 case DEVICE_REMOVED:
600 case DEVICE_SUSPENDED:
601 case PORT_ADDED:
602 case PORT_UPDATED:
603 case PORT_REMOVED:
604 default:
605 break;
606 }
607 }
608 }
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800609
610 /**
611 * Listener for network config events.
612 */
613 private class InternalNetworkConfigListener implements NetworkConfigListener {
614 @Override
615 public void event(NetworkConfigEvent event) {
Jonathan Hart25bd6682016-06-03 10:45:47 -0700616 if (event.configClass().equals(RoutingService.ROUTER_CONFIG_CLASS)) {
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800617 switch (event.type()) {
618 case CONFIG_ADDED:
619 case CONFIG_UPDATED:
620 updateConfig();
gaurav25118892016-08-24 05:35:03 +0530621 if (event.prevConfig().isPresent()) {
622 removeFilteringObjectives(event);
623 }
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800624 break;
625 case CONFIG_REGISTERED:
gaurav25118892016-08-24 05:35:03 +0530626 break;
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800627 case CONFIG_UNREGISTERED:
gaurav25118892016-08-24 05:35:03 +0530628 break;
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800629 case CONFIG_REMOVED:
gaurav25118892016-08-24 05:35:03 +0530630 cleanUp();
631 break;
Jonathan Hartb3fa42c2016-01-13 09:50:43 -0800632 default:
633 break;
634 }
635 }
636 }
637 }
gaurav164cf6d2016-03-25 21:43:04 +0530638
639 private class InternalInterfaceListener implements InterfaceListener {
gaurav164cf6d2016-03-25 21:43:04 +0530640 @Override
641 public void event(InterfaceEvent event) {
642 Interface intf = event.subject();
643 switch (event.type()) {
644 case INTERFACE_ADDED:
645 if (intf != null) {
646 processIntfFilter(true, intf);
647 }
648 break;
649 case INTERFACE_UPDATED:
650 break;
651 case INTERFACE_REMOVED:
652 if (intf != null) {
653 processIntfFilter(false, intf);
654 }
655 break;
656 default:
657 break;
658 }
659 }
660 }
Jonathan Hartdf207092015-12-10 11:19:25 -0800661}