blob: 9a16bfd14d27d4617e8951d22dd829f05ad9caa9 [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;
Vinayak Tejankar3a409c62017-01-12 02:20:53 +053020import com.google.common.collect.Lists;
Jonathan Hartf04b7d92016-03-29 09:39:11 -070021import com.google.common.collect.Maps;
Jonathan Hart6344f572015-12-15 08:26:25 -080022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onlab.packet.EthType;
Vinayak Tejankar3a409c62017-01-12 02:20:53 +053028import org.onlab.packet.Ip4Address;
Charles Chand0fd5dc2016-02-16 23:14:49 -080029import org.onlab.packet.IpPrefix;
30import org.onlab.packet.MacAddress;
Saurav Das49cb5a12016-01-16 22:54:07 -080031import org.onlab.packet.VlanId;
Vinayak Tejankar3a409c62017-01-12 02:20:53 +053032
33import static org.onlab.packet.Ethernet.TYPE_ARP;
34import static org.onlab.packet.Ethernet.TYPE_IPV4;
35import static org.onlab.packet.Ethernet.TYPE_IPV6;
36import static org.onlab.packet.ICMP6.NEIGHBOR_ADVERTISEMENT;
37import static org.onlab.packet.ICMP6.NEIGHBOR_SOLICITATION;
38import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
kishore786b7e42016-05-19 16:25:57 +053039import org.onosproject.app.ApplicationService;
Jonathan Hart6344f572015-12-15 08:26:25 -080040import org.onosproject.core.ApplicationId;
41import org.onosproject.core.CoreService;
42import org.onosproject.incubator.net.intf.Interface;
kishore71a27532016-03-16 20:23:49 +053043import org.onosproject.incubator.net.intf.InterfaceEvent;
44import org.onosproject.incubator.net.intf.InterfaceListener;
Jonathan Hart6344f572015-12-15 08:26:25 -080045import org.onosproject.incubator.net.intf.InterfaceService;
Charles Chand0fd5dc2016-02-16 23:14:49 -080046import org.onosproject.mastership.MastershipService;
Jonathan Hart6344f572015-12-15 08:26:25 -080047import org.onosproject.net.ConnectPoint;
48import org.onosproject.net.DeviceId;
Charles Chand0fd5dc2016-02-16 23:14:49 -080049import org.onosproject.net.Host;
Jonathan Hart6344f572015-12-15 08:26:25 -080050import org.onosproject.net.PortNumber;
51import org.onosproject.net.config.NetworkConfigEvent;
52import org.onosproject.net.config.NetworkConfigListener;
53import org.onosproject.net.config.NetworkConfigService;
54import org.onosproject.net.device.DeviceEvent;
55import org.onosproject.net.device.DeviceListener;
56import org.onosproject.net.device.DeviceService;
57import org.onosproject.net.flow.DefaultTrafficSelector;
58import org.onosproject.net.flow.DefaultTrafficTreatment;
59import org.onosproject.net.flow.TrafficSelector;
60import org.onosproject.net.flow.TrafficTreatment;
61import org.onosproject.net.flowobjective.DefaultForwardingObjective;
Saurav Das49cb5a12016-01-16 22:54:07 -080062import org.onosproject.net.flowobjective.DefaultNextObjective;
Jonathan Hart6344f572015-12-15 08:26:25 -080063import org.onosproject.net.flowobjective.FlowObjectiveService;
64import org.onosproject.net.flowobjective.ForwardingObjective;
Saurav Das49cb5a12016-01-16 22:54:07 -080065import org.onosproject.net.flowobjective.NextObjective;
Charles Chand0fd5dc2016-02-16 23:14:49 -080066import org.onosproject.net.host.HostEvent;
67import org.onosproject.net.host.HostListener;
68import org.onosproject.net.host.HostService;
Jonathan Hart6344f572015-12-15 08:26:25 -080069import org.onosproject.net.host.InterfaceIpAddress;
70import org.onosproject.routing.RoutingService;
71import org.onosproject.routing.config.RouterConfig;
72import org.slf4j.Logger;
73
Jonathan Hartf04b7d92016-03-29 09:39:11 -070074import java.util.Collections;
75import java.util.Iterator;
76import java.util.List;
77import java.util.Map;
78import java.util.Optional;
79import java.util.Set;
kishore7c42cbe2016-04-26 22:49:36 +053080import java.util.stream.Collectors;
Jonathan Hartf04b7d92016-03-29 09:39:11 -070081
82import static com.google.common.base.Preconditions.checkState;
83import static org.slf4j.LoggerFactory.getLogger;
Jonathan Hart6344f572015-12-15 08:26:25 -080084
85/**
86 * Manages connectivity between peers redirecting control traffic to a routing
87 * control plane available on the dataplane.
88 */
89@Component(immediate = true, enabled = false)
90public class ControlPlaneRedirectManager {
91
92 private final Logger log = getLogger(getClass());
93
Charles Chand0fd5dc2016-02-16 23:14:49 -080094 private static final int MIN_IP_PRIORITY = 10;
kishore36d1c272016-09-21 15:44:10 +053095 private static final int IPV4_PRIORITY = 2000;
96 private static final int IPV6_PRIORITY = 500;
Vinayak Tejankar3a409c62017-01-12 02:20:53 +053097 static final int ACL_PRIORITY = 40001;
Jonathan Hartea492382016-01-13 09:33:13 -080098 private static final int OSPF_IP_PROTO = 0x59;
Jonathan Hart6344f572015-12-15 08:26:25 -080099
kishore786b7e42016-05-19 16:25:57 +0530100 private static final String APP_NAME = "org.onosproject.vrouter";
Jonathan Hart6344f572015-12-15 08:26:25 -0800101 private ApplicationId appId;
102
103 private ConnectPoint controlPlaneConnectPoint;
Jonathan Hartea492382016-01-13 09:33:13 -0800104 private boolean ospfEnabled = false;
Jonathan Hart883fd372016-02-10 14:36:15 -0800105 private List<String> interfaces = Collections.emptyList();
Charles Chand0fd5dc2016-02-16 23:14:49 -0800106 private Map<Host, Set<Integer>> peerNextId = Maps.newConcurrentMap();
Jonathan Hart6344f572015-12-15 08:26:25 -0800107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected CoreService coreService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected DeviceService deviceService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected InterfaceService interfaceService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected FlowObjectiveService flowObjectiveService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected NetworkConfigService networkConfigService;
122
Charles Chand0fd5dc2016-02-16 23:14:49 -0800123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected MastershipService mastershipService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected HostService hostService;
128
kishore786b7e42016-05-19 16:25:57 +0530129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected ApplicationService applicationService;
131
Jonathan Hart6344f572015-12-15 08:26:25 -0800132 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
133 private final InternalNetworkConfigListener networkConfigListener =
134 new InternalNetworkConfigListener();
Charles Chand0fd5dc2016-02-16 23:14:49 -0800135 private final InternalHostListener hostListener = new InternalHostListener();
kishore71a27532016-03-16 20:23:49 +0530136 private final InternalInterfaceListener interfaceListener = new InternalInterfaceListener();
Jonathan Hart6344f572015-12-15 08:26:25 -0800137
138 @Activate
139 public void activate() {
140 this.appId = coreService.registerApplication(APP_NAME);
141
142 deviceService.addListener(deviceListener);
143 networkConfigService.addListener(networkConfigListener);
Charles Chand0fd5dc2016-02-16 23:14:49 -0800144 hostService.addListener(hostListener);
kishore71a27532016-03-16 20:23:49 +0530145 interfaceService.addListener(interfaceListener);
Jonathan Hart6344f572015-12-15 08:26:25 -0800146
Jonathan Hartf8035d32016-06-16 16:23:26 -0700147 readConfig();
Charles Chan00d8b5f2016-12-04 17:17:39 -0800148
149 // FIXME There can be an issue when this component is deactivated before vRouter
Jonathan Hartf8035d32016-06-16 16:23:26 -0700150 applicationService.registerDeactivateHook(this.appId, () -> provisionDevice(false));
Jonathan Hart6344f572015-12-15 08:26:25 -0800151 }
152
153 @Deactivate
154 public void deactivate() {
155 deviceService.removeListener(deviceListener);
156 networkConfigService.removeListener(networkConfigListener);
Charles Chand0fd5dc2016-02-16 23:14:49 -0800157 hostService.removeListener(hostListener);
kishore71a27532016-03-16 20:23:49 +0530158 interfaceService.removeListener(interfaceListener);
Jonathan Hart6344f572015-12-15 08:26:25 -0800159 }
160
kishore786b7e42016-05-19 16:25:57 +0530161 /**
162 * Installs or removes interface configuration
163 * based on the flag used on activate or deactivate.
164 *
kishore786b7e42016-05-19 16:25:57 +0530165 **/
Jonathan Hartf8035d32016-06-16 16:23:26 -0700166 private void readConfig() {
Jonathan Hart6344f572015-12-15 08:26:25 -0800167 ApplicationId routingAppId =
168 coreService.registerApplication(RoutingService.ROUTER_APP_ID);
169
170 RouterConfig config = networkConfigService.getConfig(
171 routingAppId, RoutingService.ROUTER_CONFIG_CLASS);
172
173 if (config == null) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800174 log.warn("Router config not available");
Jonathan Hart6344f572015-12-15 08:26:25 -0800175 return;
176 }
177
Jonathan Hartea492382016-01-13 09:33:13 -0800178 controlPlaneConnectPoint = config.getControlPlaneConnectPoint();
179 ospfEnabled = config.getOspfEnabled();
Jonathan Hart883fd372016-02-10 14:36:15 -0800180 interfaces = config.getInterfaces();
Jonathan Hart6344f572015-12-15 08:26:25 -0800181
Jonathan Hartf8035d32016-06-16 16:23:26 -0700182 provisionDevice(true);
Jonathan Hartea492382016-01-13 09:33:13 -0800183 }
184
kishore786b7e42016-05-19 16:25:57 +0530185 /**
186 * Installs or removes interface configuration for each interface
187 * based on the flag used on activate or deactivate.
188 *
Jonathan Hartf8035d32016-06-16 16:23:26 -0700189 * @param install true to install flows, false to remove them
kishore786b7e42016-05-19 16:25:57 +0530190 **/
Jonathan Hartf8035d32016-06-16 16:23:26 -0700191 private void provisionDevice(boolean install) {
Jonathan Hart6344f572015-12-15 08:26:25 -0800192 if (controlPlaneConnectPoint != null &&
193 deviceService.isAvailable(controlPlaneConnectPoint.deviceId())) {
Jonathan Hartea492382016-01-13 09:33:13 -0800194 DeviceId deviceId = controlPlaneConnectPoint.deviceId();
195
196 interfaceService.getInterfaces().stream()
197 .filter(intf -> intf.connectPoint().deviceId().equals(deviceId))
Jonathan Hart883fd372016-02-10 14:36:15 -0800198 .filter(intf -> interfaces.isEmpty() || interfaces.contains(intf.name()))
Jonathan Hartf8035d32016-06-16 16:23:26 -0700199 .forEach(intf -> provisionInterface(intf, install));
Jonathan Hartea492382016-01-13 09:33:13 -0800200
201 log.info("Set up interfaces on {}", controlPlaneConnectPoint.deviceId());
Jonathan Hart6344f572015-12-15 08:26:25 -0800202 }
203 }
204
Jonathan Hartf8035d32016-06-16 16:23:26 -0700205 private void provisionInterface(Interface intf, boolean install) {
206 updateInterfaceForwarding(intf, install);
207 updateOspfForwarding(intf, install);
Jonathan Hart6344f572015-12-15 08:26:25 -0800208 }
209
kishore71a27532016-03-16 20:23:49 +0530210 /**
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700211 * Installs or removes the basic forwarding flows for each interface
kishore71a27532016-03-16 20:23:49 +0530212 * based on the flag used.
213 *
214 * @param intf the Interface on which event is received
215 * @param install true to create an add objective, false to create a remove
216 * objective
217 **/
Jonathan Hartf8035d32016-06-16 16:23:26 -0700218 private void updateInterfaceForwarding(Interface intf, boolean install) {
Jonathan Hart6344f572015-12-15 08:26:25 -0800219 log.debug("Adding interface objectives for {}", intf);
220
221 DeviceId deviceId = controlPlaneConnectPoint.deviceId();
222 PortNumber controlPlanePort = controlPlaneConnectPoint.port();
Jonathan Hart6344f572015-12-15 08:26:25 -0800223 for (InterfaceIpAddress ip : intf.ipAddresses()) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800224 // create nextObjectives for forwarding to this interface and the
225 // controlPlaneConnectPoint
226 int cpNextId, intfNextId;
227 if (intf.vlan() == VlanId.NONE) {
kishore71a27532016-03-16 20:23:49 +0530228 cpNextId = modifyNextObjective(deviceId, controlPlanePort,
Saurav Das49cb5a12016-01-16 22:54:07 -0800229 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
kishore71a27532016-03-16 20:23:49 +0530230 true, install);
231 intfNextId = modifyNextObjective(deviceId, intf.connectPoint().port(),
Saurav Das49cb5a12016-01-16 22:54:07 -0800232 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
kishore71a27532016-03-16 20:23:49 +0530233 true, install);
Saurav Das49cb5a12016-01-16 22:54:07 -0800234 } else {
kishore71a27532016-03-16 20:23:49 +0530235 cpNextId = modifyNextObjective(deviceId, controlPlanePort,
236 intf.vlan(), false, install);
237 intfNextId = modifyNextObjective(deviceId, intf.connectPoint().port(),
238 intf.vlan(), false, install);
Saurav Das49cb5a12016-01-16 22:54:07 -0800239 }
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530240 List<ForwardingObjective> fwdToSend = Lists.newArrayList();
241 TrafficSelector selector;
242 // IP traffic toward the router.
243 selector = buildIPDstSelector(
244 ip.ipAddress().toIpPrefix(),
245 intf.connectPoint().port(),
246 null,
247 intf.mac(),
248 intf.vlan()
249 );
250 fwdToSend.add(buildForwardingObjective(selector, null, cpNextId, install, ACL_PRIORITY));
251 // IP traffic from the router.
252 selector = buildIPSrcSelector(
253 ip.ipAddress().toIpPrefix(),
254 controlPlanePort,
255 intf.mac(),
256 null,
257 intf.vlan()
258 );
259 fwdToSend.add(buildForwardingObjective(selector, null, intfNextId, install, ACL_PRIORITY));
260 // We build the punt treatment.
261 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Jonathan Hart6344f572015-12-15 08:26:25 -0800262 .punt()
263 .build();
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530264 // Handling of neighbour discovery protocols.
265 // IPv4 traffic - we have to deal with the ARP protocol.
266 // IPv6 traffic - we have to deal with the NDP protocol.
267 if (ip.ipAddress().isIp4()) {
268 // ARP traffic towards the router.
269 selector = buildArpSelector(
270 intf.connectPoint().port(),
271 intf.vlan(),
272 null,
273 null
274 );
275 fwdToSend.add(buildForwardingObjective(selector, treatment, cpNextId, install, ACL_PRIORITY + 1));
276 // ARP traffic from the router.
277 selector = buildArpSelector(
278 controlPlanePort,
279 intf.vlan(),
280 ip.ipAddress().getIp4Address(),
281 intf.mac()
282 );
283 fwdToSend.add(buildForwardingObjective(selector, treatment, intfNextId, install, ACL_PRIORITY + 1));
284 } else {
285 // Neighbour solicitation traffic towards the router.
286 selector = buildNdpSelector(
287 intf.connectPoint().port(),
288 intf.vlan(),
289 null,
290 NEIGHBOR_SOLICITATION,
291 null
292 );
293 fwdToSend.add(buildForwardingObjective(selector, treatment, cpNextId, install, ACL_PRIORITY + 1));
294 // Neighbour solicitation traffic from the router.
295 selector = buildNdpSelector(
296 controlPlanePort,
297 intf.vlan(),
298 ip.ipAddress().toIpPrefix(),
299 NEIGHBOR_SOLICITATION,
300 intf.mac()
301 );
302 fwdToSend.add(buildForwardingObjective(selector, treatment, intfNextId, install, ACL_PRIORITY + 1));
303 // Neighbour advertisement traffic towards the router.
304 selector = buildNdpSelector(
305 intf.connectPoint().port(),
306 intf.vlan(),
307 null,
308 NEIGHBOR_ADVERTISEMENT,
309 null
310 );
311 fwdToSend.add(buildForwardingObjective(selector, treatment, cpNextId, install, ACL_PRIORITY + 1));
312 // Neighbour advertisement traffic from the router.
313 selector = buildNdpSelector(
314 controlPlanePort,
315 intf.vlan(),
316 ip.ipAddress().toIpPrefix(),
317 NEIGHBOR_ADVERTISEMENT,
318 intf.mac()
319 );
320 fwdToSend.add(buildForwardingObjective(selector, treatment, intfNextId, install, ACL_PRIORITY + 1));
321 }
322 // Finally we push the fwd objectives through the flow objective service.
323 fwdToSend.stream().forEach(forwardingObjective ->
324 flowObjectiveService.forward(deviceId, forwardingObjective)
325 );
Jonathan Hart6344f572015-12-15 08:26:25 -0800326 }
327 }
328
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530329 static TrafficSelector.Builder buildBaseSelectorBuilder(PortNumber inPort,
330 MacAddress srcMac,
331 MacAddress dstMac,
332 VlanId vlanId) {
333 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
334 if (inPort != null) {
335 selectorBuilder.matchInPort(inPort);
336 }
337 if (srcMac != null) {
338 selectorBuilder.matchEthSrc(srcMac);
339 }
340 if (dstMac != null) {
341 selectorBuilder.matchEthDst(dstMac);
342 }
343 if (vlanId != null) {
344 selectorBuilder.matchVlanId(vlanId);
345 }
346 return selectorBuilder;
347 }
348
349 static TrafficSelector buildIPDstSelector(IpPrefix dstIp,
350 PortNumber inPort,
351 MacAddress srcMac,
352 MacAddress dstMac,
353 VlanId vlanId) {
354 TrafficSelector.Builder selector = buildBaseSelectorBuilder(inPort, srcMac, dstMac, vlanId);
355 if (dstIp.isIp4()) {
356 selector.matchEthType(TYPE_IPV4);
357 selector.matchIPDst(dstIp);
358 } else {
359 selector.matchEthType(TYPE_IPV6);
360 selector.matchIPv6Dst(dstIp);
361 }
362 return selector.build();
363 }
364
365 static TrafficSelector buildIPSrcSelector(IpPrefix srcIp,
366 PortNumber inPort,
367 MacAddress srcMac,
368 MacAddress dstMac,
369 VlanId vlanId) {
370 TrafficSelector.Builder selector = buildBaseSelectorBuilder(inPort, srcMac, dstMac, vlanId);
371 if (srcIp.isIp4()) {
372 selector.matchEthType(TYPE_IPV4);
373 selector.matchIPSrc(srcIp);
374 } else {
375 selector.matchEthType(TYPE_IPV6);
376 selector.matchIPv6Src(srcIp);
377 }
378 return selector.build();
379 }
380
381 static TrafficSelector buildArpSelector(PortNumber inPort,
382 VlanId vlanId,
383 Ip4Address arpSpa,
384 MacAddress srcMac) {
385 TrafficSelector.Builder selector = buildBaseSelectorBuilder(inPort, null, null, vlanId);
386 selector.matchEthType(TYPE_ARP);
387 if (arpSpa != null) {
388 selector.matchArpSpa(arpSpa);
389 }
390 if (srcMac != null) {
391 selector.matchEthSrc(srcMac);
392 }
393 return selector.build();
394 }
395
396 static TrafficSelector buildNdpSelector(PortNumber inPort,
397 VlanId vlanId,
398 IpPrefix srcIp,
399 byte subProto,
400 MacAddress srcMac) {
401 TrafficSelector.Builder selector = buildBaseSelectorBuilder(inPort, null, null, vlanId);
402 selector.matchEthType(TYPE_IPV6)
403 .matchIPProtocol(PROTOCOL_ICMP6)
404 .matchIcmpv6Type(subProto);
405 if (srcIp != null) {
406 selector.matchIPv6Src(srcIp);
407 }
408 if (srcMac != null) {
409 selector.matchEthSrc(srcMac);
410 }
411 return selector.build();
412 }
413
kishore71a27532016-03-16 20:23:49 +0530414 /**
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700415 * Installs or removes OSPF forwarding rules.
kishore71a27532016-03-16 20:23:49 +0530416 *
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700417 * @param intf the interface on which event is received
kishore71a27532016-03-16 20:23:49 +0530418 * @param install true to create an add objective, false to create a remove
419 * objective
420 **/
421 private void updateOspfForwarding(Interface intf, boolean install) {
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530422 // FIXME IPv6 support has not been implemented yet
Jonathan Hartea492382016-01-13 09:33:13 -0800423 // OSPF to router
424 TrafficSelector toSelector = DefaultTrafficSelector.builder()
425 .matchInPort(intf.connectPoint().port())
426 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
427 .matchVlanId(intf.vlan())
428 .matchIPProtocol((byte) OSPF_IP_PROTO)
429 .build();
Jonathan Hart6344f572015-12-15 08:26:25 -0800430
Saurav Das49cb5a12016-01-16 22:54:07 -0800431 // create nextObjectives for forwarding to the controlPlaneConnectPoint
432 DeviceId deviceId = controlPlaneConnectPoint.deviceId();
433 PortNumber controlPlanePort = controlPlaneConnectPoint.port();
434 int cpNextId;
435 if (intf.vlan() == VlanId.NONE) {
kishore71a27532016-03-16 20:23:49 +0530436 cpNextId = modifyNextObjective(deviceId, controlPlanePort,
Saurav Das49cb5a12016-01-16 22:54:07 -0800437 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
kishore71a27532016-03-16 20:23:49 +0530438 true, install);
Saurav Das49cb5a12016-01-16 22:54:07 -0800439 } else {
kishore71a27532016-03-16 20:23:49 +0530440 cpNextId = modifyNextObjective(deviceId, controlPlanePort,
441 intf.vlan(), false, install);
Saurav Das49cb5a12016-01-16 22:54:07 -0800442 }
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700443 log.debug("OSPF flows intf:{} nextid:{}", intf, cpNextId);
Jonathan Hartea492382016-01-13 09:33:13 -0800444 flowObjectiveService.forward(controlPlaneConnectPoint.deviceId(),
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530445 buildForwardingObjective(toSelector, null, cpNextId, install ? ospfEnabled : install, ACL_PRIORITY));
Jonathan Hartea492382016-01-13 09:33:13 -0800446 }
447
448 /**
Saurav Das49cb5a12016-01-16 22:54:07 -0800449 * Creates a next objective for forwarding to a port. Handles metadata for
450 * some pipelines that require vlan information for egress port.
451 *
452 * @param deviceId the device on which the next objective is being created
453 * @param portNumber the egress port
454 * @param vlanId vlan information for egress port
455 * @param popVlan if vlan tag should be popped or not
kishore71a27532016-03-16 20:23:49 +0530456 * @param install true to create an add next objective, false to create a remove
457 * next objective
Saurav Das49cb5a12016-01-16 22:54:07 -0800458 * @return nextId of the next objective created
459 */
kishore71a27532016-03-16 20:23:49 +0530460 private int modifyNextObjective(DeviceId deviceId, PortNumber portNumber,
461 VlanId vlanId, boolean popVlan, boolean install) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800462 int nextId = flowObjectiveService.allocateNextId();
463 NextObjective.Builder nextObjBuilder = DefaultNextObjective
464 .builder().withId(nextId)
465 .withType(NextObjective.Type.SIMPLE)
466 .fromApp(appId);
467
468 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
469 if (popVlan) {
470 ttBuilder.popVlan();
471 }
472 ttBuilder.setOutput(portNumber);
473
474 // setup metadata to pass to nextObjective - indicate the vlan on egress
475 // if needed by the switch pipeline.
476 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
477 metabuilder.matchVlanId(vlanId);
478
479 nextObjBuilder.withMeta(metabuilder.build());
480 nextObjBuilder.addTreatment(ttBuilder.build());
Jonathan Hartf04b7d92016-03-29 09:39:11 -0700481 log.debug("Submitted next objective {} in device {} for port/vlan {}/{}",
Saurav Das49cb5a12016-01-16 22:54:07 -0800482 nextId, deviceId, portNumber, vlanId);
kishore71a27532016-03-16 20:23:49 +0530483 if (install) {
484 flowObjectiveService.next(deviceId, nextObjBuilder.add());
485 } else {
486 flowObjectiveService.next(deviceId, nextObjBuilder.remove());
487 }
Saurav Das49cb5a12016-01-16 22:54:07 -0800488 return nextId;
489 }
Jonathan Hartf8035d32016-06-16 16:23:26 -0700490
Saurav Das49cb5a12016-01-16 22:54:07 -0800491 /**
492 * Builds a forwarding objective from the given selector, treatment and nextId.
Jonathan Hartea492382016-01-13 09:33:13 -0800493 *
494 * @param selector selector
Saurav Das49cb5a12016-01-16 22:54:07 -0800495 * @param treatment treatment to apply to packet, can be null
496 * @param nextId next objective to point to for forwarding packet
Jonathan Hartea492382016-01-13 09:33:13 -0800497 * @param add true to create an add objective, false to create a remove
498 * objective
499 * @return forwarding objective
500 */
501 private ForwardingObjective buildForwardingObjective(TrafficSelector selector,
502 TrafficTreatment treatment,
Saurav Das49cb5a12016-01-16 22:54:07 -0800503 int nextId,
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530504 boolean add,
505 int priority) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800506 DefaultForwardingObjective.Builder fobBuilder = DefaultForwardingObjective.builder();
507 fobBuilder.withSelector(selector);
508 if (treatment != null) {
509 fobBuilder.withTreatment(treatment);
510 }
511 if (nextId != -1) {
512 fobBuilder.nextStep(nextId);
513 }
514 fobBuilder.fromApp(appId)
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530515 .withPriority(priority)
Saurav Das49cb5a12016-01-16 22:54:07 -0800516 .withFlag(ForwardingObjective.Flag.VERSATILE);
Jonathan Hartea492382016-01-13 09:33:13 -0800517
518 return add ? fobBuilder.add() : fobBuilder.remove();
Jonathan Hart6344f572015-12-15 08:26:25 -0800519 }
520
Jonathan Hartea492382016-01-13 09:33:13 -0800521 /**
522 * Listener for device events.
523 */
Jonathan Hart6344f572015-12-15 08:26:25 -0800524 private class InternalDeviceListener implements DeviceListener {
kishore71a27532016-03-16 20:23:49 +0530525
Jonathan Hart6344f572015-12-15 08:26:25 -0800526 @Override
527 public void event(DeviceEvent event) {
528 if (controlPlaneConnectPoint != null &&
529 event.subject().id().equals(controlPlaneConnectPoint.deviceId())) {
530 switch (event.type()) {
531 case DEVICE_ADDED:
532 case DEVICE_AVAILABILITY_CHANGED:
533 if (deviceService.isAvailable(event.subject().id())) {
534 log.info("Device connected {}", event.subject().id());
Jonathan Hartf8035d32016-06-16 16:23:26 -0700535 provisionDevice(true);
Jonathan Hart6344f572015-12-15 08:26:25 -0800536 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800537 break;
538 case DEVICE_UPDATED:
539 case DEVICE_REMOVED:
540 case DEVICE_SUSPENDED:
541 case PORT_ADDED:
542 case PORT_UPDATED:
543 case PORT_REMOVED:
544 default:
545 break;
546 }
547 }
548 }
549 }
550
Jonathan Hartea492382016-01-13 09:33:13 -0800551 /**
552 * Listener for network config events.
553 */
Jonathan Hart6344f572015-12-15 08:26:25 -0800554 private class InternalNetworkConfigListener implements NetworkConfigListener {
kishore71a27532016-03-16 20:23:49 +0530555
Jonathan Hart6344f572015-12-15 08:26:25 -0800556 @Override
557 public void event(NetworkConfigEvent event) {
Jonathan Hartea492382016-01-13 09:33:13 -0800558 if (event.configClass().equals(RoutingService.ROUTER_CONFIG_CLASS)) {
Jonathan Hart6344f572015-12-15 08:26:25 -0800559 switch (event.type()) {
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530560 case CONFIG_ADDED:
561 case CONFIG_UPDATED:
562 readConfig();
563 if (event.prevConfig().isPresent()) {
kishore36d1c272016-09-21 15:44:10 +0530564 updateConfig(event);
565 }
566
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530567 break;
568 case CONFIG_REGISTERED:
569 case CONFIG_UNREGISTERED:
570 case CONFIG_REMOVED:
571 removeConfig();
kishore36d1c272016-09-21 15:44:10 +0530572
573 break;
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530574 default:
575 break;
Jonathan Hart6344f572015-12-15 08:26:25 -0800576 }
577 }
578 }
579 }
Charles Chand0fd5dc2016-02-16 23:14:49 -0800580
581 /**
582 * Listener for host events.
583 */
584 private class InternalHostListener implements HostListener {
kishore71a27532016-03-16 20:23:49 +0530585
Charles Chand0fd5dc2016-02-16 23:14:49 -0800586 private void peerAdded(HostEvent event) {
587 Host peer = event.subject();
588 Optional<Interface> peerIntf =
589 interfaceService.getInterfacesByPort(peer.location()).stream()
590 .filter(intf -> interfaces.isEmpty() || interfaces.contains(intf.name()))
591 .filter(intf -> peer.vlan().equals(intf.vlan()))
592 .findFirst();
593 if (!peerIntf.isPresent()) {
594 log.debug("Adding peer {}/{} on {} but the interface is not configured",
595 peer.mac(), peer.vlan(), peer.location());
596 return;
597 }
598
599 // Generate L3 Unicast groups and store it in the map
600 int toRouterL3Unicast = createPeerGroup(peer.mac(), peerIntf.get().mac(),
601 peer.vlan(), peer.location().deviceId(), controlPlaneConnectPoint.port());
602 int toPeerL3Unicast = createPeerGroup(peerIntf.get().mac(), peer.mac(),
603 peer.vlan(), peer.location().deviceId(), peer.location().port());
604 peerNextId.put(peer, ImmutableSortedSet.of(toRouterL3Unicast, toPeerL3Unicast));
605
606 // From peer to router
607 peerIntf.get().ipAddresses().forEach(routerIp -> {
608 flowObjectiveService.forward(peer.location().deviceId(),
609 createPeerObjBuilder(toRouterL3Unicast, routerIp.ipAddress().toIpPrefix()).add());
610 });
611
612 // From router to peer
613 peer.ipAddresses().forEach(peerIp -> {
614 flowObjectiveService.forward(peer.location().deviceId(),
615 createPeerObjBuilder(toPeerL3Unicast, peerIp.toIpPrefix()).add());
616 });
617 }
618
619 private void peerRemoved(HostEvent event) {
620 Host peer = event.subject();
621 Optional<Interface> peerIntf =
622 interfaceService.getInterfacesByPort(peer.location()).stream()
623 .filter(intf -> interfaces.isEmpty() || interfaces.contains(intf.name()))
624 .filter(intf -> peer.vlan().equals(intf.vlan()))
625 .findFirst();
626 if (!peerIntf.isPresent()) {
627 log.debug("Removing peer {}/{} on {} but the interface is not configured",
628 peer.mac(), peer.vlan(), peer.location());
629 return;
630 }
631
Charles Chand0fd5dc2016-02-16 23:14:49 -0800632 checkState(peerNextId.get(peer) != null,
633 "Peer nextId should not be null");
634 checkState(peerNextId.get(peer).size() == 2,
635 "Wrong nextId associated with the peer");
636 Iterator<Integer> iter = peerNextId.get(peer).iterator();
637 int toRouterL3Unicast = iter.next();
638 int toPeerL3Unicast = iter.next();
639
640 // From peer to router
641 peerIntf.get().ipAddresses().forEach(routerIp -> {
642 flowObjectiveService.forward(peer.location().deviceId(),
643 createPeerObjBuilder(toRouterL3Unicast, routerIp.ipAddress().toIpPrefix()).remove());
644 });
645
646 // From router to peer
647 peer.ipAddresses().forEach(peerIp -> {
648 flowObjectiveService.forward(peer.location().deviceId(),
649 createPeerObjBuilder(toPeerL3Unicast, peerIp.toIpPrefix()).remove());
650 });
651 }
652
653 private ForwardingObjective.Builder createPeerObjBuilder(
654 int nextId, IpPrefix ipAddresses) {
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530655 TrafficSelector selector = buildIPDstSelector(ipAddresses, null, null, null, null);
Charles Chand0fd5dc2016-02-16 23:14:49 -0800656 DefaultForwardingObjective.Builder builder =
657 DefaultForwardingObjective.builder()
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530658 .withSelector(selector)
Charles Chand0fd5dc2016-02-16 23:14:49 -0800659 .fromApp(appId)
660 .withPriority(getPriorityFromPrefix(ipAddresses))
661 .withFlag(ForwardingObjective.Flag.SPECIFIC);
662 if (nextId != -1) {
663 builder.nextStep(nextId);
664 }
665 return builder;
666 }
667
668 private int createPeerGroup(MacAddress srcMac, MacAddress dstMac,
669 VlanId vlanId, DeviceId deviceId, PortNumber port) {
670 int nextId = flowObjectiveService.allocateNextId();
671 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
672 .withId(nextId)
673 .withType(NextObjective.Type.SIMPLE)
674 .fromApp(appId);
675
676 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
677 ttBuilder.setEthSrc(srcMac);
678 ttBuilder.setEthDst(dstMac);
679 ttBuilder.setOutput(port);
680 nextObjBuilder.addTreatment(ttBuilder.build());
681
682 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
683 VlanId matchVlanId = (vlanId.equals(VlanId.NONE)) ?
684 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN) :
685 vlanId;
686 metabuilder.matchVlanId(matchVlanId);
687 nextObjBuilder.withMeta(metabuilder.build());
688
689 flowObjectiveService.next(deviceId, nextObjBuilder.add());
690 return nextId;
691 }
692
693 @Override
694 public void event(HostEvent event) {
695 DeviceId deviceId = event.subject().location().deviceId();
696 if (!mastershipService.isLocalMaster(deviceId)) {
697 return;
698 }
699 switch (event.type()) {
700 case HOST_ADDED:
701 peerAdded(event);
702 break;
703 case HOST_MOVED:
704 //TODO We assume BGP peer does not move for now
705 break;
706 case HOST_REMOVED:
707 peerRemoved(event);
708 break;
709 case HOST_UPDATED:
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530710 //FIXME We assume BGP peer does not change IP for now
711 // but we can discover new address.
Charles Chand0fd5dc2016-02-16 23:14:49 -0800712 break;
713 default:
714 break;
715 }
716 }
717 }
718
719 private int getPriorityFromPrefix(IpPrefix prefix) {
720 return (prefix.isIp4()) ?
kishore36d1c272016-09-21 15:44:10 +0530721 IPV4_PRIORITY * prefix.prefixLength() + MIN_IP_PRIORITY :
722 IPV6_PRIORITY * prefix.prefixLength() + MIN_IP_PRIORITY;
723 }
724
725 private void updateConfig(NetworkConfigEvent event) {
726 RouterConfig prevRouterConfig = (RouterConfig) event.prevConfig().get();
727 List<String> prevInterfaces = prevRouterConfig.getInterfaces();
728 Set<Interface> previntfs = filterInterfaces(prevInterfaces);
729 if (previntfs.isEmpty() && !interfaces.isEmpty()) {
730 interfaceService.getInterfaces().stream()
731 .filter(intf -> !interfaces.contains(intf.name()))
732 .forEach(intf -> processIntfFilter(false, intf));
733 return;
734 }
735 //remove the filtering objective for the interfaces which are not
736 //part of updated interfaces list.
737 previntfs.stream()
738 .filter(intf -> !interfaces.contains(intf.name()))
739 .forEach(intf -> processIntfFilter(false, intf));
740 }
741
742 /**
743 * process filtering objective for interface add/remove.
744 *
745 * @param install true to install flows, false to uninstall the flows
746 * @param intf Interface object captured on event
747 */
748 private void processIntfFilter(boolean install, Interface intf) {
749
750 if (!intf.connectPoint().deviceId().equals(controlPlaneConnectPoint.deviceId())) {
751 // Ignore interfaces if they are not on the router switch
752 return;
753 }
754 if (!interfaces.contains(intf.name()) && install) {
755 return;
756 }
757
758 provisionInterface(intf, install);
759 }
760
761 private Set<Interface> filterInterfaces(List<String> interfaces) {
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530762 return interfaceService.getInterfaces().stream()
kishore36d1c272016-09-21 15:44:10 +0530763 .filter(intf -> intf.connectPoint().deviceId().equals(controlPlaneConnectPoint.deviceId()))
764 .filter(intf -> interfaces.contains(intf.name()))
765 .collect(Collectors.toSet());
kishore36d1c272016-09-21 15:44:10 +0530766 }
767
768 private void removeConfig() {
769 Set<Interface> intfs = getInterfaces();
770 if (!intfs.isEmpty()) {
771 intfs.forEach(intf -> processIntfFilter(false, intf));
772 }
773 networkConfigService.removeConfig();
774 }
775
776 private Set<Interface> getInterfaces() {
777
778 return interfaces.isEmpty() ? interfaceService.getInterfaces()
779 : filterInterfaces(interfaces);
Charles Chand0fd5dc2016-02-16 23:14:49 -0800780 }
kishore7c42cbe2016-04-26 22:49:36 +0530781
782 /**
783 * Update the flows comparing previous event and current event.
784 *
785 * @param prevIntf the previous interface event
Jonathan Hartf8035d32016-06-16 16:23:26 -0700786 * @param intf the current occurred update event
kishore7c42cbe2016-04-26 22:49:36 +0530787 **/
788 private void updateInterface(Interface prevIntf, Interface intf) {
kishore36d1c272016-09-21 15:44:10 +0530789 if (!intf.connectPoint().deviceId().equals(controlPlaneConnectPoint.deviceId())
790 || !interfaces.contains(intf.name())) {
791 // Ignore interfaces if they are not on the router switch
792 return;
793 }
kdarapuc1f4aa72017-01-04 18:05:19 +0530794 if (!prevIntf.vlan().equals(intf.vlan()) || !prevIntf.mac().equals(intf.mac())) {
Jonathan Hartf8035d32016-06-16 16:23:26 -0700795 provisionInterface(prevIntf, false);
796 provisionInterface(intf, true);
kishore7c42cbe2016-04-26 22:49:36 +0530797 } else {
798 List<InterfaceIpAddress> removeIps =
799 prevIntf.ipAddressesList().stream()
800 .filter(pre -> !intf.ipAddressesList().contains(pre))
801 .collect(Collectors.toList());
802 List<InterfaceIpAddress> addIps =
803 intf.ipAddressesList().stream()
804 .filter(cur -> !prevIntf.ipAddressesList().contains(cur))
805 .collect(Collectors.toList());
806 // removing flows with match parameters present in previous subject
Jonathan Hartf8035d32016-06-16 16:23:26 -0700807 updateInterfaceForwarding(new Interface(prevIntf.name(), prevIntf.connectPoint(),
kishore7c42cbe2016-04-26 22:49:36 +0530808 removeIps, prevIntf.mac(), prevIntf.vlan()), false);
809 // adding flows with match parameters present in event subject
Jonathan Hartf8035d32016-06-16 16:23:26 -0700810 updateInterfaceForwarding(new Interface(intf.name(), intf.connectPoint(),
kishore7c42cbe2016-04-26 22:49:36 +0530811 addIps, intf.mac(), intf.vlan()), true);
812 }
813 }
814
kishore71a27532016-03-16 20:23:49 +0530815 private class InternalInterfaceListener implements InterfaceListener {
kishore71a27532016-03-16 20:23:49 +0530816 @Override
817 public void event(InterfaceEvent event) {
Charles Chan4a6e91e2016-07-19 18:36:33 -0700818 if (controlPlaneConnectPoint == null) {
kishore36d1c272016-09-21 15:44:10 +0530819 log.warn("Control plane connect point is not configured. Abort InterfaceEvent.");
Charles Chan4a6e91e2016-07-19 18:36:33 -0700820 return;
821 }
822 Interface intf = event.subject();
823 Interface prevIntf = event.prevSubject();
824 switch (event.type()) {
kishore71a27532016-03-16 20:23:49 +0530825 case INTERFACE_ADDED:
kishore36d1c272016-09-21 15:44:10 +0530826 processIntfFilter(true, intf);
kishore71a27532016-03-16 20:23:49 +0530827 break;
828 case INTERFACE_UPDATED:
kishore36d1c272016-09-21 15:44:10 +0530829 updateInterface(prevIntf, intf);
kishore71a27532016-03-16 20:23:49 +0530830 break;
831 case INTERFACE_REMOVED:
kishore36d1c272016-09-21 15:44:10 +0530832 processIntfFilter(false, intf);
kishore71a27532016-03-16 20:23:49 +0530833 break;
834 default:
835 break;
Charles Chan4a6e91e2016-07-19 18:36:33 -0700836 }
kishore71a27532016-03-16 20:23:49 +0530837 }
838 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800839}
Vinayak Tejankar3a409c62017-01-12 02:20:53 +0530840