blob: 817e11d83396df1039b7757ad1d5723edf815c63 [file] [log] [blame]
Jonathan Hart6344f572015-12-15 08:26:25 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Jonathan Hart6344f572015-12-15 08:26: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
17package org.onosproject.routing.impl;
18
Jonathan Hartf04b7d92016-03-29 09:39:11 -070019import com.google.common.collect.ImmutableSortedSet;
20import com.google.common.collect.Maps;
Jonathan Hart6344f572015-12-15 08:26:25 -080021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.onlab.packet.EthType;
Charles Chand0fd5dc2016-02-16 23:14:49 -080027import org.onlab.packet.IpPrefix;
28import org.onlab.packet.MacAddress;
Saurav Das49cb5a12016-01-16 22:54:07 -080029import org.onlab.packet.VlanId;
kishore786b7e42016-05-19 16:25:57 +053030import org.onosproject.app.ApplicationService;
Jonathan Hart6344f572015-12-15 08:26:25 -080031import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
33import org.onosproject.incubator.net.intf.Interface;
kishore71a27532016-03-16 20:23:49 +053034import org.onosproject.incubator.net.intf.InterfaceEvent;
35import org.onosproject.incubator.net.intf.InterfaceListener;
Jonathan Hart6344f572015-12-15 08:26:25 -080036import org.onosproject.incubator.net.intf.InterfaceService;
Charles Chand0fd5dc2016-02-16 23:14:49 -080037import org.onosproject.mastership.MastershipService;
Jonathan Hart6344f572015-12-15 08:26:25 -080038import org.onosproject.net.ConnectPoint;
39import org.onosproject.net.DeviceId;
Charles Chand0fd5dc2016-02-16 23:14:49 -080040import org.onosproject.net.Host;
Jonathan Hart6344f572015-12-15 08:26:25 -080041import org.onosproject.net.PortNumber;
42import org.onosproject.net.config.NetworkConfigEvent;
43import org.onosproject.net.config.NetworkConfigListener;
44import org.onosproject.net.config.NetworkConfigService;
45import org.onosproject.net.device.DeviceEvent;
46import org.onosproject.net.device.DeviceListener;
47import org.onosproject.net.device.DeviceService;
48import org.onosproject.net.flow.DefaultTrafficSelector;
49import org.onosproject.net.flow.DefaultTrafficTreatment;
50import org.onosproject.net.flow.TrafficSelector;
51import org.onosproject.net.flow.TrafficTreatment;
52import org.onosproject.net.flowobjective.DefaultForwardingObjective;
Saurav Das49cb5a12016-01-16 22:54:07 -080053import org.onosproject.net.flowobjective.DefaultNextObjective;
Jonathan Hart6344f572015-12-15 08:26:25 -080054import org.onosproject.net.flowobjective.FlowObjectiveService;
55import org.onosproject.net.flowobjective.ForwardingObjective;
Saurav Das49cb5a12016-01-16 22:54:07 -080056import org.onosproject.net.flowobjective.NextObjective;
Charles Chand0fd5dc2016-02-16 23:14:49 -080057import org.onosproject.net.host.HostEvent;
58import org.onosproject.net.host.HostListener;
59import org.onosproject.net.host.HostService;
Jonathan Hart6344f572015-12-15 08:26:25 -080060import org.onosproject.net.host.InterfaceIpAddress;
61import org.onosproject.routing.RoutingService;
62import org.onosproject.routing.config.RouterConfig;
63import org.slf4j.Logger;
64
Jonathan Hartf04b7d92016-03-29 09:39:11 -070065import java.util.Collections;
66import java.util.Iterator;
67import java.util.List;
68import java.util.Map;
69import java.util.Optional;
70import java.util.Set;
kishore7c42cbe2016-04-26 22:49:36 +053071import java.util.stream.Collectors;
Jonathan Hartf04b7d92016-03-29 09:39:11 -070072
73import static com.google.common.base.Preconditions.checkState;
74import static org.slf4j.LoggerFactory.getLogger;
Jonathan Hart6344f572015-12-15 08:26:25 -080075
76/**
77 * Manages connectivity between peers redirecting control traffic to a routing
78 * control plane available on the dataplane.
79 */
80@Component(immediate = true, enabled = false)
81public class ControlPlaneRedirectManager {
82
83 private final Logger log = getLogger(getClass());
84
Charles Chand0fd5dc2016-02-16 23:14:49 -080085 private static final int MIN_IP_PRIORITY = 10;
kishore36d1c272016-09-21 15:44:10 +053086 private static final int IPV4_PRIORITY = 2000;
87 private static final int IPV6_PRIORITY = 500;
Charles Chand0fd5dc2016-02-16 23:14:49 -080088 private static final int ACL_PRIORITY = 40001;
Jonathan Hartea492382016-01-13 09:33:13 -080089 private static final int OSPF_IP_PROTO = 0x59;
Jonathan Hart6344f572015-12-15 08:26:25 -080090
kishore786b7e42016-05-19 16:25:57 +053091 private static final String APP_NAME = "org.onosproject.vrouter";
Jonathan Hart6344f572015-12-15 08:26:25 -080092 private ApplicationId appId;
93
94 private ConnectPoint controlPlaneConnectPoint;
Jonathan Hartea492382016-01-13 09:33:13 -080095 private boolean ospfEnabled = false;
Jonathan Hart883fd372016-02-10 14:36:15 -080096 private List<String> interfaces = Collections.emptyList();
Charles Chand0fd5dc2016-02-16 23:14:49 -080097 private Map<Host, Set<Integer>> peerNextId = Maps.newConcurrentMap();
Jonathan Hart6344f572015-12-15 08:26:25 -080098
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected CoreService coreService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected DeviceService deviceService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected InterfaceService interfaceService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected FlowObjectiveService flowObjectiveService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected NetworkConfigService networkConfigService;
113
Charles Chand0fd5dc2016-02-16 23:14:49 -0800114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected MastershipService mastershipService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected HostService hostService;
119
kishore786b7e42016-05-19 16:25:57 +0530120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected ApplicationService applicationService;
122
Jonathan Hart6344f572015-12-15 08:26:25 -0800123 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
124 private final InternalNetworkConfigListener networkConfigListener =
125 new InternalNetworkConfigListener();
Charles Chand0fd5dc2016-02-16 23:14:49 -0800126 private final InternalHostListener hostListener = new InternalHostListener();
kishore71a27532016-03-16 20:23:49 +0530127 private final InternalInterfaceListener interfaceListener = new InternalInterfaceListener();
Jonathan Hart6344f572015-12-15 08:26:25 -0800128
129 @Activate
130 public void activate() {
131 this.appId = coreService.registerApplication(APP_NAME);
132
133 deviceService.addListener(deviceListener);
134 networkConfigService.addListener(networkConfigListener);
Charles Chand0fd5dc2016-02-16 23:14:49 -0800135 hostService.addListener(hostListener);
kishore71a27532016-03-16 20:23:49 +0530136 interfaceService.addListener(interfaceListener);
Jonathan Hart6344f572015-12-15 08:26:25 -0800137
Jonathan Hartf8035d32016-06-16 16:23:26 -0700138 readConfig();
139 applicationService.registerDeactivateHook(this.appId, () -> provisionDevice(false));
Jonathan Hart6344f572015-12-15 08:26:25 -0800140 }
141
142 @Deactivate
143 public void deactivate() {
144 deviceService.removeListener(deviceListener);
145 networkConfigService.removeListener(networkConfigListener);
Charles Chand0fd5dc2016-02-16 23:14:49 -0800146 hostService.removeListener(hostListener);
kishore71a27532016-03-16 20:23:49 +0530147 interfaceService.removeListener(interfaceListener);
Jonathan Hart6344f572015-12-15 08:26:25 -0800148 }
149
kishore786b7e42016-05-19 16:25:57 +0530150 /**
151 * Installs or removes interface configuration
152 * based on the flag used on activate or deactivate.
153 *
kishore786b7e42016-05-19 16:25:57 +0530154 **/
Jonathan Hartf8035d32016-06-16 16:23:26 -0700155 private void readConfig() {
Jonathan Hart6344f572015-12-15 08:26:25 -0800156 ApplicationId routingAppId =
157 coreService.registerApplication(RoutingService.ROUTER_APP_ID);
158
159 RouterConfig config = networkConfigService.getConfig(
160 routingAppId, RoutingService.ROUTER_CONFIG_CLASS);
161
162 if (config == null) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800163 log.warn("Router config not available");
Jonathan Hart6344f572015-12-15 08:26:25 -0800164 return;
165 }
166
Jonathan Hartea492382016-01-13 09:33:13 -0800167 controlPlaneConnectPoint = config.getControlPlaneConnectPoint();
168 ospfEnabled = config.getOspfEnabled();
Jonathan Hart883fd372016-02-10 14:36:15 -0800169 interfaces = config.getInterfaces();
Jonathan Hart6344f572015-12-15 08:26:25 -0800170
Jonathan Hartf8035d32016-06-16 16:23:26 -0700171 provisionDevice(true);
Jonathan Hartea492382016-01-13 09:33:13 -0800172 }
173
kishore786b7e42016-05-19 16:25:57 +0530174 /**
175 * Installs or removes interface configuration for each interface
176 * based on the flag used on activate or deactivate.
177 *
Jonathan Hartf8035d32016-06-16 16:23:26 -0700178 * @param install true to install flows, false to remove them
kishore786b7e42016-05-19 16:25:57 +0530179 **/
Jonathan Hartf8035d32016-06-16 16:23:26 -0700180 private void provisionDevice(boolean install) {
Jonathan Hart6344f572015-12-15 08:26:25 -0800181 if (controlPlaneConnectPoint != null &&
182 deviceService.isAvailable(controlPlaneConnectPoint.deviceId())) {
Jonathan Hartea492382016-01-13 09:33:13 -0800183 DeviceId deviceId = controlPlaneConnectPoint.deviceId();
184
185 interfaceService.getInterfaces().stream()
186 .filter(intf -> intf.connectPoint().deviceId().equals(deviceId))
Jonathan Hart883fd372016-02-10 14:36:15 -0800187 .filter(intf -> interfaces.isEmpty() || interfaces.contains(intf.name()))
Jonathan Hartf8035d32016-06-16 16:23:26 -0700188 .forEach(intf -> provisionInterface(intf, install));
Jonathan Hartea492382016-01-13 09:33:13 -0800189
190 log.info("Set up interfaces on {}", controlPlaneConnectPoint.deviceId());
Jonathan Hart6344f572015-12-15 08:26:25 -0800191 }
192 }
193
Jonathan Hartf8035d32016-06-16 16:23:26 -0700194 private void provisionInterface(Interface intf, boolean install) {
195 updateInterfaceForwarding(intf, install);
196 updateOspfForwarding(intf, install);
Jonathan Hart6344f572015-12-15 08:26:25 -0800197 }
198
kishore71a27532016-03-16 20:23:49 +0530199 /**
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700200 * Installs or removes the basic forwarding flows for each interface
kishore71a27532016-03-16 20:23:49 +0530201 * based on the flag used.
202 *
203 * @param intf the Interface on which event is received
204 * @param install true to create an add objective, false to create a remove
205 * objective
206 **/
Jonathan Hartf8035d32016-06-16 16:23:26 -0700207 private void updateInterfaceForwarding(Interface intf, boolean install) {
Jonathan Hart6344f572015-12-15 08:26:25 -0800208 log.debug("Adding interface objectives for {}", intf);
209
210 DeviceId deviceId = controlPlaneConnectPoint.deviceId();
211 PortNumber controlPlanePort = controlPlaneConnectPoint.port();
Jonathan Hart6344f572015-12-15 08:26:25 -0800212 for (InterfaceIpAddress ip : intf.ipAddresses()) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800213 // create nextObjectives for forwarding to this interface and the
214 // controlPlaneConnectPoint
215 int cpNextId, intfNextId;
216 if (intf.vlan() == VlanId.NONE) {
kishore71a27532016-03-16 20:23:49 +0530217 cpNextId = modifyNextObjective(deviceId, controlPlanePort,
Saurav Das49cb5a12016-01-16 22:54:07 -0800218 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
kishore71a27532016-03-16 20:23:49 +0530219 true, install);
220 intfNextId = modifyNextObjective(deviceId, intf.connectPoint().port(),
Saurav Das49cb5a12016-01-16 22:54:07 -0800221 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
kishore71a27532016-03-16 20:23:49 +0530222 true, install);
Saurav Das49cb5a12016-01-16 22:54:07 -0800223 } else {
kishore71a27532016-03-16 20:23:49 +0530224 cpNextId = modifyNextObjective(deviceId, controlPlanePort,
225 intf.vlan(), false, install);
226 intfNextId = modifyNextObjective(deviceId, intf.connectPoint().port(),
227 intf.vlan(), false, install);
Saurav Das49cb5a12016-01-16 22:54:07 -0800228 }
229
Jonathan Hart6344f572015-12-15 08:26:25 -0800230 // IPv4 to router
231 TrafficSelector toSelector = DefaultTrafficSelector.builder()
232 .matchInPort(intf.connectPoint().port())
233 .matchEthDst(intf.mac())
234 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
235 .matchVlanId(intf.vlan())
236 .matchIPDst(ip.ipAddress().toIpPrefix())
237 .build();
238
Jonathan Hart6344f572015-12-15 08:26:25 -0800239 flowObjectiveService.forward(deviceId,
kishore71a27532016-03-16 20:23:49 +0530240 buildForwardingObjective(toSelector, null, cpNextId, install));
Jonathan Hart6344f572015-12-15 08:26:25 -0800241
242 // IPv4 from router
243 TrafficSelector fromSelector = DefaultTrafficSelector.builder()
244 .matchInPort(controlPlanePort)
245 .matchEthSrc(intf.mac())
246 .matchVlanId(intf.vlan())
247 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
248 .matchIPSrc(ip.ipAddress().toIpPrefix())
249 .build();
250
Jonathan Hart6344f572015-12-15 08:26:25 -0800251 flowObjectiveService.forward(deviceId,
kishore71a27532016-03-16 20:23:49 +0530252 buildForwardingObjective(fromSelector, null, intfNextId, install));
Jonathan Hart6344f572015-12-15 08:26:25 -0800253
254 // ARP to router
255 toSelector = DefaultTrafficSelector.builder()
256 .matchInPort(intf.connectPoint().port())
257 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
258 .matchVlanId(intf.vlan())
259 .build();
260
Saurav Das49cb5a12016-01-16 22:54:07 -0800261 TrafficTreatment puntTreatment = DefaultTrafficTreatment.builder()
Jonathan Hart6344f572015-12-15 08:26:25 -0800262 .punt()
263 .build();
264
265 flowObjectiveService.forward(deviceId,
kishore71a27532016-03-16 20:23:49 +0530266 buildForwardingObjective(toSelector, puntTreatment, cpNextId, install));
Jonathan Hart6344f572015-12-15 08:26:25 -0800267
268 // ARP from router
269 fromSelector = DefaultTrafficSelector.builder()
270 .matchInPort(controlPlanePort)
271 .matchEthSrc(intf.mac())
272 .matchVlanId(intf.vlan())
273 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
Saurav Das49cb5a12016-01-16 22:54:07 -0800274 .matchArpSpa(ip.ipAddress().getIp4Address())
Jonathan Hart6344f572015-12-15 08:26:25 -0800275 .build();
276
277 flowObjectiveService.forward(deviceId,
kishore71a27532016-03-16 20:23:49 +0530278 buildForwardingObjective(fromSelector, puntTreatment, intfNextId, install));
Jonathan Hart6344f572015-12-15 08:26:25 -0800279 }
280 }
281
kishore71a27532016-03-16 20:23:49 +0530282 /**
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700283 * Installs or removes OSPF forwarding rules.
kishore71a27532016-03-16 20:23:49 +0530284 *
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700285 * @param intf the interface on which event is received
kishore71a27532016-03-16 20:23:49 +0530286 * @param install true to create an add objective, false to create a remove
287 * objective
288 **/
289 private void updateOspfForwarding(Interface intf, boolean install) {
Jonathan Hartea492382016-01-13 09:33:13 -0800290 // OSPF to router
291 TrafficSelector toSelector = DefaultTrafficSelector.builder()
292 .matchInPort(intf.connectPoint().port())
293 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
294 .matchVlanId(intf.vlan())
295 .matchIPProtocol((byte) OSPF_IP_PROTO)
296 .build();
Jonathan Hart6344f572015-12-15 08:26:25 -0800297
Saurav Das49cb5a12016-01-16 22:54:07 -0800298 // create nextObjectives for forwarding to the controlPlaneConnectPoint
299 DeviceId deviceId = controlPlaneConnectPoint.deviceId();
300 PortNumber controlPlanePort = controlPlaneConnectPoint.port();
301 int cpNextId;
302 if (intf.vlan() == VlanId.NONE) {
kishore71a27532016-03-16 20:23:49 +0530303 cpNextId = modifyNextObjective(deviceId, controlPlanePort,
Saurav Das49cb5a12016-01-16 22:54:07 -0800304 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
kishore71a27532016-03-16 20:23:49 +0530305 true, install);
Saurav Das49cb5a12016-01-16 22:54:07 -0800306 } else {
kishore71a27532016-03-16 20:23:49 +0530307 cpNextId = modifyNextObjective(deviceId, controlPlanePort,
308 intf.vlan(), false, install);
Saurav Das49cb5a12016-01-16 22:54:07 -0800309 }
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700310 log.debug("OSPF flows intf:{} nextid:{}", intf, cpNextId);
Jonathan Hartea492382016-01-13 09:33:13 -0800311 flowObjectiveService.forward(controlPlaneConnectPoint.deviceId(),
kishore71a27532016-03-16 20:23:49 +0530312 buildForwardingObjective(toSelector, null, cpNextId, install ? ospfEnabled : install));
Jonathan Hartea492382016-01-13 09:33:13 -0800313 }
314
315 /**
Saurav Das49cb5a12016-01-16 22:54:07 -0800316 * Creates a next objective for forwarding to a port. Handles metadata for
317 * some pipelines that require vlan information for egress port.
318 *
319 * @param deviceId the device on which the next objective is being created
320 * @param portNumber the egress port
321 * @param vlanId vlan information for egress port
322 * @param popVlan if vlan tag should be popped or not
kishore71a27532016-03-16 20:23:49 +0530323 * @param install true to create an add next objective, false to create a remove
324 * next objective
Saurav Das49cb5a12016-01-16 22:54:07 -0800325 * @return nextId of the next objective created
326 */
kishore71a27532016-03-16 20:23:49 +0530327 private int modifyNextObjective(DeviceId deviceId, PortNumber portNumber,
328 VlanId vlanId, boolean popVlan, boolean install) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800329 int nextId = flowObjectiveService.allocateNextId();
330 NextObjective.Builder nextObjBuilder = DefaultNextObjective
331 .builder().withId(nextId)
332 .withType(NextObjective.Type.SIMPLE)
333 .fromApp(appId);
334
335 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
336 if (popVlan) {
337 ttBuilder.popVlan();
338 }
339 ttBuilder.setOutput(portNumber);
340
341 // setup metadata to pass to nextObjective - indicate the vlan on egress
342 // if needed by the switch pipeline.
343 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
344 metabuilder.matchVlanId(vlanId);
345
346 nextObjBuilder.withMeta(metabuilder.build());
347 nextObjBuilder.addTreatment(ttBuilder.build());
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700348 log.debug("Submitted next objective {} in device {} for port/vlan {}/{}",
Saurav Das49cb5a12016-01-16 22:54:07 -0800349 nextId, deviceId, portNumber, vlanId);
kishore71a27532016-03-16 20:23:49 +0530350 if (install) {
351 flowObjectiveService.next(deviceId, nextObjBuilder.add());
352 } else {
353 flowObjectiveService.next(deviceId, nextObjBuilder.remove());
354 }
Saurav Das49cb5a12016-01-16 22:54:07 -0800355 return nextId;
356 }
Jonathan Hartf8035d32016-06-16 16:23:26 -0700357
Saurav Das49cb5a12016-01-16 22:54:07 -0800358 /**
359 * Builds a forwarding objective from the given selector, treatment and nextId.
Jonathan Hartea492382016-01-13 09:33:13 -0800360 *
361 * @param selector selector
Saurav Das49cb5a12016-01-16 22:54:07 -0800362 * @param treatment treatment to apply to packet, can be null
363 * @param nextId next objective to point to for forwarding packet
Jonathan Hartea492382016-01-13 09:33:13 -0800364 * @param add true to create an add objective, false to create a remove
365 * objective
366 * @return forwarding objective
367 */
368 private ForwardingObjective buildForwardingObjective(TrafficSelector selector,
369 TrafficTreatment treatment,
Saurav Das49cb5a12016-01-16 22:54:07 -0800370 int nextId,
Jonathan Hartea492382016-01-13 09:33:13 -0800371 boolean add) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800372 DefaultForwardingObjective.Builder fobBuilder = DefaultForwardingObjective.builder();
373 fobBuilder.withSelector(selector);
374 if (treatment != null) {
375 fobBuilder.withTreatment(treatment);
376 }
377 if (nextId != -1) {
378 fobBuilder.nextStep(nextId);
379 }
380 fobBuilder.fromApp(appId)
Charles Chand0fd5dc2016-02-16 23:14:49 -0800381 .withPriority(ACL_PRIORITY)
Saurav Das49cb5a12016-01-16 22:54:07 -0800382 .withFlag(ForwardingObjective.Flag.VERSATILE);
Jonathan Hartea492382016-01-13 09:33:13 -0800383
384 return add ? fobBuilder.add() : fobBuilder.remove();
Jonathan Hart6344f572015-12-15 08:26:25 -0800385 }
386
Jonathan Hartea492382016-01-13 09:33:13 -0800387 /**
388 * Listener for device events.
389 */
Jonathan Hart6344f572015-12-15 08:26:25 -0800390 private class InternalDeviceListener implements DeviceListener {
kishore71a27532016-03-16 20:23:49 +0530391
Jonathan Hart6344f572015-12-15 08:26:25 -0800392 @Override
393 public void event(DeviceEvent event) {
394 if (controlPlaneConnectPoint != null &&
395 event.subject().id().equals(controlPlaneConnectPoint.deviceId())) {
396 switch (event.type()) {
397 case DEVICE_ADDED:
398 case DEVICE_AVAILABILITY_CHANGED:
399 if (deviceService.isAvailable(event.subject().id())) {
400 log.info("Device connected {}", event.subject().id());
Jonathan Hartf8035d32016-06-16 16:23:26 -0700401 provisionDevice(true);
Jonathan Hart6344f572015-12-15 08:26:25 -0800402 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800403 break;
404 case DEVICE_UPDATED:
405 case DEVICE_REMOVED:
406 case DEVICE_SUSPENDED:
407 case PORT_ADDED:
408 case PORT_UPDATED:
409 case PORT_REMOVED:
410 default:
411 break;
412 }
413 }
414 }
415 }
416
Jonathan Hartea492382016-01-13 09:33:13 -0800417 /**
418 * Listener for network config events.
419 */
Jonathan Hart6344f572015-12-15 08:26:25 -0800420 private class InternalNetworkConfigListener implements NetworkConfigListener {
kishore71a27532016-03-16 20:23:49 +0530421
Jonathan Hart6344f572015-12-15 08:26:25 -0800422 @Override
423 public void event(NetworkConfigEvent event) {
Jonathan Hartea492382016-01-13 09:33:13 -0800424 if (event.configClass().equals(RoutingService.ROUTER_CONFIG_CLASS)) {
Jonathan Hart6344f572015-12-15 08:26:25 -0800425 switch (event.type()) {
kishore36d1c272016-09-21 15:44:10 +0530426 case CONFIG_ADDED:
427 case CONFIG_UPDATED:
428 readConfig();
429 if (event.prevConfig().isPresent()) {
430 updateConfig(event);
431 }
432
433 break;
434 case CONFIG_REGISTERED:
435 case CONFIG_UNREGISTERED:
436 case CONFIG_REMOVED:
437 removeConfig();
438
439 break;
440 default:
441 break;
Jonathan Hart6344f572015-12-15 08:26:25 -0800442 }
443 }
444 }
445 }
Charles Chand0fd5dc2016-02-16 23:14:49 -0800446
447 /**
448 * Listener for host events.
449 */
450 private class InternalHostListener implements HostListener {
kishore71a27532016-03-16 20:23:49 +0530451
Charles Chand0fd5dc2016-02-16 23:14:49 -0800452 private void peerAdded(HostEvent event) {
453 Host peer = event.subject();
454 Optional<Interface> peerIntf =
455 interfaceService.getInterfacesByPort(peer.location()).stream()
456 .filter(intf -> interfaces.isEmpty() || interfaces.contains(intf.name()))
457 .filter(intf -> peer.vlan().equals(intf.vlan()))
458 .findFirst();
459 if (!peerIntf.isPresent()) {
460 log.debug("Adding peer {}/{} on {} but the interface is not configured",
461 peer.mac(), peer.vlan(), peer.location());
462 return;
463 }
464
465 // Generate L3 Unicast groups and store it in the map
466 int toRouterL3Unicast = createPeerGroup(peer.mac(), peerIntf.get().mac(),
467 peer.vlan(), peer.location().deviceId(), controlPlaneConnectPoint.port());
468 int toPeerL3Unicast = createPeerGroup(peerIntf.get().mac(), peer.mac(),
469 peer.vlan(), peer.location().deviceId(), peer.location().port());
470 peerNextId.put(peer, ImmutableSortedSet.of(toRouterL3Unicast, toPeerL3Unicast));
471
472 // From peer to router
473 peerIntf.get().ipAddresses().forEach(routerIp -> {
474 flowObjectiveService.forward(peer.location().deviceId(),
475 createPeerObjBuilder(toRouterL3Unicast, routerIp.ipAddress().toIpPrefix()).add());
476 });
477
478 // From router to peer
479 peer.ipAddresses().forEach(peerIp -> {
480 flowObjectiveService.forward(peer.location().deviceId(),
481 createPeerObjBuilder(toPeerL3Unicast, peerIp.toIpPrefix()).add());
482 });
483 }
484
485 private void peerRemoved(HostEvent event) {
486 Host peer = event.subject();
487 Optional<Interface> peerIntf =
488 interfaceService.getInterfacesByPort(peer.location()).stream()
489 .filter(intf -> interfaces.isEmpty() || interfaces.contains(intf.name()))
490 .filter(intf -> peer.vlan().equals(intf.vlan()))
491 .findFirst();
492 if (!peerIntf.isPresent()) {
493 log.debug("Removing peer {}/{} on {} but the interface is not configured",
494 peer.mac(), peer.vlan(), peer.location());
495 return;
496 }
497
Charles Chand0fd5dc2016-02-16 23:14:49 -0800498 checkState(peerNextId.get(peer) != null,
499 "Peer nextId should not be null");
500 checkState(peerNextId.get(peer).size() == 2,
501 "Wrong nextId associated with the peer");
502 Iterator<Integer> iter = peerNextId.get(peer).iterator();
503 int toRouterL3Unicast = iter.next();
504 int toPeerL3Unicast = iter.next();
505
506 // From peer to router
507 peerIntf.get().ipAddresses().forEach(routerIp -> {
508 flowObjectiveService.forward(peer.location().deviceId(),
509 createPeerObjBuilder(toRouterL3Unicast, routerIp.ipAddress().toIpPrefix()).remove());
510 });
511
512 // From router to peer
513 peer.ipAddresses().forEach(peerIp -> {
514 flowObjectiveService.forward(peer.location().deviceId(),
515 createPeerObjBuilder(toPeerL3Unicast, peerIp.toIpPrefix()).remove());
516 });
517 }
518
519 private ForwardingObjective.Builder createPeerObjBuilder(
520 int nextId, IpPrefix ipAddresses) {
521 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
522 sbuilder.matchEthType(EthType.EtherType.IPV4.ethType().toShort());
523 sbuilder.matchIPDst(ipAddresses);
524 DefaultForwardingObjective.Builder builder =
525 DefaultForwardingObjective.builder()
526 .withSelector(sbuilder.build())
527 .fromApp(appId)
528 .withPriority(getPriorityFromPrefix(ipAddresses))
529 .withFlag(ForwardingObjective.Flag.SPECIFIC);
530 if (nextId != -1) {
531 builder.nextStep(nextId);
532 }
533 return builder;
534 }
535
536 private int createPeerGroup(MacAddress srcMac, MacAddress dstMac,
537 VlanId vlanId, DeviceId deviceId, PortNumber port) {
538 int nextId = flowObjectiveService.allocateNextId();
539 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
540 .withId(nextId)
541 .withType(NextObjective.Type.SIMPLE)
542 .fromApp(appId);
543
544 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
545 ttBuilder.setEthSrc(srcMac);
546 ttBuilder.setEthDst(dstMac);
547 ttBuilder.setOutput(port);
548 nextObjBuilder.addTreatment(ttBuilder.build());
549
550 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
551 VlanId matchVlanId = (vlanId.equals(VlanId.NONE)) ?
552 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN) :
553 vlanId;
554 metabuilder.matchVlanId(matchVlanId);
555 nextObjBuilder.withMeta(metabuilder.build());
556
557 flowObjectiveService.next(deviceId, nextObjBuilder.add());
558 return nextId;
559 }
560
561 @Override
562 public void event(HostEvent event) {
563 DeviceId deviceId = event.subject().location().deviceId();
564 if (!mastershipService.isLocalMaster(deviceId)) {
565 return;
566 }
567 switch (event.type()) {
568 case HOST_ADDED:
569 peerAdded(event);
570 break;
571 case HOST_MOVED:
572 //TODO We assume BGP peer does not move for now
573 break;
574 case HOST_REMOVED:
575 peerRemoved(event);
576 break;
577 case HOST_UPDATED:
578 //TODO We assume BGP peer does not change IP for now
579 break;
580 default:
581 break;
582 }
583 }
584 }
585
586 private int getPriorityFromPrefix(IpPrefix prefix) {
587 return (prefix.isIp4()) ?
kishore36d1c272016-09-21 15:44:10 +0530588 IPV4_PRIORITY * prefix.prefixLength() + MIN_IP_PRIORITY :
589 IPV6_PRIORITY * prefix.prefixLength() + MIN_IP_PRIORITY;
590 }
591
592 private void updateConfig(NetworkConfigEvent event) {
593 RouterConfig prevRouterConfig = (RouterConfig) event.prevConfig().get();
594 List<String> prevInterfaces = prevRouterConfig.getInterfaces();
595 Set<Interface> previntfs = filterInterfaces(prevInterfaces);
596 if (previntfs.isEmpty() && !interfaces.isEmpty()) {
597 interfaceService.getInterfaces().stream()
598 .filter(intf -> !interfaces.contains(intf.name()))
599 .forEach(intf -> processIntfFilter(false, intf));
600 return;
601 }
602 //remove the filtering objective for the interfaces which are not
603 //part of updated interfaces list.
604 previntfs.stream()
605 .filter(intf -> !interfaces.contains(intf.name()))
606 .forEach(intf -> processIntfFilter(false, intf));
607 }
608
609 /**
610 * process filtering objective for interface add/remove.
611 *
612 * @param install true to install flows, false to uninstall the flows
613 * @param intf Interface object captured on event
614 */
615 private void processIntfFilter(boolean install, Interface intf) {
616
617 if (!intf.connectPoint().deviceId().equals(controlPlaneConnectPoint.deviceId())) {
618 // Ignore interfaces if they are not on the router switch
619 return;
620 }
621 if (!interfaces.contains(intf.name()) && install) {
622 return;
623 }
624
625 provisionInterface(intf, install);
626 }
627
628 private Set<Interface> filterInterfaces(List<String> interfaces) {
629 Set<Interface> intfs = interfaceService.getInterfaces().stream()
630 .filter(intf -> intf.connectPoint().deviceId().equals(controlPlaneConnectPoint.deviceId()))
631 .filter(intf -> interfaces.contains(intf.name()))
632 .collect(Collectors.toSet());
633 return intfs;
634 }
635
636 private void removeConfig() {
637 Set<Interface> intfs = getInterfaces();
638 if (!intfs.isEmpty()) {
639 intfs.forEach(intf -> processIntfFilter(false, intf));
640 }
641 networkConfigService.removeConfig();
642 }
643
644 private Set<Interface> getInterfaces() {
645
646 return interfaces.isEmpty() ? interfaceService.getInterfaces()
647 : filterInterfaces(interfaces);
Charles Chand0fd5dc2016-02-16 23:14:49 -0800648 }
kishore7c42cbe2016-04-26 22:49:36 +0530649
650 /**
651 * Update the flows comparing previous event and current event.
652 *
653 * @param prevIntf the previous interface event
Jonathan Hartf8035d32016-06-16 16:23:26 -0700654 * @param intf the current occurred update event
kishore7c42cbe2016-04-26 22:49:36 +0530655 **/
656 private void updateInterface(Interface prevIntf, Interface intf) {
kishore36d1c272016-09-21 15:44:10 +0530657 if (!intf.connectPoint().deviceId().equals(controlPlaneConnectPoint.deviceId())
658 || !interfaces.contains(intf.name())) {
659 // Ignore interfaces if they are not on the router switch
660 return;
661 }
kishore7c42cbe2016-04-26 22:49:36 +0530662 if (!prevIntf.vlan().equals(intf.vlan()) || !prevIntf.mac().equals(intf)) {
Jonathan Hartf8035d32016-06-16 16:23:26 -0700663 provisionInterface(prevIntf, false);
664 provisionInterface(intf, true);
kishore7c42cbe2016-04-26 22:49:36 +0530665 } else {
666 List<InterfaceIpAddress> removeIps =
667 prevIntf.ipAddressesList().stream()
668 .filter(pre -> !intf.ipAddressesList().contains(pre))
669 .collect(Collectors.toList());
670 List<InterfaceIpAddress> addIps =
671 intf.ipAddressesList().stream()
672 .filter(cur -> !prevIntf.ipAddressesList().contains(cur))
673 .collect(Collectors.toList());
674 // removing flows with match parameters present in previous subject
Jonathan Hartf8035d32016-06-16 16:23:26 -0700675 updateInterfaceForwarding(new Interface(prevIntf.name(), prevIntf.connectPoint(),
kishore7c42cbe2016-04-26 22:49:36 +0530676 removeIps, prevIntf.mac(), prevIntf.vlan()), false);
677 // adding flows with match parameters present in event subject
Jonathan Hartf8035d32016-06-16 16:23:26 -0700678 updateInterfaceForwarding(new Interface(intf.name(), intf.connectPoint(),
kishore7c42cbe2016-04-26 22:49:36 +0530679 addIps, intf.mac(), intf.vlan()), true);
680 }
681 }
682
kishore71a27532016-03-16 20:23:49 +0530683 private class InternalInterfaceListener implements InterfaceListener {
kishore71a27532016-03-16 20:23:49 +0530684 @Override
685 public void event(InterfaceEvent event) {
Charles Chan4a6e91e2016-07-19 18:36:33 -0700686 if (controlPlaneConnectPoint == null) {
kishore36d1c272016-09-21 15:44:10 +0530687 log.warn("Control plane connect point is not configured. Abort InterfaceEvent.");
Charles Chan4a6e91e2016-07-19 18:36:33 -0700688 return;
689 }
690 Interface intf = event.subject();
691 Interface prevIntf = event.prevSubject();
692 switch (event.type()) {
kishore71a27532016-03-16 20:23:49 +0530693 case INTERFACE_ADDED:
kishore36d1c272016-09-21 15:44:10 +0530694 processIntfFilter(true, intf);
kishore71a27532016-03-16 20:23:49 +0530695 break;
696 case INTERFACE_UPDATED:
kishore36d1c272016-09-21 15:44:10 +0530697 updateInterface(prevIntf, intf);
kishore71a27532016-03-16 20:23:49 +0530698 break;
699 case INTERFACE_REMOVED:
kishore36d1c272016-09-21 15:44:10 +0530700 processIntfFilter(false, intf);
kishore71a27532016-03-16 20:23:49 +0530701 break;
702 default:
703 break;
Charles Chan4a6e91e2016-07-19 18:36:33 -0700704 }
kishore71a27532016-03-16 20:23:49 +0530705 }
706 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800707}