blob: 1ff77a884d6c27dbfe90358bba95baef05fbea16 [file] [log] [blame]
Jian Li25257212019-03-26 13:31:14 +09001/*
2 * Copyright 2019-present Open Networking Foundation
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.collect.ImmutableSet;
19import org.onlab.packet.Ethernet;
20import org.onlab.packet.IPv4;
21import org.onlab.packet.IpAddress;
22import org.onlab.packet.VlanId;
23import org.onosproject.cfg.ComponentConfigService;
24import org.onosproject.cfg.ConfigProperty;
25import org.onosproject.cluster.ClusterService;
26import org.onosproject.cluster.LeadershipService;
27import org.onosproject.cluster.NodeId;
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.mastership.MastershipService;
31import org.onosproject.net.Device;
32import org.onosproject.net.PortNumber;
33import org.onosproject.net.device.DeviceService;
34import org.onosproject.net.driver.DriverService;
35import org.onosproject.net.flow.DefaultTrafficSelector;
36import org.onosproject.net.flow.DefaultTrafficTreatment;
37import org.onosproject.net.flow.TrafficSelector;
38import org.onosproject.net.flow.TrafficTreatment;
39import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
40import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type;
41import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
42import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
43import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
44import org.onosproject.openstacknetworking.api.OpenstackRouterService;
45import org.onosproject.openstacknode.api.OpenstackNode;
46import org.onosproject.openstacknode.api.OpenstackNodeEvent;
47import org.onosproject.openstacknode.api.OpenstackNodeListener;
48import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Lia2995192019-04-02 14:13:04 +090049import org.openstack4j.model.network.Network;
Jian Li25257212019-03-26 13:31:14 +090050import org.openstack4j.model.network.Router;
51import org.openstack4j.model.network.RouterInterface;
52import org.openstack4j.model.network.Subnet;
53import org.osgi.service.component.annotations.Activate;
54import org.osgi.service.component.annotations.Component;
55import org.osgi.service.component.annotations.Deactivate;
56import org.osgi.service.component.annotations.Reference;
57import org.osgi.service.component.annotations.ReferenceCardinality;
58import org.slf4j.Logger;
59
60import java.util.Objects;
Jian Lia2995192019-04-02 14:13:04 +090061import java.util.Optional;
Jian Li25257212019-03-26 13:31:14 +090062import java.util.Set;
63import java.util.concurrent.ExecutorService;
64import java.util.stream.Collectors;
65
66import static java.util.concurrent.Executors.newSingleThreadExecutor;
67import static org.onlab.packet.ICMP.CODE_ECHO_REQEUST;
68import static org.onlab.packet.ICMP.TYPE_ECHO_REPLY;
69import static org.onlab.packet.ICMP.TYPE_ECHO_REQUEST;
70import static org.onlab.util.Tools.groupedThreads;
71import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC;
72import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
73import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ICMP_RULE;
74import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
75import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.USE_STATEFUL_SNAT;
Jian Lia2995192019-04-02 14:13:04 +090076import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.FLAT;
77import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getExternalIp;
Jian Li25257212019-03-26 13:31:14 +090078import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValueAsBoolean;
79import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.NXM_NX_IP_TTL;
80import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.NXM_OF_ICMP_TYPE;
81import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildLoadExtension;
82import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildMoveEthSrcToDstExtension;
83import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildMoveIpSrcToDstExtension;
84import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
85import static org.slf4j.LoggerFactory.getLogger;
86
87/**
88 * Populates the ICMP flow rules for providing connectivity with gateways.
89 */
90@Component(immediate = true)
91public class OpenstackSwitchingIcmpHandler {
92
93 private final Logger log = getLogger(getClass());
94
95 private static final int DEFAULT_TTL = 0xff;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY)
98 protected CoreService coreService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY)
101 protected MastershipService mastershipService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
104 protected DeviceService deviceService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
107 protected DriverService driverService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
110 protected ClusterService clusterService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
113 protected ComponentConfigService configService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
116 protected LeadershipService leadershipService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
119 protected OpenstackNodeService osNodeService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
122 protected OpenstackNetworkService osNetworkService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
125 protected OpenstackFlowRuleService osFlowRuleService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
128 protected OpenstackRouterService osRouterService;
129
130 private final ExecutorService eventExecutor = newSingleThreadExecutor(
131 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
132 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
133 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
134
135 private ApplicationId appId;
136 private NodeId localNodeId;
137
138 @Activate
139 protected void activate() {
140 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
141 localNodeId = clusterService.getLocalNode().id();
142 osRouterService.addListener(osRouterListener);
143 osNodeService.addListener(osNodeListener);
144
145 log.info("Started");
146 }
147
148 @Deactivate
149 protected void deactivate() {
150 osRouterService.removeListener(osRouterListener);
151 osNodeService.removeListener(osNodeListener);
152 eventExecutor.shutdown();
153
154 log.info("Stopped");
155 }
156
157 private boolean getStatefulSnatFlag() {
158 Set<ConfigProperty> properties =
159 configService.getProperties(OpenstackRoutingSnatHandler.class.getName());
160 return getPropertyValueAsBoolean(properties, USE_STATEFUL_SNAT);
161 }
162
163 private void processRouterIntfEvent(Router osRouter, RouterInterface routerIface, boolean install) {
164 if (!getStatefulSnatFlag()) {
165 return;
166 }
167
168 Subnet osSubnet = osNetworkService.subnet(routerIface.getSubnetId());
169 Type netType = osNetworkService.networkType(osSubnet.getNetworkId());
170 String segId = osNetworkService.segmentId(osSubnet.getNetworkId());
171 IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
172 Set<Subnet> routableSubnets = routableSubnets(osRouter, osSubnet.getId());
173
174 osNodeService.completeNodes(COMPUTE).stream()
175 .filter(cNode -> cNode.dataIp() != null)
Jian Lia2995192019-04-02 14:13:04 +0900176 .forEach(cNode -> {
177 setRoutableSubnetsIcmpRules(cNode, segId, osSubnet,
178 routableSubnets, gatewayIp, netType, install);
179 setExtGatewayIcmpReplyRules(cNode, routerIface,
180 netType, install);
181 });
182 }
183
184 private void setExtGatewayIcmpReplyRules(OpenstackNode osNode,
185 RouterInterface routerIface,
186 Type networkType, boolean install) {
187
188 if (networkType == FLAT) {
189 return;
190 }
191
192 Optional<Router> osRouter = osRouterService.routers().stream()
193 .filter(router -> osRouterService.routerInterfaces(routerIface.getId()) != null)
194 .findAny();
195
196 if (!osRouter.isPresent()) {
197 log.error("Cannot find a router for router interface {} ", routerIface);
198 return;
199 }
200
201 IpAddress natAddress = getExternalIp(osRouter.get(), osNetworkService);
202 if (natAddress == null) {
203 return;
204 }
205
206 setGatewayIcmpReplyRule(osNode, null, natAddress, networkType, install);
Jian Li25257212019-03-26 13:31:14 +0900207 }
208
209 private void setRoutableSubnetsIcmpRules(OpenstackNode osNode,
210 String segmentId,
Jian Lia2995192019-04-02 14:13:04 +0900211 Subnet updatedSubnet,
Jian Li25257212019-03-26 13:31:14 +0900212 Set<Subnet> routableSubnets,
213 IpAddress gatewayIp,
214 Type networkType,
215 boolean install) {
216 setGatewayIcmpReplyRule(osNode, segmentId, gatewayIp, networkType, install);
217
218 routableSubnets.forEach(subnet -> {
219 setGatewayIcmpReplyRule(osNode, segmentId,
220 IpAddress.valueOf(subnet.getGateway()), networkType, install);
Jian Lia2995192019-04-02 14:13:04 +0900221
222 Network network = osNetworkService.network(subnet.getNetworkId());
223
224 setGatewayIcmpReplyRule(osNode, network.getProviderSegID(),
225 IpAddress.valueOf(updatedSubnet.getGateway()), networkType, install);
Jian Li25257212019-03-26 13:31:14 +0900226 });
227 }
228
229 private Set<Subnet> routableSubnets(Router osRouter, String osSubnetId) {
230 Set<Subnet> osSubnets = osRouterService.routerInterfaces(osRouter.getId())
231 .stream()
232 .filter(iface -> !Objects.equals(iface.getSubnetId(), osSubnetId))
233 .map(iface -> osNetworkService.subnet(iface.getSubnetId()))
234 .collect(Collectors.toSet());
235 return ImmutableSet.copyOf(osSubnets);
236 }
237
238 private void setGatewayIcmpReplyRule(OpenstackNode osNode,
239 String segmentId,
240 IpAddress gatewayIp,
241 Type networkType,
242 boolean install) {
243 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
244 .matchEthType(Ethernet.TYPE_IPV4)
245 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
246 .matchIcmpType(TYPE_ECHO_REQUEST)
247 .matchIcmpCode(CODE_ECHO_REQEUST)
248 .matchIPDst(gatewayIp.getIp4Address().toIpPrefix());
249
Jian Lia2995192019-04-02 14:13:04 +0900250 if (segmentId != null) {
251 switch (networkType) {
252 case VXLAN:
253 case GRE:
254 case GENEVE:
255 sBuilder.matchTunnelId(Long.parseLong(segmentId));
256 break;
257 case VLAN:
258 sBuilder.matchVlanId(VlanId.vlanId(segmentId));
259 break;
260 default:
261 break;
262 }
Jian Li25257212019-03-26 13:31:14 +0900263 }
264
265 Device device = deviceService.getDevice(osNode.intgBridge());
266 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
267 .extension(buildMoveEthSrcToDstExtension(device), device.id())
268 .extension(buildMoveIpSrcToDstExtension(device), device.id())
269 .extension(buildLoadExtension(device, NXM_NX_IP_TTL, DEFAULT_TTL), device.id())
270 .extension(buildLoadExtension(device, NXM_OF_ICMP_TYPE, TYPE_ECHO_REPLY), device.id())
271 .setIpSrc(gatewayIp)
272 .setEthSrc(DEFAULT_GATEWAY_MAC)
273 .setOutput(PortNumber.IN_PORT);
274
275 osFlowRuleService.setRule(
276 appId,
277 osNode.intgBridge(),
278 sBuilder.build(),
279 tBuilder.build(),
280 PRIORITY_ICMP_RULE,
281 ROUTING_TABLE,
282 install);
283 }
284
285 private class InternalRouterEventListener implements OpenstackRouterListener {
286 private boolean isRelevantHelper() {
287 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
288 }
289
290 @Override
291 public void event(OpenstackRouterEvent event) {
292 switch (event.type()) {
293 case OPENSTACK_ROUTER_INTERFACE_ADDED:
294 eventExecutor.execute(() -> processRouterIntfCreation(event));
295 break;
296 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
297 eventExecutor.execute(() -> processRouterIntfRemoval(event));
298 break;
299 default:
300 // do nothing for the other events
301 break;
302 }
303 }
304
305 private void processRouterIntfCreation(OpenstackRouterEvent event) {
306 if (!isRelevantHelper()) {
307 return;
308 }
309
310 log.debug("Router interface {} added to router {}",
311 event.routerIface().getPortId(),
312 event.routerIface().getId());
313
314 processRouterIntfEvent(event.subject(), event.routerIface(), true);
315 }
316
317 private void processRouterIntfRemoval(OpenstackRouterEvent event) {
318 if (!isRelevantHelper()) {
319 return;
320 }
321
322 log.debug("Router interface {} removed from router {}",
323 event.routerIface().getPortId(),
324 event.routerIface().getId());
325
326 processRouterIntfEvent(event.subject(), event.routerIface(), false);
327 }
328 }
329
330 private class InternalNodeEventListener implements OpenstackNodeListener {
331
332 private boolean isRelevantHelper() {
333 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
334 }
335
336 @Override
337 public void event(OpenstackNodeEvent event) {
338 OpenstackNode osNode = event.subject();
339 switch (event.type()) {
340 case OPENSTACK_NODE_COMPLETE:
341 case OPENSTACK_NODE_INCOMPLETE:
342 case OPENSTACK_NODE_UPDATED:
343 case OPENSTACK_NODE_REMOVED:
344 eventExecutor.execute(() -> {
345 if (!isRelevantHelper()) {
346 return;
347 }
348 reconfigureRouters(osNode);
349 });
350 break;
351 default:
352 break;
353 }
354 }
355
356 private void reconfigureRouters(OpenstackNode osNode) {
357 osRouterService.routers().forEach(osRouter -> {
358 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
359 processRouterIntfEvent(osRouter, iface, true);
360 });
361 });
362 log.info("Reconfigure routers for {}", osNode.hostname());
363 }
364 }
365}