blob: c5d5f2996e06120a87ff98a363a039ce4e9e8d68 [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
2 * Copyright 2016-present 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 */
16package org.onosproject.openstacknetworking.impl;
17
18import com.google.common.base.Strings;
19import com.google.common.collect.ImmutableSet;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.onlab.packet.Ethernet;
26import org.onlab.packet.IPv4;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.IpPrefix;
29import org.onosproject.cluster.ClusterService;
30import org.onosproject.cluster.LeadershipService;
31import org.onosproject.cluster.NodeId;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
34import org.onosproject.core.GroupId;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.PortNumber;
37import org.onosproject.net.flow.DefaultTrafficSelector;
38import org.onosproject.net.flow.DefaultTrafficTreatment;
39import org.onosproject.net.flow.TrafficSelector;
40import org.onosproject.net.flow.TrafficTreatment;
41import org.onosproject.net.flowobjective.FlowObjectiveService;
42import org.onosproject.net.flowobjective.ForwardingObjective;
43import org.onosproject.openstacknetworking.api.Constants;
44import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
45import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
46import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
47import org.onosproject.openstacknetworking.api.OpenstackRouterService;
48import org.onosproject.openstacknode.OpenstackNode;
49import org.onosproject.openstacknode.OpenstackNodeEvent;
50import org.onosproject.openstacknode.OpenstackNodeListener;
51import org.onosproject.openstacknode.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090052import org.openstack4j.model.network.ExternalGateway;
53import org.openstack4j.model.network.Network;
54import org.openstack4j.model.network.Router;
55import org.openstack4j.model.network.RouterInterface;
56import org.openstack4j.model.network.Subnet;
57import org.slf4j.Logger;
58import org.slf4j.LoggerFactory;
59
60import java.util.Objects;
61import java.util.Set;
62import java.util.concurrent.ExecutorService;
63import java.util.stream.Collectors;
64
65import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
66import static org.onlab.util.Tools.groupedThreads;
67import static org.onosproject.openstacknetworking.api.Constants.*;
68import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.COMPUTE;
69
70/**
71 * Handles OpenStack router events.
72 */
73@Component(immediate = true)
74public class OpenstackRoutingHandler {
75
76 private final Logger log = LoggerFactory.getLogger(getClass());
77
78 private static final String MSG_ENABLED = "Enabled ";
79 private static final String MSG_DISABLED = "Disabled ";
80 private static final String ERR_SET_FLOWS = "Failed to set flows for router %s:";
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected CoreService coreService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected LeadershipService leadershipService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected ClusterService clusterService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected FlowObjectiveService flowObjectiveService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected OpenstackNodeService osNodeService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +090098 protected OpenstackNetworkService osNetworkService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected OpenstackRouterService osRouterService;
102
103 private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
104 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
105 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
106 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
107
108 private ApplicationId appId;
109 private NodeId localNodeId;
110
111 @Activate
112 protected void activate() {
113 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
114 localNodeId = clusterService.getLocalNode().id();
115 leadershipService.runForLeadership(appId.name());
116 osNodeService.addListener(osNodeListener);
117 osRouterService.addListener(osRouterListener);
118
119 log.info("Started");
120 }
121
122 @Deactivate
123 protected void deactivate() {
124 osRouterService.removeListener(osRouterListener);
125 osNodeService.removeListener(osNodeListener);
126 leadershipService.withdraw(appId.name());
127 eventExecutor.shutdown();
128
129 log.info("Stopped");
130 }
131
132 private void routerUpdated(Router osRouter) {
133 ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
134 if (exGateway == null) {
135 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
136 setSourceNat(iface, false);
137 });
138 } else {
139 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
140 setSourceNat(iface, exGateway.isEnableSnat());
141 });
142 }
143 }
144
145 private void routerIfaceAdded(Router osRouter, RouterInterface osRouterIface) {
146 Subnet osSubnet = osNetworkService.subnet(osRouterIface.getSubnetId());
147 if (osSubnet == null) {
148 final String error = String.format(
149 ERR_SET_FLOWS + "subnet %s does not exist",
150 osRouterIface.getId(),
151 osRouterIface.getSubnetId());
152 throw new IllegalStateException(error);
153 }
154
155 setInternalRoutes(osRouter, osSubnet, true);
156 setGatewayIcmp(osSubnet, true);
157 ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
158 if (exGateway != null && exGateway.isEnableSnat()) {
159 setSourceNat(osRouterIface, true);
160 }
161
162 log.info("Connected subnet({}) to {}", osSubnet.getCidr(), osRouter.getName());
163 }
164
165 private void routerIfaceRemoved(Router osRouter, RouterInterface osRouterIface) {
166 Subnet osSubnet = osNetworkService.subnet(osRouterIface.getSubnetId());
167 if (osSubnet == null) {
168 final String error = String.format(
169 ERR_SET_FLOWS + "subnet %s does not exist",
170 osRouterIface.getId(),
171 osRouterIface.getSubnetId());
172 throw new IllegalStateException(error);
173 }
174
175 setInternalRoutes(osRouter, osSubnet, false);
176 setGatewayIcmp(osSubnet, false);
177 ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
178 if (exGateway != null && exGateway.isEnableSnat()) {
179 setSourceNat(osRouterIface, false);
180 }
181
182 log.info("Disconnected subnet({}) from {}", osSubnet.getCidr(), osRouter.getName());
183 }
184
185 private void setSourceNat(RouterInterface routerIface, boolean install) {
186 Subnet osSubnet = osNetworkService.subnet(routerIface.getSubnetId());
187 Network osNet = osNetworkService.network(osSubnet.getNetworkId());
188
189 osNodeService.completeNodes().stream()
190 .filter(osNode -> osNode.type() == COMPUTE)
191 .forEach(osNode -> {
192 setRulesToGateway(
193 osNode.intBridge(),
daniel parke49eb382017-04-05 16:48:28 +0900194 osNodeService.gatewayGroupId(osNode.intBridge()),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900195 Long.valueOf(osNet.getProviderSegID()),
196 IpPrefix.valueOf(osSubnet.getCidr()),
197 install);
198 });
199
200 // take the first outgoing packet to controller for source NAT
daniel parke49eb382017-04-05 16:48:28 +0900201 osNodeService.gatewayDeviceIds()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900202 .forEach(gwDeviceId -> setRulesToController(
203 gwDeviceId,
204 Long.valueOf(osNet.getProviderSegID()),
205 IpPrefix.valueOf(osSubnet.getCidr()),
206 install));
207
208 final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
209 log.info(updateStr + "external access for subnet({})", osSubnet.getCidr());
210 }
211
212 private void setGatewayIcmp(Subnet osSubnet, boolean install) {
213 if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
214 // do nothing if no gateway is set
215 return;
216 }
217
218 // take ICMP request to a subnet gateway through gateway node group
219 Network network = osNetworkService.network(osSubnet.getNetworkId());
220 osNodeService.completeNodes().stream()
221 .filter(osNode -> osNode.type() == COMPUTE)
222 .forEach(osNode -> setRulesToGatewayWithDstIp(
223 osNode.intBridge(),
daniel parke49eb382017-04-05 16:48:28 +0900224 osNodeService.gatewayGroupId(osNode.intBridge()),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900225 Long.valueOf(network.getProviderSegID()),
226 IpAddress.valueOf(osSubnet.getGateway()),
227 install));
228
229 IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
daniel parke49eb382017-04-05 16:48:28 +0900230 osNodeService.gatewayDeviceIds()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900231 .forEach(gwDeviceId -> setGatewayIcmpRule(
232 gatewayIp,
233 gwDeviceId,
234 install
235 ));
236
237 final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
238 log.debug(updateStr + "ICMP to {}", osSubnet.getGateway());
239 }
240
241 private void setInternalRoutes(Router osRouter, Subnet updatedSubnet, boolean install) {
242 Set<Subnet> routableSubnets = routableSubnets(osRouter, updatedSubnet.getId());
243 Long updatedVni = getVni(updatedSubnet);
244
245 // installs rule from/to my subnet intentionally to fix ICMP failure
246 // to my subnet gateway if no external gateway added to the router
247 osNodeService.completeNodes().stream()
248 .filter(osNode -> osNode.type() == COMPUTE)
249 .forEach(osNode -> {
250 setInternalRouterRules(
251 osNode.intBridge(),
252 updatedVni,
253 updatedVni,
254 IpPrefix.valueOf(updatedSubnet.getCidr()),
255 IpPrefix.valueOf(updatedSubnet.getCidr()),
256 install
257 );
258
259 routableSubnets.forEach(subnet -> {
260 setInternalRouterRules(
261 osNode.intBridge(),
262 updatedVni,
263 getVni(subnet),
264 IpPrefix.valueOf(updatedSubnet.getCidr()),
265 IpPrefix.valueOf(subnet.getCidr()),
266 install
267 );
268 setInternalRouterRules(
269 osNode.intBridge(),
270 getVni(subnet),
271 updatedVni,
272 IpPrefix.valueOf(subnet.getCidr()),
273 IpPrefix.valueOf(updatedSubnet.getCidr()),
274 install
275 );
276 });
277 });
278
279 final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
280 routableSubnets.forEach(subnet -> log.debug(
281 updateStr + "route between subnet:{} and subnet:{}",
282 subnet.getCidr(),
283 updatedSubnet.getCidr()));
284 }
285
286 private Set<Subnet> routableSubnets(Router osRouter, String osSubnetId) {
287 Set<Subnet> osSubnets = osRouterService.routerInterfaces(osRouter.getId())
288 .stream()
289 .filter(iface -> !Objects.equals(iface.getSubnetId(), osSubnetId))
290 .map(iface -> osNetworkService.subnet(iface.getSubnetId()))
291 .collect(Collectors.toSet());
292 return ImmutableSet.copyOf(osSubnets);
293 }
294
295 private Long getVni(Subnet osSubnet) {
296 return Long.parseLong(osNetworkService.network(
297 osSubnet.getNetworkId()).getProviderSegID());
298 }
299
300 private void setGatewayIcmpRule(IpAddress gatewayIp, DeviceId deviceId, boolean install) {
301 TrafficSelector selector = DefaultTrafficSelector.builder()
302 .matchEthType(Ethernet.TYPE_IPV4)
303 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
304 .matchIPDst(gatewayIp.toIpPrefix())
305 .build();
306
307 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
308 .setOutput(PortNumber.CONTROLLER)
309 .build();
310
311 RulePopulatorUtil.setRule(
312 flowObjectiveService,
313 appId,
314 deviceId,
315 selector,
316 treatment,
317 ForwardingObjective.Flag.VERSATILE,
318 PRIORITY_ICMP_RULE,
319 install);
320 }
321
322 private void setInternalRouterRules(DeviceId deviceId, Long srcVni, Long dstVni,
323 IpPrefix srcSubnet, IpPrefix dstSubnet, boolean install) {
324 TrafficSelector selector = DefaultTrafficSelector.builder()
325 .matchEthType(Ethernet.TYPE_IPV4)
326 .matchTunnelId(srcVni)
327 .matchIPSrc(srcSubnet)
328 .matchIPDst(dstSubnet)
329 .build();
330
331 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
332 .setTunnelId(dstVni)
333 .build();
334
335 RulePopulatorUtil.setRule(
336 flowObjectiveService,
337 appId,
338 deviceId,
339 selector,
340 treatment,
341 ForwardingObjective.Flag.SPECIFIC,
342 PRIORITY_INTERNAL_ROUTING_RULE,
343 install);
344
345 selector = DefaultTrafficSelector.builder()
346 .matchEthType(Ethernet.TYPE_IPV4)
347 .matchTunnelId(dstVni)
348 .matchIPSrc(srcSubnet)
349 .matchIPDst(dstSubnet)
350 .build();
351
352 treatment = DefaultTrafficTreatment.builder()
353 .setTunnelId(dstVni)
354 .build();
355
356 RulePopulatorUtil.setRule(
357 flowObjectiveService,
358 appId,
359 deviceId,
360 selector,
361 treatment,
362 ForwardingObjective.Flag.SPECIFIC,
363 PRIORITY_INTERNAL_ROUTING_RULE,
364 install);
365 }
366
367 private void setRulesToGateway(DeviceId deviceId, GroupId groupId, Long vni,
368 IpPrefix srcSubnet, boolean install) {
369 TrafficSelector selector = DefaultTrafficSelector.builder()
370 .matchEthType(Ethernet.TYPE_IPV4)
371 .matchTunnelId(vni)
372 .matchIPSrc(srcSubnet)
373 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC)
374 .build();
375
376 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
377 .group(groupId)
378 .build();
379
380 RulePopulatorUtil.setRule(
381 flowObjectiveService,
382 appId,
383 deviceId,
384 selector,
385 treatment,
386 ForwardingObjective.Flag.SPECIFIC,
387 PRIORITY_EXTERNAL_ROUTING_RULE,
388 install);
389 }
390
391 private void setRulesToController(DeviceId deviceId, Long vni, IpPrefix srcSubnet, boolean install) {
392 TrafficSelector selector = DefaultTrafficSelector.builder()
393 .matchEthType(Ethernet.TYPE_IPV4)
394 .matchTunnelId(vni)
395 .matchIPSrc(srcSubnet)
396 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC)
397 .build();
398
399 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
400 .setOutput(PortNumber.CONTROLLER)
401 .build();
402
403 RulePopulatorUtil.setRule(
404 flowObjectiveService,
405 appId,
406 deviceId,
407 selector,
408 treatment,
409 ForwardingObjective.Flag.VERSATILE,
410 PRIORITY_EXTERNAL_ROUTING_RULE,
411 install);
412 }
413
414 private void setRulesToGatewayWithDstIp(DeviceId deviceId, GroupId groupId, Long vni,
415 IpAddress dstIp, boolean install) {
416 TrafficSelector selector = DefaultTrafficSelector.builder()
417 .matchEthType(Ethernet.TYPE_IPV4)
418 .matchTunnelId(vni)
419 .matchIPDst(dstIp.toIpPrefix())
420 .build();
421
422 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
423 .group(groupId)
424 .build();
425
426 RulePopulatorUtil.setRule(
427 flowObjectiveService,
428 appId,
429 deviceId,
430 selector,
431 treatment,
432 ForwardingObjective.Flag.SPECIFIC,
433 PRIORITY_SWITCHING_RULE,
434 install);
435 }
436
437 private class InternalRouterEventListener implements OpenstackRouterListener {
438
439 @Override
440 public boolean isRelevant(OpenstackRouterEvent event) {
441 // do not allow to proceed without leadership
442 NodeId leader = leadershipService.getLeader(appId.name());
443 return Objects.equals(localNodeId, leader);
444 }
445
446 // FIXME only one leader in the cluster should process
447 @Override
448 public void event(OpenstackRouterEvent event) {
449 switch (event.type()) {
450 case OPENSTACK_ROUTER_CREATED:
451 log.debug("Router(name:{}, ID:{}) is created",
452 event.subject().getName(),
453 event.subject().getId());
454 eventExecutor.execute(() -> routerUpdated(event.subject()));
455 break;
456 case OPENSTACK_ROUTER_UPDATED:
457 log.debug("Router(name:{}, ID:{}) is updated",
458 event.subject().getName(),
459 event.subject().getId());
460 eventExecutor.execute(() -> routerUpdated(event.subject()));
461 break;
462 case OPENSTACK_ROUTER_REMOVED:
463 log.debug("Router(name:{}, ID:{}) is removed",
464 event.subject().getName(),
465 event.subject().getId());
466 break;
467 case OPENSTACK_ROUTER_INTERFACE_ADDED:
468 log.debug("Router interface {} added to router {}",
469 event.routerIface().getPortId(),
470 event.routerIface().getId());
471 eventExecutor.execute(() -> routerIfaceAdded(
472 event.subject(),
473 event.routerIface()));
474 break;
475 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
476 log.debug("Router interface {} on {} updated",
477 event.routerIface().getPortId(),
478 event.routerIface().getId());
479 break;
480 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
481 log.debug("Router interface {} removed from router {}",
482 event.routerIface().getPortId(),
483 event.routerIface().getId());
484 eventExecutor.execute(() -> routerIfaceRemoved(
485 event.subject(),
486 event.routerIface()));
487 break;
488 case OPENSTACK_ROUTER_GATEWAY_ADDED:
489 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
490 case OPENSTACK_FLOATING_IP_CREATED:
491 case OPENSTACK_FLOATING_IP_UPDATED:
492 case OPENSTACK_FLOATING_IP_REMOVED:
493 case OPENSTACK_FLOATING_IP_ASSOCIATED:
494 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
495 default:
496 // do nothing for the other events
497 break;
498 }
499 }
500 }
501
502 private class InternalNodeEventListener implements OpenstackNodeListener {
503
504 @Override
505 public boolean isRelevant(OpenstackNodeEvent event) {
506 // do not allow to proceed without leadership
507 NodeId leader = leadershipService.getLeader(appId.name());
508 return Objects.equals(localNodeId, leader);
509 }
510
511 @Override
512 public void event(OpenstackNodeEvent event) {
513 OpenstackNode osNode = event.subject();
514
515 switch (event.type()) {
516 case COMPLETE:
517 case INCOMPLETE:
518 eventExecutor.execute(() -> {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900519 log.info("Reconfigure routers for {}", osNode.hostname());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900520 reconfigureRouters();
521 });
522 break;
523 case INIT:
524 case DEVICE_CREATED:
525 default:
526 break;
527 }
528 }
529
530 private void reconfigureRouters() {
531 osRouterService.routers().forEach(osRouter -> {
532 routerUpdated(osRouter);
533 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
534 routerIfaceAdded(osRouter, iface);
535 });
536 });
537 }
538 }
539}