blob: b78b0ff2b9878b3441cdef6a3b7488192d40357a [file] [log] [blame]
Jonathan Hart6344f572015-12-15 08:26:25 -08001/*
2 * Copyright 2016 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.routing.impl;
18
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.onlab.packet.EthType;
Saurav Das49cb5a12016-01-16 22:54:07 -080025import org.onlab.packet.VlanId;
Jonathan Hart6344f572015-12-15 08:26:25 -080026import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
28import org.onosproject.incubator.net.intf.Interface;
29import org.onosproject.incubator.net.intf.InterfaceService;
30import org.onosproject.net.ConnectPoint;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.PortNumber;
33import org.onosproject.net.config.NetworkConfigEvent;
34import org.onosproject.net.config.NetworkConfigListener;
35import org.onosproject.net.config.NetworkConfigService;
36import org.onosproject.net.device.DeviceEvent;
37import org.onosproject.net.device.DeviceListener;
38import org.onosproject.net.device.DeviceService;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
43import org.onosproject.net.flowobjective.DefaultForwardingObjective;
Saurav Das49cb5a12016-01-16 22:54:07 -080044import org.onosproject.net.flowobjective.DefaultNextObjective;
Jonathan Hart6344f572015-12-15 08:26:25 -080045import org.onosproject.net.flowobjective.FlowObjectiveService;
46import org.onosproject.net.flowobjective.ForwardingObjective;
Saurav Das49cb5a12016-01-16 22:54:07 -080047import org.onosproject.net.flowobjective.NextObjective;
Jonathan Hart6344f572015-12-15 08:26:25 -080048import org.onosproject.net.host.InterfaceIpAddress;
49import org.onosproject.routing.RoutingService;
50import org.onosproject.routing.config.RouterConfig;
51import org.slf4j.Logger;
52
53import static org.slf4j.LoggerFactory.getLogger;
54
55/**
56 * Manages connectivity between peers redirecting control traffic to a routing
57 * control plane available on the dataplane.
58 */
59@Component(immediate = true, enabled = false)
60public class ControlPlaneRedirectManager {
61
62 private final Logger log = getLogger(getClass());
63
64 private static final int PRIORITY = 40001;
Jonathan Hartea492382016-01-13 09:33:13 -080065 private static final int OSPF_IP_PROTO = 0x59;
Jonathan Hart6344f572015-12-15 08:26:25 -080066
67 private static final String APP_NAME = "org.onosproject.cpredirect";
68 private ApplicationId appId;
69
70 private ConnectPoint controlPlaneConnectPoint;
Jonathan Hartea492382016-01-13 09:33:13 -080071 private boolean ospfEnabled = false;
Jonathan Hart6344f572015-12-15 08:26:25 -080072
73 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected CoreService coreService;
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected DeviceService deviceService;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected InterfaceService interfaceService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected FlowObjectiveService flowObjectiveService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected NetworkConfigService networkConfigService;
87
88 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
89 private final InternalNetworkConfigListener networkConfigListener =
90 new InternalNetworkConfigListener();
91
92 @Activate
93 public void activate() {
94 this.appId = coreService.registerApplication(APP_NAME);
95
96 deviceService.addListener(deviceListener);
97 networkConfigService.addListener(networkConfigListener);
98
99 updateConfig();
100 }
101
102 @Deactivate
103 public void deactivate() {
104 deviceService.removeListener(deviceListener);
105 networkConfigService.removeListener(networkConfigListener);
106 }
107
108 private void updateConfig() {
109 ApplicationId routingAppId =
110 coreService.registerApplication(RoutingService.ROUTER_APP_ID);
111
112 RouterConfig config = networkConfigService.getConfig(
113 routingAppId, RoutingService.ROUTER_CONFIG_CLASS);
114
115 if (config == null) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800116 log.warn("Router config not available");
Jonathan Hart6344f572015-12-15 08:26:25 -0800117 return;
118 }
119
Jonathan Hartea492382016-01-13 09:33:13 -0800120 controlPlaneConnectPoint = config.getControlPlaneConnectPoint();
121 ospfEnabled = config.getOspfEnabled();
Jonathan Hart6344f572015-12-15 08:26:25 -0800122
Jonathan Hartea492382016-01-13 09:33:13 -0800123 updateDevice();
124 }
125
126 private void updateDevice() {
Jonathan Hart6344f572015-12-15 08:26:25 -0800127 if (controlPlaneConnectPoint != null &&
128 deviceService.isAvailable(controlPlaneConnectPoint.deviceId())) {
Jonathan Hartea492382016-01-13 09:33:13 -0800129 DeviceId deviceId = controlPlaneConnectPoint.deviceId();
130
131 interfaceService.getInterfaces().stream()
132 .filter(intf -> intf.connectPoint().deviceId().equals(deviceId))
133 .forEach(this::provisionInterface);
134
135 log.info("Set up interfaces on {}", controlPlaneConnectPoint.deviceId());
Jonathan Hart6344f572015-12-15 08:26:25 -0800136 }
137 }
138
Jonathan Hartea492382016-01-13 09:33:13 -0800139 private void provisionInterface(Interface intf) {
140 addBasicInterfaceForwarding(intf);
141 updateOspfForwarding(intf);
Jonathan Hart6344f572015-12-15 08:26:25 -0800142 }
143
Jonathan Hartea492382016-01-13 09:33:13 -0800144 private void addBasicInterfaceForwarding(Interface intf) {
Jonathan Hart6344f572015-12-15 08:26:25 -0800145 log.debug("Adding interface objectives for {}", intf);
146
147 DeviceId deviceId = controlPlaneConnectPoint.deviceId();
148 PortNumber controlPlanePort = controlPlaneConnectPoint.port();
149
150 for (InterfaceIpAddress ip : intf.ipAddresses()) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800151 // create nextObjectives for forwarding to this interface and the
152 // controlPlaneConnectPoint
153 int cpNextId, intfNextId;
154 if (intf.vlan() == VlanId.NONE) {
155 cpNextId = createNextObjective(deviceId, controlPlanePort,
156 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
157 true);
158 intfNextId = createNextObjective(deviceId, intf.connectPoint().port(),
159 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
160 true);
161 } else {
162 cpNextId = createNextObjective(deviceId, controlPlanePort,
163 intf.vlan(), false);
164 intfNextId = createNextObjective(deviceId, intf.connectPoint().port(),
165 intf.vlan(), false);
166 }
167
Jonathan Hart6344f572015-12-15 08:26:25 -0800168 // IPv4 to router
169 TrafficSelector toSelector = DefaultTrafficSelector.builder()
170 .matchInPort(intf.connectPoint().port())
171 .matchEthDst(intf.mac())
172 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
173 .matchVlanId(intf.vlan())
174 .matchIPDst(ip.ipAddress().toIpPrefix())
175 .build();
176
Jonathan Hart6344f572015-12-15 08:26:25 -0800177 flowObjectiveService.forward(deviceId,
Saurav Das49cb5a12016-01-16 22:54:07 -0800178 buildForwardingObjective(toSelector, null, cpNextId, true));
Jonathan Hart6344f572015-12-15 08:26:25 -0800179
180 // IPv4 from router
181 TrafficSelector fromSelector = DefaultTrafficSelector.builder()
182 .matchInPort(controlPlanePort)
183 .matchEthSrc(intf.mac())
184 .matchVlanId(intf.vlan())
185 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
186 .matchIPSrc(ip.ipAddress().toIpPrefix())
187 .build();
188
Jonathan Hart6344f572015-12-15 08:26:25 -0800189 flowObjectiveService.forward(deviceId,
Saurav Das49cb5a12016-01-16 22:54:07 -0800190 buildForwardingObjective(fromSelector, null, intfNextId, true));
Jonathan Hart6344f572015-12-15 08:26:25 -0800191
192 // ARP to router
193 toSelector = DefaultTrafficSelector.builder()
194 .matchInPort(intf.connectPoint().port())
195 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
196 .matchVlanId(intf.vlan())
197 .build();
198
Saurav Das49cb5a12016-01-16 22:54:07 -0800199 TrafficTreatment puntTreatment = DefaultTrafficTreatment.builder()
Jonathan Hart6344f572015-12-15 08:26:25 -0800200 .punt()
201 .build();
202
203 flowObjectiveService.forward(deviceId,
Saurav Das49cb5a12016-01-16 22:54:07 -0800204 buildForwardingObjective(toSelector, puntTreatment, cpNextId, true));
Jonathan Hart6344f572015-12-15 08:26:25 -0800205
206 // ARP from router
207 fromSelector = DefaultTrafficSelector.builder()
208 .matchInPort(controlPlanePort)
209 .matchEthSrc(intf.mac())
210 .matchVlanId(intf.vlan())
211 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
Saurav Das49cb5a12016-01-16 22:54:07 -0800212 .matchArpSpa(ip.ipAddress().getIp4Address())
Jonathan Hart6344f572015-12-15 08:26:25 -0800213 .build();
214
215 flowObjectiveService.forward(deviceId,
Saurav Das49cb5a12016-01-16 22:54:07 -0800216 buildForwardingObjective(fromSelector, puntTreatment, intfNextId, true));
Jonathan Hart6344f572015-12-15 08:26:25 -0800217 }
218 }
219
Jonathan Hartea492382016-01-13 09:33:13 -0800220 private void updateOspfForwarding(Interface intf) {
221 // OSPF to router
222 TrafficSelector toSelector = DefaultTrafficSelector.builder()
223 .matchInPort(intf.connectPoint().port())
224 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
225 .matchVlanId(intf.vlan())
226 .matchIPProtocol((byte) OSPF_IP_PROTO)
227 .build();
Jonathan Hart6344f572015-12-15 08:26:25 -0800228
Saurav Das49cb5a12016-01-16 22:54:07 -0800229 // create nextObjectives for forwarding to the controlPlaneConnectPoint
230 DeviceId deviceId = controlPlaneConnectPoint.deviceId();
231 PortNumber controlPlanePort = controlPlaneConnectPoint.port();
232 int cpNextId;
233 if (intf.vlan() == VlanId.NONE) {
234 cpNextId = createNextObjective(deviceId, controlPlanePort,
235 VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
236 true);
237 } else {
238 cpNextId = createNextObjective(deviceId, controlPlanePort,
239 intf.vlan(), false);
240 }
241 log.debug("ospf flows intf:{} nextid:{}", intf, cpNextId);
Jonathan Hartea492382016-01-13 09:33:13 -0800242 flowObjectiveService.forward(controlPlaneConnectPoint.deviceId(),
Saurav Das49cb5a12016-01-16 22:54:07 -0800243 buildForwardingObjective(toSelector, null, cpNextId, ospfEnabled));
Jonathan Hartea492382016-01-13 09:33:13 -0800244 }
245
246 /**
Saurav Das49cb5a12016-01-16 22:54:07 -0800247 * Creates a next objective for forwarding to a port. Handles metadata for
248 * some pipelines that require vlan information for egress port.
249 *
250 * @param deviceId the device on which the next objective is being created
251 * @param portNumber the egress port
252 * @param vlanId vlan information for egress port
253 * @param popVlan if vlan tag should be popped or not
254 * @return nextId of the next objective created
255 */
256 private int createNextObjective(DeviceId deviceId, PortNumber portNumber,
257 VlanId vlanId, boolean popVlan) {
258 int nextId = flowObjectiveService.allocateNextId();
259 NextObjective.Builder nextObjBuilder = DefaultNextObjective
260 .builder().withId(nextId)
261 .withType(NextObjective.Type.SIMPLE)
262 .fromApp(appId);
263
264 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
265 if (popVlan) {
266 ttBuilder.popVlan();
267 }
268 ttBuilder.setOutput(portNumber);
269
270 // setup metadata to pass to nextObjective - indicate the vlan on egress
271 // if needed by the switch pipeline.
272 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
273 metabuilder.matchVlanId(vlanId);
274
275 nextObjBuilder.withMeta(metabuilder.build());
276 nextObjBuilder.addTreatment(ttBuilder.build());
277 log.debug("Submited next objective {} in device {} for port/vlan {}/{}",
278 nextId, deviceId, portNumber, vlanId);
279 flowObjectiveService.next(deviceId, nextObjBuilder.add());
280 return nextId;
281 }
282
283 /**
284 * Builds a forwarding objective from the given selector, treatment and nextId.
Jonathan Hartea492382016-01-13 09:33:13 -0800285 *
286 * @param selector selector
Saurav Das49cb5a12016-01-16 22:54:07 -0800287 * @param treatment treatment to apply to packet, can be null
288 * @param nextId next objective to point to for forwarding packet
Jonathan Hartea492382016-01-13 09:33:13 -0800289 * @param add true to create an add objective, false to create a remove
290 * objective
291 * @return forwarding objective
292 */
293 private ForwardingObjective buildForwardingObjective(TrafficSelector selector,
294 TrafficTreatment treatment,
Saurav Das49cb5a12016-01-16 22:54:07 -0800295 int nextId,
Jonathan Hartea492382016-01-13 09:33:13 -0800296 boolean add) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800297 DefaultForwardingObjective.Builder fobBuilder = DefaultForwardingObjective.builder();
298 fobBuilder.withSelector(selector);
299 if (treatment != null) {
300 fobBuilder.withTreatment(treatment);
301 }
302 if (nextId != -1) {
303 fobBuilder.nextStep(nextId);
304 }
305 fobBuilder.fromApp(appId)
306 .withPriority(PRIORITY)
307 .withFlag(ForwardingObjective.Flag.VERSATILE);
Jonathan Hartea492382016-01-13 09:33:13 -0800308
309 return add ? fobBuilder.add() : fobBuilder.remove();
Jonathan Hart6344f572015-12-15 08:26:25 -0800310 }
311
Jonathan Hartea492382016-01-13 09:33:13 -0800312 /**
313 * Listener for device events.
314 */
Jonathan Hart6344f572015-12-15 08:26:25 -0800315 private class InternalDeviceListener implements DeviceListener {
316 @Override
317 public void event(DeviceEvent event) {
318 if (controlPlaneConnectPoint != null &&
319 event.subject().id().equals(controlPlaneConnectPoint.deviceId())) {
320 switch (event.type()) {
321 case DEVICE_ADDED:
322 case DEVICE_AVAILABILITY_CHANGED:
323 if (deviceService.isAvailable(event.subject().id())) {
324 log.info("Device connected {}", event.subject().id());
Jonathan Hartea492382016-01-13 09:33:13 -0800325 updateDevice();
Jonathan Hart6344f572015-12-15 08:26:25 -0800326 }
327
328 break;
329 case DEVICE_UPDATED:
330 case DEVICE_REMOVED:
331 case DEVICE_SUSPENDED:
332 case PORT_ADDED:
333 case PORT_UPDATED:
334 case PORT_REMOVED:
335 default:
336 break;
337 }
338 }
339 }
340 }
341
Jonathan Hartea492382016-01-13 09:33:13 -0800342 /**
343 * Listener for network config events.
344 */
Jonathan Hart6344f572015-12-15 08:26:25 -0800345 private class InternalNetworkConfigListener implements NetworkConfigListener {
346 @Override
347 public void event(NetworkConfigEvent event) {
Jonathan Hartea492382016-01-13 09:33:13 -0800348 if (event.configClass().equals(RoutingService.ROUTER_CONFIG_CLASS)) {
Jonathan Hart6344f572015-12-15 08:26:25 -0800349 switch (event.type()) {
350 case CONFIG_ADDED:
351 case CONFIG_UPDATED:
352 updateConfig();
353 break;
354 case CONFIG_REGISTERED:
355 case CONFIG_UNREGISTERED:
356 case CONFIG_REMOVED:
357 default:
358 break;
359 }
360 }
361 }
362 }
363}