blob: 5cd78a988ada32437e85f99f8235e38314dd11c6 [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();
Charles Chan00d8b5f2016-12-04 17:17:39 -0800139
140 // FIXME There can be an issue when this component is deactivated before vRouter
Jonathan Hartf8035d32016-06-16 16:23:26 -0700141 applicationService.registerDeactivateHook(this.appId, () -> provisionDevice(false));
Jonathan Hart6344f572015-12-15 08:26:25 -0800142 }
143
144 @Deactivate
145 public void deactivate() {
146 deviceService.removeListener(deviceListener);
147 networkConfigService.removeListener(networkConfigListener);
Charles Chand0fd5dc2016-02-16 23:14:49 -0800148 hostService.removeListener(hostListener);
kishore71a27532016-03-16 20:23:49 +0530149 interfaceService.removeListener(interfaceListener);
Jonathan Hart6344f572015-12-15 08:26:25 -0800150 }
151
kishore786b7e42016-05-19 16:25:57 +0530152 /**
153 * Installs or removes interface configuration
154 * based on the flag used on activate or deactivate.
155 *
kishore786b7e42016-05-19 16:25:57 +0530156 **/
Jonathan Hartf8035d32016-06-16 16:23:26 -0700157 private void readConfig() {
Jonathan Hart6344f572015-12-15 08:26:25 -0800158 ApplicationId routingAppId =
159 coreService.registerApplication(RoutingService.ROUTER_APP_ID);
160
161 RouterConfig config = networkConfigService.getConfig(
162 routingAppId, RoutingService.ROUTER_CONFIG_CLASS);
163
164 if (config == null) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800165 log.warn("Router config not available");
Jonathan Hart6344f572015-12-15 08:26:25 -0800166 return;
167 }
168
Jonathan Hartea492382016-01-13 09:33:13 -0800169 controlPlaneConnectPoint = config.getControlPlaneConnectPoint();
170 ospfEnabled = config.getOspfEnabled();
Jonathan Hart883fd372016-02-10 14:36:15 -0800171 interfaces = config.getInterfaces();
Jonathan Hart6344f572015-12-15 08:26:25 -0800172
Jonathan Hartf8035d32016-06-16 16:23:26 -0700173 provisionDevice(true);
Jonathan Hartea492382016-01-13 09:33:13 -0800174 }
175
kishore786b7e42016-05-19 16:25:57 +0530176 /**
177 * Installs or removes interface configuration for each interface
178 * based on the flag used on activate or deactivate.
179 *
Jonathan Hartf8035d32016-06-16 16:23:26 -0700180 * @param install true to install flows, false to remove them
kishore786b7e42016-05-19 16:25:57 +0530181 **/
Jonathan Hartf8035d32016-06-16 16:23:26 -0700182 private void provisionDevice(boolean install) {
Jonathan Hart6344f572015-12-15 08:26:25 -0800183 if (controlPlaneConnectPoint != null &&
184 deviceService.isAvailable(controlPlaneConnectPoint.deviceId())) {
Jonathan Hartea492382016-01-13 09:33:13 -0800185 DeviceId deviceId = controlPlaneConnectPoint.deviceId();
186
187 interfaceService.getInterfaces().stream()
188 .filter(intf -> intf.connectPoint().deviceId().equals(deviceId))
Jonathan Hart883fd372016-02-10 14:36:15 -0800189 .filter(intf -> interfaces.isEmpty() || interfaces.contains(intf.name()))
Jonathan Hartf8035d32016-06-16 16:23:26 -0700190 .forEach(intf -> provisionInterface(intf, install));
Jonathan Hartea492382016-01-13 09:33:13 -0800191
192 log.info("Set up interfaces on {}", controlPlaneConnectPoint.deviceId());
Jonathan Hart6344f572015-12-15 08:26:25 -0800193 }
194 }
195
Jonathan Hartf8035d32016-06-16 16:23:26 -0700196 private void provisionInterface(Interface intf, boolean install) {
197 updateInterfaceForwarding(intf, install);
198 updateOspfForwarding(intf, install);
Jonathan Hart6344f572015-12-15 08:26:25 -0800199 }
200
kishore71a27532016-03-16 20:23:49 +0530201 /**
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700202 * Installs or removes the basic forwarding flows for each interface
kishore71a27532016-03-16 20:23:49 +0530203 * based on the flag used.
204 *
205 * @param intf the Interface on which event is received
206 * @param install true to create an add objective, false to create a remove
207 * objective
208 **/
Jonathan Hartf8035d32016-06-16 16:23:26 -0700209 private void updateInterfaceForwarding(Interface intf, boolean install) {
Jonathan Hart6344f572015-12-15 08:26:25 -0800210 log.debug("Adding interface objectives for {}", intf);
211
212 DeviceId deviceId = controlPlaneConnectPoint.deviceId();
213 PortNumber controlPlanePort = controlPlaneConnectPoint.port();
Jonathan Hart6344f572015-12-15 08:26:25 -0800214 for (InterfaceIpAddress ip : intf.ipAddresses()) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800215 // create nextObjectives for forwarding to this interface and the
216 // controlPlaneConnectPoint
217 int cpNextId, intfNextId;
218 if (intf.vlan() == VlanId.NONE) {
kishore71a27532016-03-16 20:23:49 +0530219 cpNextId = modifyNextObjective(deviceId, controlPlanePort,
Saurav Das49cb5a12016-01-16 22:54:07 -0800220 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
kishore71a27532016-03-16 20:23:49 +0530221 true, install);
222 intfNextId = modifyNextObjective(deviceId, intf.connectPoint().port(),
Saurav Das49cb5a12016-01-16 22:54:07 -0800223 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
kishore71a27532016-03-16 20:23:49 +0530224 true, install);
Saurav Das49cb5a12016-01-16 22:54:07 -0800225 } else {
kishore71a27532016-03-16 20:23:49 +0530226 cpNextId = modifyNextObjective(deviceId, controlPlanePort,
227 intf.vlan(), false, install);
228 intfNextId = modifyNextObjective(deviceId, intf.connectPoint().port(),
229 intf.vlan(), false, install);
Saurav Das49cb5a12016-01-16 22:54:07 -0800230 }
231
Jonathan Hart6344f572015-12-15 08:26:25 -0800232 // IPv4 to router
233 TrafficSelector toSelector = DefaultTrafficSelector.builder()
234 .matchInPort(intf.connectPoint().port())
235 .matchEthDst(intf.mac())
236 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
237 .matchVlanId(intf.vlan())
238 .matchIPDst(ip.ipAddress().toIpPrefix())
239 .build();
240
Jonathan Hart6344f572015-12-15 08:26:25 -0800241 flowObjectiveService.forward(deviceId,
kishore71a27532016-03-16 20:23:49 +0530242 buildForwardingObjective(toSelector, null, cpNextId, install));
Jonathan Hart6344f572015-12-15 08:26:25 -0800243
244 // IPv4 from router
245 TrafficSelector fromSelector = DefaultTrafficSelector.builder()
246 .matchInPort(controlPlanePort)
247 .matchEthSrc(intf.mac())
248 .matchVlanId(intf.vlan())
249 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
250 .matchIPSrc(ip.ipAddress().toIpPrefix())
251 .build();
252
Jonathan Hart6344f572015-12-15 08:26:25 -0800253 flowObjectiveService.forward(deviceId,
kishore71a27532016-03-16 20:23:49 +0530254 buildForwardingObjective(fromSelector, null, intfNextId, install));
Jonathan Hart6344f572015-12-15 08:26:25 -0800255
256 // ARP to router
257 toSelector = DefaultTrafficSelector.builder()
258 .matchInPort(intf.connectPoint().port())
259 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
260 .matchVlanId(intf.vlan())
261 .build();
262
Saurav Das49cb5a12016-01-16 22:54:07 -0800263 TrafficTreatment puntTreatment = DefaultTrafficTreatment.builder()
Jonathan Hart6344f572015-12-15 08:26:25 -0800264 .punt()
265 .build();
266
267 flowObjectiveService.forward(deviceId,
kishore71a27532016-03-16 20:23:49 +0530268 buildForwardingObjective(toSelector, puntTreatment, cpNextId, install));
Jonathan Hart6344f572015-12-15 08:26:25 -0800269
270 // ARP from router
271 fromSelector = DefaultTrafficSelector.builder()
272 .matchInPort(controlPlanePort)
273 .matchEthSrc(intf.mac())
274 .matchVlanId(intf.vlan())
275 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
Saurav Das49cb5a12016-01-16 22:54:07 -0800276 .matchArpSpa(ip.ipAddress().getIp4Address())
Jonathan Hart6344f572015-12-15 08:26:25 -0800277 .build();
278
279 flowObjectiveService.forward(deviceId,
kishore71a27532016-03-16 20:23:49 +0530280 buildForwardingObjective(fromSelector, puntTreatment, intfNextId, install));
Jonathan Hart6344f572015-12-15 08:26:25 -0800281 }
282 }
283
kishore71a27532016-03-16 20:23:49 +0530284 /**
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700285 * Installs or removes OSPF forwarding rules.
kishore71a27532016-03-16 20:23:49 +0530286 *
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700287 * @param intf the interface on which event is received
kishore71a27532016-03-16 20:23:49 +0530288 * @param install true to create an add objective, false to create a remove
289 * objective
290 **/
291 private void updateOspfForwarding(Interface intf, boolean install) {
Jonathan Hartea492382016-01-13 09:33:13 -0800292 // OSPF to router
293 TrafficSelector toSelector = DefaultTrafficSelector.builder()
294 .matchInPort(intf.connectPoint().port())
295 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
296 .matchVlanId(intf.vlan())
297 .matchIPProtocol((byte) OSPF_IP_PROTO)
298 .build();
Jonathan Hart6344f572015-12-15 08:26:25 -0800299
Saurav Das49cb5a12016-01-16 22:54:07 -0800300 // create nextObjectives for forwarding to the controlPlaneConnectPoint
301 DeviceId deviceId = controlPlaneConnectPoint.deviceId();
302 PortNumber controlPlanePort = controlPlaneConnectPoint.port();
303 int cpNextId;
304 if (intf.vlan() == VlanId.NONE) {
kishore71a27532016-03-16 20:23:49 +0530305 cpNextId = modifyNextObjective(deviceId, controlPlanePort,
Saurav Das49cb5a12016-01-16 22:54:07 -0800306 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
kishore71a27532016-03-16 20:23:49 +0530307 true, install);
Saurav Das49cb5a12016-01-16 22:54:07 -0800308 } else {
kishore71a27532016-03-16 20:23:49 +0530309 cpNextId = modifyNextObjective(deviceId, controlPlanePort,
310 intf.vlan(), false, install);
Saurav Das49cb5a12016-01-16 22:54:07 -0800311 }
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700312 log.debug("OSPF flows intf:{} nextid:{}", intf, cpNextId);
Jonathan Hartea492382016-01-13 09:33:13 -0800313 flowObjectiveService.forward(controlPlaneConnectPoint.deviceId(),
kishore71a27532016-03-16 20:23:49 +0530314 buildForwardingObjective(toSelector, null, cpNextId, install ? ospfEnabled : install));
Jonathan Hartea492382016-01-13 09:33:13 -0800315 }
316
317 /**
Saurav Das49cb5a12016-01-16 22:54:07 -0800318 * Creates a next objective for forwarding to a port. Handles metadata for
319 * some pipelines that require vlan information for egress port.
320 *
321 * @param deviceId the device on which the next objective is being created
322 * @param portNumber the egress port
323 * @param vlanId vlan information for egress port
324 * @param popVlan if vlan tag should be popped or not
kishore71a27532016-03-16 20:23:49 +0530325 * @param install true to create an add next objective, false to create a remove
326 * next objective
Saurav Das49cb5a12016-01-16 22:54:07 -0800327 * @return nextId of the next objective created
328 */
kishore71a27532016-03-16 20:23:49 +0530329 private int modifyNextObjective(DeviceId deviceId, PortNumber portNumber,
330 VlanId vlanId, boolean popVlan, boolean install) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800331 int nextId = flowObjectiveService.allocateNextId();
332 NextObjective.Builder nextObjBuilder = DefaultNextObjective
333 .builder().withId(nextId)
334 .withType(NextObjective.Type.SIMPLE)
335 .fromApp(appId);
336
337 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
338 if (popVlan) {
339 ttBuilder.popVlan();
340 }
341 ttBuilder.setOutput(portNumber);
342
343 // setup metadata to pass to nextObjective - indicate the vlan on egress
344 // if needed by the switch pipeline.
345 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
346 metabuilder.matchVlanId(vlanId);
347
348 nextObjBuilder.withMeta(metabuilder.build());
349 nextObjBuilder.addTreatment(ttBuilder.build());
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700350 log.debug("Submitted next objective {} in device {} for port/vlan {}/{}",
Saurav Das49cb5a12016-01-16 22:54:07 -0800351 nextId, deviceId, portNumber, vlanId);
kishore71a27532016-03-16 20:23:49 +0530352 if (install) {
353 flowObjectiveService.next(deviceId, nextObjBuilder.add());
354 } else {
355 flowObjectiveService.next(deviceId, nextObjBuilder.remove());
356 }
Saurav Das49cb5a12016-01-16 22:54:07 -0800357 return nextId;
358 }
Jonathan Hartf8035d32016-06-16 16:23:26 -0700359
Saurav Das49cb5a12016-01-16 22:54:07 -0800360 /**
361 * Builds a forwarding objective from the given selector, treatment and nextId.
Jonathan Hartea492382016-01-13 09:33:13 -0800362 *
363 * @param selector selector
Saurav Das49cb5a12016-01-16 22:54:07 -0800364 * @param treatment treatment to apply to packet, can be null
365 * @param nextId next objective to point to for forwarding packet
Jonathan Hartea492382016-01-13 09:33:13 -0800366 * @param add true to create an add objective, false to create a remove
367 * objective
368 * @return forwarding objective
369 */
370 private ForwardingObjective buildForwardingObjective(TrafficSelector selector,
371 TrafficTreatment treatment,
Saurav Das49cb5a12016-01-16 22:54:07 -0800372 int nextId,
Jonathan Hartea492382016-01-13 09:33:13 -0800373 boolean add) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800374 DefaultForwardingObjective.Builder fobBuilder = DefaultForwardingObjective.builder();
375 fobBuilder.withSelector(selector);
376 if (treatment != null) {
377 fobBuilder.withTreatment(treatment);
378 }
379 if (nextId != -1) {
380 fobBuilder.nextStep(nextId);
381 }
382 fobBuilder.fromApp(appId)
Charles Chand0fd5dc2016-02-16 23:14:49 -0800383 .withPriority(ACL_PRIORITY)
Saurav Das49cb5a12016-01-16 22:54:07 -0800384 .withFlag(ForwardingObjective.Flag.VERSATILE);
Jonathan Hartea492382016-01-13 09:33:13 -0800385
386 return add ? fobBuilder.add() : fobBuilder.remove();
Jonathan Hart6344f572015-12-15 08:26:25 -0800387 }
388
Jonathan Hartea492382016-01-13 09:33:13 -0800389 /**
390 * Listener for device events.
391 */
Jonathan Hart6344f572015-12-15 08:26:25 -0800392 private class InternalDeviceListener implements DeviceListener {
kishore71a27532016-03-16 20:23:49 +0530393
Jonathan Hart6344f572015-12-15 08:26:25 -0800394 @Override
395 public void event(DeviceEvent event) {
396 if (controlPlaneConnectPoint != null &&
397 event.subject().id().equals(controlPlaneConnectPoint.deviceId())) {
398 switch (event.type()) {
399 case DEVICE_ADDED:
400 case DEVICE_AVAILABILITY_CHANGED:
401 if (deviceService.isAvailable(event.subject().id())) {
402 log.info("Device connected {}", event.subject().id());
Jonathan Hartf8035d32016-06-16 16:23:26 -0700403 provisionDevice(true);
Jonathan Hart6344f572015-12-15 08:26:25 -0800404 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800405 break;
406 case DEVICE_UPDATED:
407 case DEVICE_REMOVED:
408 case DEVICE_SUSPENDED:
409 case PORT_ADDED:
410 case PORT_UPDATED:
411 case PORT_REMOVED:
412 default:
413 break;
414 }
415 }
416 }
417 }
418
Jonathan Hartea492382016-01-13 09:33:13 -0800419 /**
420 * Listener for network config events.
421 */
Jonathan Hart6344f572015-12-15 08:26:25 -0800422 private class InternalNetworkConfigListener implements NetworkConfigListener {
kishore71a27532016-03-16 20:23:49 +0530423
Jonathan Hart6344f572015-12-15 08:26:25 -0800424 @Override
425 public void event(NetworkConfigEvent event) {
Jonathan Hartea492382016-01-13 09:33:13 -0800426 if (event.configClass().equals(RoutingService.ROUTER_CONFIG_CLASS)) {
Jonathan Hart6344f572015-12-15 08:26:25 -0800427 switch (event.type()) {
kishore36d1c272016-09-21 15:44:10 +0530428 case CONFIG_ADDED:
429 case CONFIG_UPDATED:
430 readConfig();
431 if (event.prevConfig().isPresent()) {
432 updateConfig(event);
433 }
434
435 break;
436 case CONFIG_REGISTERED:
437 case CONFIG_UNREGISTERED:
438 case CONFIG_REMOVED:
439 removeConfig();
440
441 break;
442 default:
443 break;
Jonathan Hart6344f572015-12-15 08:26:25 -0800444 }
445 }
446 }
447 }
Charles Chand0fd5dc2016-02-16 23:14:49 -0800448
449 /**
450 * Listener for host events.
451 */
452 private class InternalHostListener implements HostListener {
kishore71a27532016-03-16 20:23:49 +0530453
Charles Chand0fd5dc2016-02-16 23:14:49 -0800454 private void peerAdded(HostEvent event) {
455 Host peer = event.subject();
456 Optional<Interface> peerIntf =
457 interfaceService.getInterfacesByPort(peer.location()).stream()
458 .filter(intf -> interfaces.isEmpty() || interfaces.contains(intf.name()))
459 .filter(intf -> peer.vlan().equals(intf.vlan()))
460 .findFirst();
461 if (!peerIntf.isPresent()) {
462 log.debug("Adding peer {}/{} on {} but the interface is not configured",
463 peer.mac(), peer.vlan(), peer.location());
464 return;
465 }
466
467 // Generate L3 Unicast groups and store it in the map
468 int toRouterL3Unicast = createPeerGroup(peer.mac(), peerIntf.get().mac(),
469 peer.vlan(), peer.location().deviceId(), controlPlaneConnectPoint.port());
470 int toPeerL3Unicast = createPeerGroup(peerIntf.get().mac(), peer.mac(),
471 peer.vlan(), peer.location().deviceId(), peer.location().port());
472 peerNextId.put(peer, ImmutableSortedSet.of(toRouterL3Unicast, toPeerL3Unicast));
473
474 // From peer to router
475 peerIntf.get().ipAddresses().forEach(routerIp -> {
476 flowObjectiveService.forward(peer.location().deviceId(),
477 createPeerObjBuilder(toRouterL3Unicast, routerIp.ipAddress().toIpPrefix()).add());
478 });
479
480 // From router to peer
481 peer.ipAddresses().forEach(peerIp -> {
482 flowObjectiveService.forward(peer.location().deviceId(),
483 createPeerObjBuilder(toPeerL3Unicast, peerIp.toIpPrefix()).add());
484 });
485 }
486
487 private void peerRemoved(HostEvent event) {
488 Host peer = event.subject();
489 Optional<Interface> peerIntf =
490 interfaceService.getInterfacesByPort(peer.location()).stream()
491 .filter(intf -> interfaces.isEmpty() || interfaces.contains(intf.name()))
492 .filter(intf -> peer.vlan().equals(intf.vlan()))
493 .findFirst();
494 if (!peerIntf.isPresent()) {
495 log.debug("Removing peer {}/{} on {} but the interface is not configured",
496 peer.mac(), peer.vlan(), peer.location());
497 return;
498 }
499
Charles Chand0fd5dc2016-02-16 23:14:49 -0800500 checkState(peerNextId.get(peer) != null,
501 "Peer nextId should not be null");
502 checkState(peerNextId.get(peer).size() == 2,
503 "Wrong nextId associated with the peer");
504 Iterator<Integer> iter = peerNextId.get(peer).iterator();
505 int toRouterL3Unicast = iter.next();
506 int toPeerL3Unicast = iter.next();
507
508 // From peer to router
509 peerIntf.get().ipAddresses().forEach(routerIp -> {
510 flowObjectiveService.forward(peer.location().deviceId(),
511 createPeerObjBuilder(toRouterL3Unicast, routerIp.ipAddress().toIpPrefix()).remove());
512 });
513
514 // From router to peer
515 peer.ipAddresses().forEach(peerIp -> {
516 flowObjectiveService.forward(peer.location().deviceId(),
517 createPeerObjBuilder(toPeerL3Unicast, peerIp.toIpPrefix()).remove());
518 });
519 }
520
521 private ForwardingObjective.Builder createPeerObjBuilder(
522 int nextId, IpPrefix ipAddresses) {
523 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
524 sbuilder.matchEthType(EthType.EtherType.IPV4.ethType().toShort());
525 sbuilder.matchIPDst(ipAddresses);
526 DefaultForwardingObjective.Builder builder =
527 DefaultForwardingObjective.builder()
528 .withSelector(sbuilder.build())
529 .fromApp(appId)
530 .withPriority(getPriorityFromPrefix(ipAddresses))
531 .withFlag(ForwardingObjective.Flag.SPECIFIC);
532 if (nextId != -1) {
533 builder.nextStep(nextId);
534 }
535 return builder;
536 }
537
538 private int createPeerGroup(MacAddress srcMac, MacAddress dstMac,
539 VlanId vlanId, DeviceId deviceId, PortNumber port) {
540 int nextId = flowObjectiveService.allocateNextId();
541 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
542 .withId(nextId)
543 .withType(NextObjective.Type.SIMPLE)
544 .fromApp(appId);
545
546 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
547 ttBuilder.setEthSrc(srcMac);
548 ttBuilder.setEthDst(dstMac);
549 ttBuilder.setOutput(port);
550 nextObjBuilder.addTreatment(ttBuilder.build());
551
552 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
553 VlanId matchVlanId = (vlanId.equals(VlanId.NONE)) ?
554 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN) :
555 vlanId;
556 metabuilder.matchVlanId(matchVlanId);
557 nextObjBuilder.withMeta(metabuilder.build());
558
559 flowObjectiveService.next(deviceId, nextObjBuilder.add());
560 return nextId;
561 }
562
563 @Override
564 public void event(HostEvent event) {
565 DeviceId deviceId = event.subject().location().deviceId();
566 if (!mastershipService.isLocalMaster(deviceId)) {
567 return;
568 }
569 switch (event.type()) {
570 case HOST_ADDED:
571 peerAdded(event);
572 break;
573 case HOST_MOVED:
574 //TODO We assume BGP peer does not move for now
575 break;
576 case HOST_REMOVED:
577 peerRemoved(event);
578 break;
579 case HOST_UPDATED:
580 //TODO We assume BGP peer does not change IP for now
581 break;
582 default:
583 break;
584 }
585 }
586 }
587
588 private int getPriorityFromPrefix(IpPrefix prefix) {
589 return (prefix.isIp4()) ?
kishore36d1c272016-09-21 15:44:10 +0530590 IPV4_PRIORITY * prefix.prefixLength() + MIN_IP_PRIORITY :
591 IPV6_PRIORITY * prefix.prefixLength() + MIN_IP_PRIORITY;
592 }
593
594 private void updateConfig(NetworkConfigEvent event) {
595 RouterConfig prevRouterConfig = (RouterConfig) event.prevConfig().get();
596 List<String> prevInterfaces = prevRouterConfig.getInterfaces();
597 Set<Interface> previntfs = filterInterfaces(prevInterfaces);
598 if (previntfs.isEmpty() && !interfaces.isEmpty()) {
599 interfaceService.getInterfaces().stream()
600 .filter(intf -> !interfaces.contains(intf.name()))
601 .forEach(intf -> processIntfFilter(false, intf));
602 return;
603 }
604 //remove the filtering objective for the interfaces which are not
605 //part of updated interfaces list.
606 previntfs.stream()
607 .filter(intf -> !interfaces.contains(intf.name()))
608 .forEach(intf -> processIntfFilter(false, intf));
609 }
610
611 /**
612 * process filtering objective for interface add/remove.
613 *
614 * @param install true to install flows, false to uninstall the flows
615 * @param intf Interface object captured on event
616 */
617 private void processIntfFilter(boolean install, Interface intf) {
618
619 if (!intf.connectPoint().deviceId().equals(controlPlaneConnectPoint.deviceId())) {
620 // Ignore interfaces if they are not on the router switch
621 return;
622 }
623 if (!interfaces.contains(intf.name()) && install) {
624 return;
625 }
626
627 provisionInterface(intf, install);
628 }
629
630 private Set<Interface> filterInterfaces(List<String> interfaces) {
631 Set<Interface> intfs = interfaceService.getInterfaces().stream()
632 .filter(intf -> intf.connectPoint().deviceId().equals(controlPlaneConnectPoint.deviceId()))
633 .filter(intf -> interfaces.contains(intf.name()))
634 .collect(Collectors.toSet());
635 return intfs;
636 }
637
638 private void removeConfig() {
639 Set<Interface> intfs = getInterfaces();
640 if (!intfs.isEmpty()) {
641 intfs.forEach(intf -> processIntfFilter(false, intf));
642 }
643 networkConfigService.removeConfig();
644 }
645
646 private Set<Interface> getInterfaces() {
647
648 return interfaces.isEmpty() ? interfaceService.getInterfaces()
649 : filterInterfaces(interfaces);
Charles Chand0fd5dc2016-02-16 23:14:49 -0800650 }
kishore7c42cbe2016-04-26 22:49:36 +0530651
652 /**
653 * Update the flows comparing previous event and current event.
654 *
655 * @param prevIntf the previous interface event
Jonathan Hartf8035d32016-06-16 16:23:26 -0700656 * @param intf the current occurred update event
kishore7c42cbe2016-04-26 22:49:36 +0530657 **/
658 private void updateInterface(Interface prevIntf, Interface intf) {
kishore36d1c272016-09-21 15:44:10 +0530659 if (!intf.connectPoint().deviceId().equals(controlPlaneConnectPoint.deviceId())
660 || !interfaces.contains(intf.name())) {
661 // Ignore interfaces if they are not on the router switch
662 return;
663 }
kdarapuc1f4aa72017-01-04 18:05:19 +0530664 if (!prevIntf.vlan().equals(intf.vlan()) || !prevIntf.mac().equals(intf.mac())) {
Jonathan Hartf8035d32016-06-16 16:23:26 -0700665 provisionInterface(prevIntf, false);
666 provisionInterface(intf, true);
kishore7c42cbe2016-04-26 22:49:36 +0530667 } else {
668 List<InterfaceIpAddress> removeIps =
669 prevIntf.ipAddressesList().stream()
670 .filter(pre -> !intf.ipAddressesList().contains(pre))
671 .collect(Collectors.toList());
672 List<InterfaceIpAddress> addIps =
673 intf.ipAddressesList().stream()
674 .filter(cur -> !prevIntf.ipAddressesList().contains(cur))
675 .collect(Collectors.toList());
676 // removing flows with match parameters present in previous subject
Jonathan Hartf8035d32016-06-16 16:23:26 -0700677 updateInterfaceForwarding(new Interface(prevIntf.name(), prevIntf.connectPoint(),
kishore7c42cbe2016-04-26 22:49:36 +0530678 removeIps, prevIntf.mac(), prevIntf.vlan()), false);
679 // adding flows with match parameters present in event subject
Jonathan Hartf8035d32016-06-16 16:23:26 -0700680 updateInterfaceForwarding(new Interface(intf.name(), intf.connectPoint(),
kishore7c42cbe2016-04-26 22:49:36 +0530681 addIps, intf.mac(), intf.vlan()), true);
682 }
683 }
684
kishore71a27532016-03-16 20:23:49 +0530685 private class InternalInterfaceListener implements InterfaceListener {
kishore71a27532016-03-16 20:23:49 +0530686 @Override
687 public void event(InterfaceEvent event) {
Charles Chan4a6e91e2016-07-19 18:36:33 -0700688 if (controlPlaneConnectPoint == null) {
kishore36d1c272016-09-21 15:44:10 +0530689 log.warn("Control plane connect point is not configured. Abort InterfaceEvent.");
Charles Chan4a6e91e2016-07-19 18:36:33 -0700690 return;
691 }
692 Interface intf = event.subject();
693 Interface prevIntf = event.prevSubject();
694 switch (event.type()) {
kishore71a27532016-03-16 20:23:49 +0530695 case INTERFACE_ADDED:
kishore36d1c272016-09-21 15:44:10 +0530696 processIntfFilter(true, intf);
kishore71a27532016-03-16 20:23:49 +0530697 break;
698 case INTERFACE_UPDATED:
kishore36d1c272016-09-21 15:44:10 +0530699 updateInterface(prevIntf, intf);
kishore71a27532016-03-16 20:23:49 +0530700 break;
701 case INTERFACE_REMOVED:
kishore36d1c272016-09-21 15:44:10 +0530702 processIntfFilter(false, intf);
kishore71a27532016-03-16 20:23:49 +0530703 break;
704 default:
705 break;
Charles Chan4a6e91e2016-07-19 18:36:33 -0700706 }
kishore71a27532016-03-16 20:23:49 +0530707 }
708 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800709}