blob: f42b07364698a0bdc3a046a613b00fc4e956ac4f [file] [log] [blame]
sangho6a9ff0d2017-03-27 11:23:37 +09001/*
Jian Li26949762018-03-30 15:46:37 +09002 * Copyright 2017-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 */
sangho6a9ff0d2017-03-27 11:23:37 +090016
17package org.onosproject.openstacknetworking.impl;
18
19import com.google.common.base.Strings;
sangho2e97be02017-07-03 18:18:27 +090020import com.google.common.collect.Maps;
21import com.google.common.collect.Sets;
sangho6a9ff0d2017-03-27 11:23:37 +090022import org.onlab.packet.Ethernet;
23import org.onlab.packet.IPv4;
24import org.onlab.packet.Ip4Address;
25import org.onlab.packet.Ip4Prefix;
26import org.onlab.packet.IpPrefix;
27import org.onlab.packet.TpPort;
Jian Lie8b28db2018-10-17 14:04:09 +090028import org.onlab.packet.VlanId;
Jian Liac30e272018-10-18 23:08:03 +090029import org.onlab.util.KryoNamespace;
sangho0248ca22017-05-31 13:22:47 +090030import org.onlab.util.Tools;
31import org.onosproject.cfg.ComponentConfigService;
sangho1aaa7882017-05-31 13:22:47 +090032import org.onosproject.cluster.ClusterService;
33import org.onosproject.cluster.LeadershipService;
34import org.onosproject.cluster.NodeId;
sangho6a9ff0d2017-03-27 11:23:37 +090035import org.onosproject.core.ApplicationId;
36import org.onosproject.core.CoreService;
37import org.onosproject.mastership.MastershipService;
sangho1aaa7882017-05-31 13:22:47 +090038import org.onosproject.net.DeviceId;
39import org.onosproject.net.driver.DriverService;
sangho6a9ff0d2017-03-27 11:23:37 +090040import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.TrafficSelector;
sangho1aaa7882017-05-31 13:22:47 +090043import org.onosproject.net.flow.TrafficTreatment;
44import org.onosproject.net.flow.criteria.ExtensionSelector;
Jian Li28ec77f2018-10-31 07:07:25 +090045import org.onosproject.net.flow.instructions.ExtensionTreatment;
sangho6a9ff0d2017-03-27 11:23:37 +090046import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li9d35bd62018-10-13 01:43:24 +090047import org.onosproject.openstacknetworking.api.InstancePortAdminService;
sangho6a9ff0d2017-03-27 11:23:37 +090048import org.onosproject.openstacknetworking.api.InstancePortEvent;
49import org.onosproject.openstacknetworking.api.InstancePortListener;
sanghodc375372017-06-08 10:41:30 +090050import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
sangho6a9ff0d2017-03-27 11:23:37 +090051import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
52import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
53import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
54import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupEvent;
55import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupListener;
56import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
Jian Li26949762018-03-30 15:46:37 +090057import org.onosproject.openstacknetworking.util.RulePopulatorUtil;
sangho1aaa7882017-05-31 13:22:47 +090058import org.onosproject.openstacknode.api.OpenstackNodeEvent;
59import org.onosproject.openstacknode.api.OpenstackNodeListener;
sangho3dd2a8b2017-07-19 15:54:31 +090060import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Liac30e272018-10-18 23:08:03 +090061import org.onosproject.store.serializers.KryoNamespaces;
62import org.onosproject.store.service.ConsistentMap;
63import org.onosproject.store.service.Serializer;
64import org.onosproject.store.service.StorageService;
Jian Li1e9cb732018-11-25 23:17:21 +090065import org.openstack4j.model.network.Network;
66import org.openstack4j.model.network.NetworkType;
sangho6a9ff0d2017-03-27 11:23:37 +090067import org.openstack4j.model.network.Port;
68import org.openstack4j.model.network.SecurityGroup;
69import org.openstack4j.model.network.SecurityGroupRule;
Jian Liac30e272018-10-18 23:08:03 +090070import org.openstack4j.model.network.State;
71import org.openstack4j.openstack.networking.domain.NeutronAllowedAddressPair;
72import org.openstack4j.openstack.networking.domain.NeutronExtraDhcpOptCreate;
73import org.openstack4j.openstack.networking.domain.NeutronIP;
74import org.openstack4j.openstack.networking.domain.NeutronPort;
sangho6a9ff0d2017-03-27 11:23:37 +090075import org.openstack4j.openstack.networking.domain.NeutronSecurityGroupRule;
sangho0248ca22017-05-31 13:22:47 +090076import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070077import org.osgi.service.component.annotations.Activate;
78import org.osgi.service.component.annotations.Component;
79import org.osgi.service.component.annotations.Deactivate;
80import org.osgi.service.component.annotations.Modified;
81import org.osgi.service.component.annotations.Reference;
82import org.osgi.service.component.annotations.ReferenceCardinality;
sangho6a9ff0d2017-03-27 11:23:37 +090083import org.slf4j.Logger;
84
sangho6a9ff0d2017-03-27 11:23:37 +090085import java.util.Collections;
sangho0248ca22017-05-31 13:22:47 +090086import java.util.Dictionary;
Jian Liac30e272018-10-18 23:08:03 +090087import java.util.HashSet;
88import java.util.LinkedHashMap;
sangho2e97be02017-07-03 18:18:27 +090089import java.util.Map;
sangho6a9ff0d2017-03-27 11:23:37 +090090import java.util.Objects;
91import java.util.Set;
92import java.util.concurrent.ExecutorService;
93import java.util.stream.Collectors;
94
95import static java.util.concurrent.Executors.newSingleThreadExecutor;
96import static org.onlab.util.Tools.groupedThreads;
Jian Li1e9cb732018-11-25 23:17:21 +090097import static org.onosproject.openstacknetworking.api.Constants.ACL_EGRESS_TABLE;
98import static org.onosproject.openstacknetworking.api.Constants.ACL_INGRESS_TABLE;
99import static org.onosproject.openstacknetworking.api.Constants.ACL_RECIRC_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900100import static org.onosproject.openstacknetworking.api.Constants.CT_TABLE;
101import static org.onosproject.openstacknetworking.api.Constants.ERROR_TABLE;
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700102import static org.onosproject.openstacknetworking.api.Constants.JUMP_TABLE;
sangho6a9ff0d2017-03-27 11:23:37 +0900103import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Li1e9cb732018-11-25 23:17:21 +0900104import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ACL_INGRESS_RULE;
sangho6a9ff0d2017-03-27 11:23:37 +0900105import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ACL_RULE;
sangho1aaa7882017-05-31 13:22:47 +0900106import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_DROP_RULE;
107import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_HOOK_RULE;
108import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_RULE;
Jian Li4d138702018-11-27 17:25:28 +0900109import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_PRE_REMOVE;
Ray Milkey8e406512018-10-24 15:56:50 -0700110import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.USE_SECURITY_GROUP;
111import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.USE_SECURITY_GROUP_DEFAULT;
Jian Lib8cdcc12018-10-23 01:53:10 +0900112import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Libcc42282018-09-13 20:59:34 +0900113import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtMaskFlag;
114import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtStateFlag;
115import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.niciraConnTrackTreatmentBuilder;
sangho1aaa7882017-05-31 13:22:47 +0900116import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
sangho6a9ff0d2017-03-27 11:23:37 +0900117import static org.slf4j.LoggerFactory.getLogger;
118
119/**
120 * Populates flow rules to handle OpenStack SecurityGroups.
121 */
Ray Milkey8e406512018-10-24 15:56:50 -0700122@Component(
123 immediate = true,
124 property = {
125 USE_SECURITY_GROUP + ":Boolean=" + USE_SECURITY_GROUP_DEFAULT
126 }
127)
sangho6a9ff0d2017-03-27 11:23:37 +0900128public class OpenstackSecurityGroupHandler {
129
130 private final Logger log = getLogger(getClass());
131
Jian Li2b9838c2018-10-28 17:09:42 +0900132 private static final int VM_IP_PREFIX = 32;
133
Jian Li4d138702018-11-27 17:25:28 +0900134 private static final String STR_ZERO = "0";
135 private static final String STR_ONE = "1";
136 private static final String STR_NULL = "null";
137 private static final String STR_PADDING = "0000000000000000";
138 private static final int MASK_BEGIN_IDX = 0;
139 private static final int MASK_MAX_IDX = 16;
140 private static final int MASK_RADIX = 2;
141 private static final int PORT_RADIX = 16;
142
143 // Apply OpenStack security group rule for VM traffic.
Ray Milkey8e406512018-10-24 15:56:50 -0700144 private boolean useSecurityGroup = USE_SECURITY_GROUP_DEFAULT;
sangho0248ca22017-05-31 13:22:47 +0900145
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700146 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho6a9ff0d2017-03-27 11:23:37 +0900147 protected CoreService coreService;
148
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700149 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li9d35bd62018-10-13 01:43:24 +0900150 protected InstancePortAdminService instancePortService;
sangho6a9ff0d2017-03-27 11:23:37 +0900151
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700152 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho6a9ff0d2017-03-27 11:23:37 +0900153 protected MastershipService mastershipService;
154
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moonae51e732017-04-25 17:46:21 +0900156 protected OpenstackNetworkService osNetService;
sangho6a9ff0d2017-03-27 11:23:37 +0900157
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho6a9ff0d2017-03-27 11:23:37 +0900159 protected OpenstackSecurityGroupService securityGroupService;
160
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sanghodc375372017-06-08 10:41:30 +0900162 protected OpenstackFlowRuleService osFlowRuleService;
sangho6a9ff0d2017-03-27 11:23:37 +0900163
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho0248ca22017-05-31 13:22:47 +0900165 protected ComponentConfigService configService;
166
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho3dd2a8b2017-07-19 15:54:31 +0900168 protected OpenstackNodeService osNodeService;
169
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho1aaa7882017-05-31 13:22:47 +0900171 protected DriverService driverService;
172
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho1aaa7882017-05-31 13:22:47 +0900174 protected LeadershipService leadershipService;
175
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho1aaa7882017-05-31 13:22:47 +0900177 protected ClusterService clusterService;
178
Ray Milkey0b18b722018-10-16 13:19:15 -0700179 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Liac30e272018-10-18 23:08:03 +0900180 protected StorageService storageService;
181
182 private static final KryoNamespace SERIALIZER_PORT = KryoNamespace.newBuilder()
183 .register(KryoNamespaces.API)
184 .register(Port.class)
185 .register(NeutronPort.class)
186 .register(NeutronIP.class)
187 .register(State.class)
188 .register(NeutronAllowedAddressPair.class)
189 .register(NeutronExtraDhcpOptCreate.class)
190 .register(LinkedHashMap.class)
191 .build();
Jian Li9d35bd62018-10-13 01:43:24 +0900192
Jian Libcc42282018-09-13 20:59:34 +0900193 private final InstancePortListener instancePortListener =
Jian Lia70fdb602018-11-05 01:32:22 +0900194 new InternalInstancePortListener();
Jian Libcc42282018-09-13 20:59:34 +0900195 private final OpenstackNetworkListener osNetworkListener =
Jian Lia70fdb602018-11-05 01:32:22 +0900196 new InternalOpenstackNetworkListener();
Jian Libcc42282018-09-13 20:59:34 +0900197 private final OpenstackNetworkListener osPortListener =
Jian Lia70fdb602018-11-05 01:32:22 +0900198 new InternalOpenstackPortListener();
Jian Libcc42282018-09-13 20:59:34 +0900199 private final OpenstackSecurityGroupListener securityGroupListener =
Jian Lia70fdb602018-11-05 01:32:22 +0900200 new InternalSecurityGroupListener();
sangho1aaa7882017-05-31 13:22:47 +0900201 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
Jian Liac30e272018-10-18 23:08:03 +0900202
203 private ConsistentMap<String, Port> removedOsPortStore;
204
sangho6a9ff0d2017-03-27 11:23:37 +0900205 private ApplicationId appId;
sangho1aaa7882017-05-31 13:22:47 +0900206 private NodeId localNodeId;
sangho6a9ff0d2017-03-27 11:23:37 +0900207
208 private final ExecutorService eventExecutor = newSingleThreadExecutor(
209 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
210
211 private static final String PROTO_ICMP = "ICMP";
212 private static final String PROTO_TCP = "TCP";
213 private static final String PROTO_UDP = "UDP";
214 private static final String ETHTYPE_IPV4 = "IPV4";
215 private static final String EGRESS = "EGRESS";
216 private static final String INGRESS = "INGRESS";
217 private static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
218
Jian Lie8b28db2018-10-17 14:04:09 +0900219 private static final String VXLAN = "VXLAN";
220 private static final String VLAN = "VLAN";
221
sangho1aaa7882017-05-31 13:22:47 +0900222 // We expose pipeline structure to SONA application considering removing pipeline soon.
sanghoshinbbeb31a2018-09-11 17:01:01 +0800223 private static final int GOTO_CONNTRACK_TABLE = CT_TABLE;
224 private static final int GOTO_JUMP_TABLE = JUMP_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900225
226 private static final int CT_COMMIT = 0;
227 private static final int CT_NO_COMMIT = 1;
228 private static final short CT_NO_RECIRC = -1;
229
230 private static final int ACTION_NONE = 0;
231 private static final int ACTION_DROP = -1;
232
sangho6a9ff0d2017-03-27 11:23:37 +0900233 @Activate
234 protected void activate() {
235 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
sangho1aaa7882017-05-31 13:22:47 +0900236 localNodeId = clusterService.getLocalNode().id();
sangho6a9ff0d2017-03-27 11:23:37 +0900237 instancePortService.addListener(instancePortListener);
238 securityGroupService.addListener(securityGroupListener);
Jian Libcc42282018-09-13 20:59:34 +0900239 osNetService.addListener(osPortListener);
240 osNetService.addListener(osNetworkListener);
sangho0248ca22017-05-31 13:22:47 +0900241 configService.registerProperties(getClass());
sangho1aaa7882017-05-31 13:22:47 +0900242 osNodeService.addListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900243
Jian Liac30e272018-10-18 23:08:03 +0900244 removedOsPortStore = storageService.<String, Port>consistentMapBuilder()
245 .withSerializer(Serializer.using(SERIALIZER_PORT))
246 .withName("openstack-removed-portstore")
247 .withApplicationId(appId)
248 .build();
249
sangho6a9ff0d2017-03-27 11:23:37 +0900250 log.info("Started");
251 }
252
253 @Deactivate
254 protected void deactivate() {
255 instancePortService.removeListener(instancePortListener);
256 securityGroupService.removeListener(securityGroupListener);
Jian Libcc42282018-09-13 20:59:34 +0900257 osNetService.removeListener(osNetworkListener);
258 osNetService.removeListener(osPortListener);
sangho0248ca22017-05-31 13:22:47 +0900259 configService.unregisterProperties(getClass(), false);
sangho1aaa7882017-05-31 13:22:47 +0900260 osNodeService.removeListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900261 eventExecutor.shutdown();
262
263 log.info("Stopped");
264 }
265
sangho0248ca22017-05-31 13:22:47 +0900266 @Modified
267 protected void modified(ComponentContext context) {
268 Dictionary<?, ?> properties = context.getProperties();
269 Boolean flag;
270
Ray Milkey8e406512018-10-24 15:56:50 -0700271 flag = Tools.isPropertyEnabled(properties, USE_SECURITY_GROUP);
sangho0248ca22017-05-31 13:22:47 +0900272 if (flag == null) {
273 log.info("useSecurityGroup is not configured, " +
274 "using current value of {}", useSecurityGroup);
275 } else {
276 useSecurityGroup = flag;
277 log.info("Configured. useSecurityGroup is {}",
278 useSecurityGroup ? "enabled" : "disabled");
279 }
280
sanghoe6457a32017-08-24 14:31:19 +0900281 securityGroupService.setSecurityGroupEnabled(useSecurityGroup);
sangho0248ca22017-05-31 13:22:47 +0900282 resetSecurityGroupRules();
283 }
284
sangho1aaa7882017-05-31 13:22:47 +0900285 private void initializeConnTrackTable(DeviceId deviceId, boolean install) {
286
287 //table=1,ip,ct_state=-trk, actions=ct(table:2)
Jian Libcc42282018-09-13 20:59:34 +0900288 long ctState = computeCtStateFlag(false, false, false);
289 long ctMask = computeCtMaskFlag(true, false, false);
sangho1aaa7882017-05-31 13:22:47 +0900290 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, (short) GOTO_CONNTRACK_TABLE,
291 ACTION_NONE, PRIORITY_CT_HOOK_RULE, install);
292
293 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+est,action=goto_table:3
Jian Libcc42282018-09-13 20:59:34 +0900294 ctState = computeCtStateFlag(true, false, true);
295 ctMask = computeCtMaskFlag(true, false, true);
sangho1aaa7882017-05-31 13:22:47 +0900296 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
297 GOTO_JUMP_TABLE, PRIORITY_CT_RULE, install);
298
299 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+new,action=drop
Jian Libcc42282018-09-13 20:59:34 +0900300 ctState = computeCtStateFlag(true, true, false);
301 ctMask = computeCtMaskFlag(true, true, false);
sangho1aaa7882017-05-31 13:22:47 +0900302 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
303 ACTION_DROP, PRIORITY_CT_DROP_RULE, install);
304 }
305
Jian Li1e9cb732018-11-25 23:17:21 +0900306 private void initializeAclTable(DeviceId deviceId, boolean install) {
307
308 ExtensionTreatment ctTreatment =
309 niciraConnTrackTreatmentBuilder(driverService, deviceId)
310 .commit(true)
311 .build();
312
313 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
314 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
315
316 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
317 tBuilder.extension(ctTreatment, deviceId)
318 .transition(JUMP_TABLE);
319
320 osFlowRuleService.setRule(appId,
321 deviceId,
322 sBuilder.build(),
323 tBuilder.build(),
324 PRIORITY_ACL_INGRESS_RULE,
325 ACL_RECIRC_TABLE,
326 install);
327 }
328
329 private void initializeIngressTable(DeviceId deviceId, boolean install) {
330 if (install) {
331 osFlowRuleService.setUpTableMissEntry(deviceId, ACL_INGRESS_TABLE);
332 } else {
333 osFlowRuleService.connectTables(deviceId, ACL_INGRESS_TABLE, JUMP_TABLE);
334 }
335 }
336
Jian Libcc42282018-09-13 20:59:34 +0900337 private void updateSecurityGroupRule(InstancePort instPort, Port port,
338 SecurityGroupRule sgRule, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900339
Daniel Park3a140592018-06-28 18:33:10 +0900340 if (instPort == null || port == null || sgRule == null) {
341 return;
342 }
343
sangho6a9ff0d2017-03-27 11:23:37 +0900344 if (sgRule.getRemoteGroupId() != null && !sgRule.getRemoteGroupId().isEmpty()) {
Jian Lia70fdb602018-11-05 01:32:22 +0900345 getRemoteInstPorts(port, sgRule.getRemoteGroupId(), install)
Jian Libcc42282018-09-13 20:59:34 +0900346 .forEach(rInstPort -> {
Jian Li1e9cb732018-11-25 23:17:21 +0900347 populateSecurityGroupRule(sgRule, instPort,
Jian Libcc42282018-09-13 20:59:34 +0900348 rInstPort.ipAddress().toIpPrefix(), install);
Jian Li1e9cb732018-11-25 23:17:21 +0900349 populateSecurityGroupRule(sgRule, rInstPort,
Jian Libcc42282018-09-13 20:59:34 +0900350 instPort.ipAddress().toIpPrefix(), install);
sangho6a9ff0d2017-03-27 11:23:37 +0900351
Jian Libcc42282018-09-13 20:59:34 +0900352 SecurityGroupRule rSgRule =
353 new NeutronSecurityGroupRule
354 .SecurityGroupRuleConcreteBuilder()
Jian Li28ec77f2018-10-31 07:07:25 +0900355 .from(sgRule)
Jian Li4d138702018-11-27 17:25:28 +0900356 .direction(sgRule.getDirection()
357 .equalsIgnoreCase(EGRESS) ? INGRESS : EGRESS)
Jian Li28ec77f2018-10-31 07:07:25 +0900358 .build();
Jian Li1e9cb732018-11-25 23:17:21 +0900359 populateSecurityGroupRule(rSgRule, instPort,
Jian Libcc42282018-09-13 20:59:34 +0900360 rInstPort.ipAddress().toIpPrefix(), install);
Jian Li1e9cb732018-11-25 23:17:21 +0900361 populateSecurityGroupRule(rSgRule, rInstPort,
Jian Libcc42282018-09-13 20:59:34 +0900362 instPort.ipAddress().toIpPrefix(), install);
363 });
sangho6a9ff0d2017-03-27 11:23:37 +0900364 } else {
Jian Li1e9cb732018-11-25 23:17:21 +0900365 populateSecurityGroupRule(sgRule, instPort,
Jian Libcc42282018-09-13 20:59:34 +0900366 sgRule.getRemoteIpPrefix() == null ? IP_PREFIX_ANY :
Jian Li28ec77f2018-10-31 07:07:25 +0900367 IpPrefix.valueOf(sgRule.getRemoteIpPrefix()), install);
sangho6a9ff0d2017-03-27 11:23:37 +0900368 }
369 }
370
Jian Libcc42282018-09-13 20:59:34 +0900371 private void populateSecurityGroupRule(SecurityGroupRule sgRule,
372 InstancePort instPort,
Jian Lie8b28db2018-10-17 14:04:09 +0900373 IpPrefix remoteIp,
374 boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900375 Set<TrafficSelector> selectors = buildSelectors(sgRule,
Jian Li1e9cb732018-11-25 23:17:21 +0900376 Ip4Address.valueOf(instPort.ipAddress().toInetAddress()),
377 remoteIp, instPort.networkId());
sangho2e97be02017-07-03 18:18:27 +0900378 if (selectors == null || selectors.isEmpty()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900379 return;
380 }
381
Jian Li28ec77f2018-10-31 07:07:25 +0900382 // XXX All egress traffic needs to go through connection tracking module,
383 // which might hurt its performance.
384 ExtensionTreatment ctTreatment =
385 niciraConnTrackTreatmentBuilder(driverService, instPort.deviceId())
386 .commit(true)
387 .build();
388
Jian Li1e9cb732018-11-25 23:17:21 +0900389 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Jian Li28ec77f2018-10-31 07:07:25 +0900390
Jian Li1e9cb732018-11-25 23:17:21 +0900391 int aclTable;
Jian Li4d138702018-11-27 17:25:28 +0900392 if (sgRule.getDirection().equalsIgnoreCase(EGRESS)) {
Jian Li1e9cb732018-11-25 23:17:21 +0900393 aclTable = ACL_EGRESS_TABLE;
394 tBuilder.transition(ACL_RECIRC_TABLE);
395 } else {
396 aclTable = ACL_INGRESS_TABLE;
397 tBuilder.extension(ctTreatment, instPort.deviceId())
398 .transition(JUMP_TABLE);
399 }
400
401 int finalAclTable = aclTable;
402 selectors.forEach(selector -> {
403 osFlowRuleService.setRule(appId,
404 instPort.deviceId(),
405 selector, tBuilder.build(),
406 PRIORITY_ACL_RULE,
407 finalAclTable,
408 install);
409 });
sangho6a9ff0d2017-03-27 11:23:37 +0900410 }
411
412 /**
sangho1aaa7882017-05-31 13:22:47 +0900413 * Sets connection tracking rule using OVS extension commands.
Jian Li2b9838c2018-10-28 17:09:42 +0900414 * It is not so graceful, but I don't want to make it more general because
415 * it is going to be used only here.
416 * The following is the usage of the function.
sangho1aaa7882017-05-31 13:22:47 +0900417 *
418 * @param deviceId Device ID
Jian Li2b9838c2018-10-28 17:09:42 +0900419 * @param ctState ctState: please use RulePopulatorUtil.computeCtStateFlag()
420 * to build the value
421 * @param ctMask crMask: please use RulePopulatorUtil.computeCtMaskFlag()
422 * to build the value
sangho1aaa7882017-05-31 13:22:47 +0900423 * @param commit CT_COMMIT for commit action, CT_NO_COMMIT otherwise
Jian Li2b9838c2018-10-28 17:09:42 +0900424 * @param recircTable table number for recirculation after CT actions.
425 * CT_NO_RECIRC with no recirculation
426 * @param action Additional actions. ACTION_DROP, ACTION_NONE,
427 * GOTO_XXX_TABLE are supported.
sangho1aaa7882017-05-31 13:22:47 +0900428 * @param priority priority value for the rule
429 * @param install true for insertion, false for removal
430 */
431 private void setConnTrackRule(DeviceId deviceId, long ctState, long ctMask,
432 int commit, short recircTable,
433 int action, int priority, boolean install) {
434
Jian Libcc42282018-09-13 20:59:34 +0900435 ExtensionSelector esCtSate = RulePopulatorUtil
436 .buildCtExtensionSelector(driverService, deviceId, ctState, ctMask);
sangho1aaa7882017-05-31 13:22:47 +0900437 TrafficSelector selector = DefaultTrafficSelector.builder()
438 .extension(esCtSate, deviceId)
439 .matchEthType(Ethernet.TYPE_IPV4)
440 .build();
441
442 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
443
444 if (commit == CT_COMMIT || recircTable > 0) {
445 RulePopulatorUtil.NiriraConnTrackTreatmentBuilder natTreatmentBuilder =
Jian Libcc42282018-09-13 20:59:34 +0900446 niciraConnTrackTreatmentBuilder(driverService, deviceId);
sangho1aaa7882017-05-31 13:22:47 +0900447 natTreatmentBuilder.natAction(false);
448 if (commit == CT_COMMIT) {
449 natTreatmentBuilder.commit(true);
450 } else {
451 natTreatmentBuilder.commit(false);
452 }
453 if (recircTable > 0) {
454 natTreatmentBuilder.table(recircTable);
455 }
456 tb.extension(natTreatmentBuilder.build(), deviceId);
457 } else if (action == ACTION_DROP) {
458 tb.drop();
459 }
460
sanghoshinbbeb31a2018-09-11 17:01:01 +0800461 if (action != ACTION_NONE && action != ACTION_DROP) {
sangho1aaa7882017-05-31 13:22:47 +0900462 tb.transition(action);
463 }
464
465 int tableType = ERROR_TABLE;
466 if (priority == PRIORITY_CT_RULE || priority == PRIORITY_CT_DROP_RULE) {
467 tableType = CT_TABLE;
468 } else if (priority == PRIORITY_CT_HOOK_RULE) {
Jian Li1e9cb732018-11-25 23:17:21 +0900469 tableType = ACL_INGRESS_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900470 } else {
471 log.error("Cannot an appropriate table for the conn track rule.");
472 }
473
474 osFlowRuleService.setRule(
475 appId,
476 deviceId,
477 selector,
478 tb.build(),
479 priority,
480 tableType,
481 install);
482 }
483
484 /**
sangho6a9ff0d2017-03-27 11:23:37 +0900485 * Returns a set of host IP addresses engaged with supplied security group ID.
486 * It only searches a VM in the same tenant boundary.
487 *
Jian Lia70fdb602018-11-05 01:32:22 +0900488 * @param srcPort openstack port
sangho6a9ff0d2017-03-27 11:23:37 +0900489 * @param sgId security group id
490 * @return set of ip addresses
491 */
Jian Lia70fdb602018-11-05 01:32:22 +0900492 private Set<InstancePort> getRemoteInstPorts(Port srcPort,
Jian Li2b9838c2018-10-28 17:09:42 +0900493 String sgId, boolean install) {
sangho6a9ff0d2017-03-27 11:23:37 +0900494 Set<InstancePort> remoteInstPorts;
495
Jian Liac30e272018-10-18 23:08:03 +0900496 Set<Port> removedPorts = Sets.newConcurrentHashSet();
497
498 if (!install) {
499 removedPorts = new HashSet<>(removedOsPortStore.asJavaMap().values());
500 }
501
502 remoteInstPorts = Sets.union(osNetService.ports(), removedPorts).stream()
Jian Lia70fdb602018-11-05 01:32:22 +0900503 .filter(port -> !port.getId().equals(srcPort.getId()))
504 .filter(port -> port.getTenantId().equals(srcPort.getTenantId()))
sangho6a9ff0d2017-03-27 11:23:37 +0900505 .filter(port -> port.getSecurityGroups().contains(sgId))
Jian Lia70fdb602018-11-05 01:32:22 +0900506 .filter(port -> port.getNetworkId().equals(srcPort.getNetworkId()))
sangho6a9ff0d2017-03-27 11:23:37 +0900507 .map(port -> instancePortService.instancePort(port.getId()))
508 .filter(instPort -> instPort != null && instPort.ipAddress() != null)
509 .collect(Collectors.toSet());
510
511 return Collections.unmodifiableSet(remoteInstPorts);
512 }
513
sangho2e97be02017-07-03 18:18:27 +0900514 private Set<TrafficSelector> buildSelectors(SecurityGroupRule sgRule,
515 Ip4Address vmIp,
Jian Lie8b28db2018-10-17 14:04:09 +0900516 IpPrefix remoteIp,
Jian Li1e9cb732018-11-25 23:17:21 +0900517 String netId) {
Jian Li2b9838c2018-10-28 17:09:42 +0900518 if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, VM_IP_PREFIX))) {
sangho2e97be02017-07-03 18:18:27 +0900519 // do nothing if the remote IP is my IP
520 return null;
521 }
522
523 Set<TrafficSelector> selectorSet = Sets.newHashSet();
524
525 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
Jian Li1e9cb732018-11-25 23:17:21 +0900526 buildMatches(sBuilder, sgRule, vmIp, remoteIp, netId);
sangho2e97be02017-07-03 18:18:27 +0900527
528 if (sgRule.getPortRangeMax() != null && sgRule.getPortRangeMin() != null &&
529 sgRule.getPortRangeMin() < sgRule.getPortRangeMax()) {
Jian Libcc42282018-09-13 20:59:34 +0900530 Map<TpPort, TpPort> portRangeMatchMap =
531 buildPortRangeMatches(sgRule.getPortRangeMin(),
Jian Li28ec77f2018-10-31 07:07:25 +0900532 sgRule.getPortRangeMax());
Jian Li2b9838c2018-10-28 17:09:42 +0900533 portRangeMatchMap.forEach((key, value) -> {
sangho2e97be02017-07-03 18:18:27 +0900534
Jian Li4d138702018-11-27 17:25:28 +0900535 if (sgRule.getProtocol().equalsIgnoreCase(PROTO_TCP)) {
536 if (sgRule.getDirection().equalsIgnoreCase(EGRESS)) {
Jian Li2b9838c2018-10-28 17:09:42 +0900537 sBuilder.matchTcpSrcMasked(key, value);
538 } else {
539 sBuilder.matchTcpDstMasked(key, value);
Jian Libcc42282018-09-13 20:59:34 +0900540 }
Jian Li4d138702018-11-27 17:25:28 +0900541 } else if (sgRule.getProtocol().equalsIgnoreCase(PROTO_UDP)) {
542 if (sgRule.getDirection().equalsIgnoreCase(EGRESS)) {
Jian Li2b9838c2018-10-28 17:09:42 +0900543 sBuilder.matchUdpSrcMasked(key, value);
544 } else {
545 sBuilder.matchUdpDstMasked(key, value);
546 }
547 }
548
549 selectorSet.add(sBuilder.build());
550 });
sangho2e97be02017-07-03 18:18:27 +0900551 } else {
552 selectorSet.add(sBuilder.build());
553 }
554
555 return selectorSet;
556 }
557
Jian Libcc42282018-09-13 20:59:34 +0900558 private void buildMatches(TrafficSelector.Builder sBuilder,
Jian Li1e9cb732018-11-25 23:17:21 +0900559 SecurityGroupRule sgRule, Ip4Address vmIp,
560 IpPrefix remoteIp, String netId) {
561 buildTunnelId(sBuilder, netId);
sangho6a9ff0d2017-03-27 11:23:37 +0900562 buildMatchEthType(sBuilder, sgRule.getEtherType());
563 buildMatchDirection(sBuilder, sgRule.getDirection(), vmIp);
564 buildMatchProto(sBuilder, sgRule.getProtocol());
565 buildMatchPort(sBuilder, sgRule.getProtocol(), sgRule.getDirection(),
sangho2e97be02017-07-03 18:18:27 +0900566 sgRule.getPortRangeMin() == null ? 0 : sgRule.getPortRangeMin(),
567 sgRule.getPortRangeMax() == null ? 0 : sgRule.getPortRangeMax());
sangho6a9ff0d2017-03-27 11:23:37 +0900568 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
sangho6a9ff0d2017-03-27 11:23:37 +0900569 }
570
Jian Li1e9cb732018-11-25 23:17:21 +0900571 private void buildTunnelId(TrafficSelector.Builder sBuilder, String netId) {
572 String segId = osNetService.segmentId(netId);
573 String netType = osNetService.networkType(netId);
Jian Lie8b28db2018-10-17 14:04:09 +0900574
575 if (VLAN.equals(netType)) {
576 sBuilder.matchVlanId(VlanId.vlanId(segId));
577 } else if (VXLAN.equals(netType)) {
578 sBuilder.matchTunnelId(Long.valueOf(segId));
Jian Li28ec77f2018-10-31 07:07:25 +0900579 } else {
580 log.warn("Cannot tag the VID due to lack of support of virtual network type {}", netType);
Jian Lie8b28db2018-10-17 14:04:09 +0900581 }
582 }
583
sangho6a9ff0d2017-03-27 11:23:37 +0900584 private void buildMatchDirection(TrafficSelector.Builder sBuilder,
585 String direction,
586 Ip4Address vmIp) {
Jian Li4d138702018-11-27 17:25:28 +0900587 if (direction.equalsIgnoreCase(EGRESS)) {
Jian Li2b9838c2018-10-28 17:09:42 +0900588 sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, VM_IP_PREFIX));
sangho6a9ff0d2017-03-27 11:23:37 +0900589 } else {
Jian Li2b9838c2018-10-28 17:09:42 +0900590 sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, VM_IP_PREFIX));
sangho6a9ff0d2017-03-27 11:23:37 +0900591 }
592 }
593
594 private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
595 // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
596 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
Jian Li4d138702018-11-27 17:25:28 +0900597 if (etherType != null && !Objects.equals(etherType, STR_NULL) &&
598 !etherType.equalsIgnoreCase(ETHTYPE_IPV4)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900599 log.debug("EthType {} is not supported yet in Security Group", etherType);
600 }
601 }
602
Jian Libcc42282018-09-13 20:59:34 +0900603 private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder,
604 IpPrefix remoteIpPrefix, String direction) {
605 if (remoteIpPrefix != null &&
606 !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
Jian Li4d138702018-11-27 17:25:28 +0900607 if (direction.equalsIgnoreCase(EGRESS)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900608 sBuilder.matchIPDst(remoteIpPrefix);
609 } else {
610 sBuilder.matchIPSrc(remoteIpPrefix);
611 }
612 }
613 }
614
615 private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
616 if (protocol != null) {
617 switch (protocol.toUpperCase()) {
618 case PROTO_ICMP:
619 sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
620 break;
621 case PROTO_TCP:
622 sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
623 break;
624 case PROTO_UDP:
625 sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
626 break;
627 default:
628 }
629 }
630 }
631
Jian Libcc42282018-09-13 20:59:34 +0900632 private void buildMatchPort(TrafficSelector.Builder sBuilder,
633 String protocol, String direction,
sangho6a9ff0d2017-03-27 11:23:37 +0900634 int portMin, int portMax) {
635 if (portMin > 0 && portMax > 0 && portMin == portMax) {
Jian Li4d138702018-11-27 17:25:28 +0900636 if (protocol.equalsIgnoreCase(PROTO_TCP)) {
637 if (direction.equalsIgnoreCase(EGRESS)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900638 sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
639 } else {
640 sBuilder.matchTcpDst(TpPort.tpPort(portMax));
641 }
Jian Li4d138702018-11-27 17:25:28 +0900642 } else if (protocol.equalsIgnoreCase(PROTO_UDP)) {
643 if (direction.equalsIgnoreCase(EGRESS)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900644 sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
645 } else {
646 sBuilder.matchUdpDst(TpPort.tpPort(portMax));
647 }
648 }
649 }
650 }
651
sangho0248ca22017-05-31 13:22:47 +0900652 private void resetSecurityGroupRules() {
653
654 if (useSecurityGroup) {
Jian Lib6969502018-10-30 20:38:07 +0900655 osNodeService.completeNodes(COMPUTE).forEach(node -> {
Jian Li1e9cb732018-11-25 23:17:21 +0900656 osFlowRuleService.setUpTableMissEntry(node.intgBridge(), ACL_EGRESS_TABLE);
Jian Lib6969502018-10-30 20:38:07 +0900657 initializeConnTrackTable(node.intgBridge(), true);
Jian Li1e9cb732018-11-25 23:17:21 +0900658 initializeAclTable(node.intgBridge(), true);
659 initializeIngressTable(node.intgBridge(), true);
Jian Lib6969502018-10-30 20:38:07 +0900660 });
661
sangho0248ca22017-05-31 13:22:47 +0900662 securityGroupService.securityGroups().forEach(securityGroup ->
663 securityGroup.getRules().forEach(this::securityGroupRuleAdded));
664 } else {
Jian Lib6969502018-10-30 20:38:07 +0900665 osNodeService.completeNodes(COMPUTE).forEach(node -> {
Jian Li1e9cb732018-11-25 23:17:21 +0900666 osFlowRuleService.connectTables(node.intgBridge(), ACL_EGRESS_TABLE, JUMP_TABLE);
Jian Lib6969502018-10-30 20:38:07 +0900667 initializeConnTrackTable(node.intgBridge(), false);
Jian Li1e9cb732018-11-25 23:17:21 +0900668 initializeAclTable(node.intgBridge(), false);
669 initializeIngressTable(node.intgBridge(), false);
Jian Lib6969502018-10-30 20:38:07 +0900670 });
671
sangho0248ca22017-05-31 13:22:47 +0900672 securityGroupService.securityGroups().forEach(securityGroup ->
673 securityGroup.getRules().forEach(this::securityGroupRuleRemoved));
674 }
675
Jian Libcc42282018-09-13 20:59:34 +0900676 log.info("Reset security group info " +
Jian Li28ec77f2018-10-31 07:07:25 +0900677 (useSecurityGroup ? " with " : " without") + " Security Group");
sangho0248ca22017-05-31 13:22:47 +0900678 }
679
680 private void securityGroupRuleAdded(SecurityGroupRule sgRule) {
681 osNetService.ports().stream()
Jian Libcc42282018-09-13 20:59:34 +0900682 .filter(port -> port.getSecurityGroups()
Jian Li28ec77f2018-10-31 07:07:25 +0900683 .contains(sgRule.getSecurityGroupId()))
sangho0248ca22017-05-31 13:22:47 +0900684 .forEach(port -> {
685 updateSecurityGroupRule(
686 instancePortService.instancePort(port.getId()),
687 port, sgRule, true);
688 log.debug("Applied security group rule {} to port {}",
689 sgRule.getId(), port.getId());
690 });
691 }
692
693 private void securityGroupRuleRemoved(SecurityGroupRule sgRule) {
Jian Liac30e272018-10-18 23:08:03 +0900694 Set<Port> removedPorts = new HashSet<>(removedOsPortStore.asJavaMap().values());
695
696 Sets.union(osNetService.ports(), removedPorts).stream()
Jian Libcc42282018-09-13 20:59:34 +0900697 .filter(port -> port.getSecurityGroups()
Jian Li28ec77f2018-10-31 07:07:25 +0900698 .contains(sgRule.getSecurityGroupId()))
sangho0248ca22017-05-31 13:22:47 +0900699 .forEach(port -> {
700 updateSecurityGroupRule(
701 instancePortService.instancePort(port.getId()),
702 port, sgRule, false);
703 log.debug("Removed security group rule {} from port {}",
704 sgRule.getId(), port.getId());
705 });
706 }
707
sangho2e97be02017-07-03 18:18:27 +0900708 private int binLower(String binStr, int bits) {
Jian Li4d138702018-11-27 17:25:28 +0900709 StringBuilder outBin = new StringBuilder(
710 binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
sangho2e97be02017-07-03 18:18:27 +0900711 for (int i = 0; i < bits; i++) {
Jian Li4d138702018-11-27 17:25:28 +0900712 outBin.append(STR_ZERO);
sangho2e97be02017-07-03 18:18:27 +0900713 }
714
Jian Li4d138702018-11-27 17:25:28 +0900715 return Integer.parseInt(outBin.toString(), MASK_RADIX);
sangho2e97be02017-07-03 18:18:27 +0900716 }
717
718 private int binHigher(String binStr, int bits) {
Jian Li4d138702018-11-27 17:25:28 +0900719 StringBuilder outBin = new StringBuilder(
720 binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
sangho2e97be02017-07-03 18:18:27 +0900721 for (int i = 0; i < bits; i++) {
Jian Li4d138702018-11-27 17:25:28 +0900722 outBin.append(STR_ONE);
sangho2e97be02017-07-03 18:18:27 +0900723 }
724
Jian Li4d138702018-11-27 17:25:28 +0900725 return Integer.parseInt(outBin.toString(), MASK_RADIX);
sangho2e97be02017-07-03 18:18:27 +0900726 }
727
728 private int testMasks(String binStr, int start, int end) {
Jian Li4d138702018-11-27 17:25:28 +0900729 int mask = MASK_BEGIN_IDX;
730 for (; mask <= MASK_MAX_IDX; mask++) {
sangho2e97be02017-07-03 18:18:27 +0900731 int maskStart = binLower(binStr, mask);
732 int maskEnd = binHigher(binStr, mask);
733 if (maskStart < start || maskEnd > end) {
734 return mask - 1;
735 }
736 }
737
738 return mask;
739 }
740
741 private String getMask(int bits) {
742 switch (bits) {
743 case 0: return "ffff";
744 case 1: return "fffe";
745 case 2: return "fffc";
746 case 3: return "fff8";
747 case 4: return "fff0";
748 case 5: return "ffe0";
749 case 6: return "ffc0";
750 case 7: return "ff80";
751 case 8: return "ff00";
752 case 9: return "fe00";
753 case 10: return "fc00";
754 case 11: return "f800";
755 case 12: return "f000";
756 case 13: return "e000";
757 case 14: return "c000";
758 case 15: return "8000";
759 case 16: return "0000";
760 default: return null;
761 }
762 }
763
764 private Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
765
766 boolean processing = true;
767 int start = portMin;
768 Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
769 while (processing) {
770 String minStr = Integer.toBinaryString(start);
Jian Li4d138702018-11-27 17:25:28 +0900771 String binStrMinPadded = STR_PADDING.substring(minStr.length()) + minStr;
sangho2e97be02017-07-03 18:18:27 +0900772
773 int mask = testMasks(binStrMinPadded, start, portMax);
774 int maskStart = binLower(binStrMinPadded, mask);
775 int maskEnd = binHigher(binStrMinPadded, mask);
776
777 log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
Jian Libcc42282018-09-13 20:59:34 +0900778 portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(
Jian Li4d138702018-11-27 17:25:28 +0900779 Integer.parseInt(Objects.requireNonNull(getMask(mask)), PORT_RADIX)));
sangho2e97be02017-07-03 18:18:27 +0900780
781 start = maskEnd + 1;
782 if (start > portMax) {
783 processing = false;
784 }
785 }
786
787 return portMaskMap;
788 }
789
sangho6a9ff0d2017-03-27 11:23:37 +0900790 private class InternalInstancePortListener implements InstancePortListener {
791
792 @Override
793 public boolean isRelevant(InstancePortEvent event) {
Jian Li34220ea2018-11-14 01:30:24 +0900794 return useSecurityGroup;
795 }
796
797 private boolean isRelevantHelper(InstancePortEvent event) {
798 return mastershipService.isLocalMaster(event.subject().deviceId());
sangho6a9ff0d2017-03-27 11:23:37 +0900799 }
800
801 @Override
802 public void event(InstancePortEvent event) {
sangho6a9ff0d2017-03-27 11:23:37 +0900803 switch (event.type()) {
Jian Liec5c32b2018-07-13 14:28:58 +0900804 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Lie8b28db2018-10-17 14:04:09 +0900805 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liac30e272018-10-18 23:08:03 +0900806 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
Jian Li4d138702018-11-27 17:25:28 +0900807 eventExecutor.execute(() -> processInstanceMigrationStart(event));
Jian Liac30e272018-10-18 23:08:03 +0900808 break;
809 case OPENSTACK_INSTANCE_PORT_VANISHED:
Jian Li4d138702018-11-27 17:25:28 +0900810 eventExecutor.execute(() -> processInstancePortVanish(event));
sangho6a9ff0d2017-03-27 11:23:37 +0900811 break;
Jian Lib8cdcc12018-10-23 01:53:10 +0900812 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Li4d138702018-11-27 17:25:28 +0900813 eventExecutor.execute(() -> processInstanceMigrationEnd(event));
sangho6a9ff0d2017-03-27 11:23:37 +0900814 break;
sangho6a9ff0d2017-03-27 11:23:37 +0900815 default:
816 break;
817 }
818 }
Jian Liac30e272018-10-18 23:08:03 +0900819
Jian Li4d138702018-11-27 17:25:28 +0900820 private void processInstanceMigrationStart(InstancePortEvent event) {
821 if (!isRelevantHelper(event)) {
822 return;
823 }
824
825 InstancePort instPort = event.subject();
826 installSecurityGroupRules(event, instPort);
827 setAclRecircRules(instPort, true);
828 }
829
830 private void processInstancePortVanish(InstancePortEvent event) {
831 if (!isRelevantHelper(event)) {
832 return;
833 }
834
835 InstancePort instPort = event.subject();
836 Port osPort = removedOsPortStore.asJavaMap().get(instPort.portId());
837 setSecurityGroupRules(instPort, osPort, false);
838 removedOsPortStore.remove(instPort.portId());
839 setAclRecircRules(instPort, false);
840 }
841
842 private void processInstanceMigrationEnd(InstancePortEvent event) {
843 if (!isRelevantHelper(event)) {
844 return;
845 }
846
847 InstancePort instPort = event.subject();
848 InstancePort revisedInstPort = swapStaleLocation(instPort);
849 Port port = osNetService.port(instPort.portId());
850 setSecurityGroupRules(revisedInstPort, port, false);
851 setAclRecircRules(revisedInstPort, false);
852 }
853
Jian Liac30e272018-10-18 23:08:03 +0900854 private void installSecurityGroupRules(InstancePortEvent event,
855 InstancePort instPort) {
856 log.debug("Instance port detected/updated MAC:{} IP:{}",
857 instPort.macAddress(),
858 instPort.ipAddress());
Jian Li2b9838c2018-10-28 17:09:42 +0900859 eventExecutor.execute(() ->
860 setSecurityGroupRules(instPort,
861 osNetService.port(event.subject().portId()), true));
Jian Liac30e272018-10-18 23:08:03 +0900862 }
Jian Li1e9cb732018-11-25 23:17:21 +0900863
Jian Li4d138702018-11-27 17:25:28 +0900864 private void setSecurityGroupRules(InstancePort instPort,
865 Port port, boolean install) {
866 Port osPort = port;
867
868 if (!install) {
869 Port rmvPort = removedOsPortStore.asJavaMap().get(instPort.portId());
870 if (osPort == null && rmvPort == null) {
871 return;
872 }
873
874 if (port == null) {
875 osPort = rmvPort;
876 }
877 }
878
879 final Port finalPort = osPort;
880
881 osPort.getSecurityGroups().forEach(sgId -> {
882 SecurityGroup sg = securityGroupService.securityGroup(sgId);
883 if (sg == null) {
884 log.error("Security Group {} not found", sgId);
885 return;
886 }
887 sg.getRules().forEach(sgRule ->
888 updateSecurityGroupRule(instPort, finalPort, sgRule, install));
889 final String action = install ? "Installed " : "Removed ";
890 log.debug(action + "Security Group Rule ID : " + sgId);
891 });
892 }
893
Jian Li1e9cb732018-11-25 23:17:21 +0900894 private void setAclRecircRules(InstancePort instPort, boolean install) {
895 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
896
897 Network net = osNetService.network(instPort.networkId());
898 NetworkType netType = net.getNetworkType();
899 String segId = net.getProviderSegID();
900
901 switch (netType) {
902 case VXLAN:
903 sBuilder.matchTunnelId(Long.valueOf(segId));
904 break;
905 case VLAN:
906 sBuilder.matchVlanId(VlanId.vlanId(segId));
907 break;
908 default:
909 break;
910 }
911
912 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
913 sBuilder.matchIPDst(IpPrefix.valueOf(instPort.ipAddress(), VM_IP_PREFIX));
914
915 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
916 tBuilder.transition(ACL_INGRESS_TABLE);
917
918 osFlowRuleService.setRule(
919 appId,
920 instPort.deviceId(),
921 sBuilder.build(),
922 tBuilder.build(),
923 PRIORITY_ACL_RULE,
924 ACL_RECIRC_TABLE,
925 install);
926 }
sangho6a9ff0d2017-03-27 11:23:37 +0900927 }
928
929 private class InternalOpenstackPortListener implements OpenstackNetworkListener {
930
931 @Override
932 public boolean isRelevant(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +0800933 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
sangho6a9ff0d2017-03-27 11:23:37 +0900934 return false;
935 }
Jian Libcc42282018-09-13 20:59:34 +0900936
Jian Li34220ea2018-11-14 01:30:24 +0900937 return useSecurityGroup;
938 }
939
940 private boolean isRelevantHelper(OpenstackNetworkEvent event) {
Jian Libcc42282018-09-13 20:59:34 +0900941 InstancePort instPort = instancePortService.instancePort(event.port().getId());
942
943 if (instPort == null) {
944 return false;
945 }
946
Jian Li34220ea2018-11-14 01:30:24 +0900947 return mastershipService.isLocalMaster(instPort.deviceId());
Jian Libcc42282018-09-13 20:59:34 +0900948 }
949
950 @Override
951 public void event(OpenstackNetworkEvent event) {
952 log.debug("openstack port event received {}", event);
Jian Libcc42282018-09-13 20:59:34 +0900953
Jian Li4d138702018-11-27 17:25:28 +0900954 if (event.type() == OPENSTACK_PORT_PRE_REMOVE) {
955 eventExecutor.execute(() -> processPortPreRemove(event));
Jian Libcc42282018-09-13 20:59:34 +0900956 }
957 }
Jian Li4d138702018-11-27 17:25:28 +0900958
959 private void processPortPreRemove(OpenstackNetworkEvent event) {
960 if (!isRelevantHelper(event)) {
961 return;
962 }
963
964 Port osPort = event.port();
965 removedOsPortStore.put(osPort.getId(), osPort);
966 }
Jian Libcc42282018-09-13 20:59:34 +0900967 }
968
Jian Li34220ea2018-11-14 01:30:24 +0900969 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
Jian Libcc42282018-09-13 20:59:34 +0900970
971 @Override
972 public boolean isRelevant(OpenstackNetworkEvent event) {
973 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
974 return false;
975 }
Jian Li2b9838c2018-10-28 17:09:42 +0900976
Jian Li34220ea2018-11-14 01:30:24 +0900977 return useSecurityGroup;
978 }
979
980 private boolean isRelevantHelper(OpenstackNetworkEvent event) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900981 if (event.securityGroupId() == null ||
982 securityGroupService.securityGroup(event.securityGroupId()) == null) {
983 return false;
984 }
Jian Li2b9838c2018-10-28 17:09:42 +0900985
986 InstancePort instPort = instancePortService.instancePort(event.port().getId());
987
988 if (instPort == null) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900989 return false;
990 }
Jian Li2b9838c2018-10-28 17:09:42 +0900991
Jian Li34220ea2018-11-14 01:30:24 +0900992 return mastershipService.isLocalMaster(instPort.deviceId());
sangho6a9ff0d2017-03-27 11:23:37 +0900993 }
994
995 @Override
996 public void event(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +0800997 log.debug("security group event received {}", event);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900998
sangho6a9ff0d2017-03-27 11:23:37 +0900999 switch (event.type()) {
Hyunsun Moonae51e732017-04-25 17:46:21 +09001000 case OPENSTACK_PORT_SECURITY_GROUP_ADDED:
Jian Li4d138702018-11-27 17:25:28 +09001001 eventExecutor.execute(() -> processPortSgAdd(event));
sangho6a9ff0d2017-03-27 11:23:37 +09001002 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +09001003 case OPENSTACK_PORT_SECURITY_GROUP_REMOVED:
Jian Li4d138702018-11-27 17:25:28 +09001004 eventExecutor.execute(() -> processPortSgRemove(event));
sangho6a9ff0d2017-03-27 11:23:37 +09001005 break;
1006 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +09001007 // do nothing for the other events
sangho6a9ff0d2017-03-27 11:23:37 +09001008 break;
1009 }
1010 }
Jian Li4d138702018-11-27 17:25:28 +09001011
1012 private void processPortSgAdd(OpenstackNetworkEvent event) {
1013 if (!isRelevantHelper(event)) {
1014 return;
1015 }
1016
1017 InstancePort instPort = instancePortService.instancePort(event.port().getId());
1018 SecurityGroup osSg = securityGroupService.securityGroup(event.securityGroupId());
1019
1020 osSg.getRules().forEach(sgRule -> {
1021 updateSecurityGroupRule(instPort, event.port(), sgRule, true);
1022 });
1023 log.info("Added security group {} to port {}",
1024 event.securityGroupId(), event.port().getId());
1025 }
1026
1027 private void processPortSgRemove(OpenstackNetworkEvent event) {
1028 if (!isRelevantHelper(event)) {
1029 return;
1030 }
1031
1032 InstancePort instPort = instancePortService.instancePort(event.port().getId());
1033 SecurityGroup osSg = securityGroupService.securityGroup(event.securityGroupId());
1034
1035 osSg.getRules().forEach(sgRule -> {
1036 updateSecurityGroupRule(instPort, event.port(), sgRule, false);
1037 });
1038 log.info("Removed security group {} from port {}",
1039 event.securityGroupId(), event.port().getId());
1040 }
sangho6a9ff0d2017-03-27 11:23:37 +09001041 }
1042
Jian Li34220ea2018-11-14 01:30:24 +09001043 private class InternalSecurityGroupListener implements OpenstackSecurityGroupListener {
sangho6a9ff0d2017-03-27 11:23:37 +09001044
1045 @Override
sangho0248ca22017-05-31 13:22:47 +09001046 public boolean isRelevant(OpenstackSecurityGroupEvent event) {
Jian Libcc42282018-09-13 20:59:34 +09001047 return useSecurityGroup;
sangho0248ca22017-05-31 13:22:47 +09001048 }
1049
Jian Li34220ea2018-11-14 01:30:24 +09001050 private boolean isRelevantHelper() {
1051 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
1052 }
1053
sangho0248ca22017-05-31 13:22:47 +09001054 @Override
sangho6a9ff0d2017-03-27 11:23:37 +09001055 public void event(OpenstackSecurityGroupEvent event) {
1056 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +09001057 case OPENSTACK_SECURITY_GROUP_RULE_CREATED:
Jian Li4d138702018-11-27 17:25:28 +09001058 eventExecutor.execute(() -> processSgRuleCreate(event));
sangho6a9ff0d2017-03-27 11:23:37 +09001059 break;
sangho6a9ff0d2017-03-27 11:23:37 +09001060 case OPENSTACK_SECURITY_GROUP_RULE_REMOVED:
Jian Li4d138702018-11-27 17:25:28 +09001061 eventExecutor.execute(() -> processSgRuleRemove(event));
sangho6a9ff0d2017-03-27 11:23:37 +09001062 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +09001063 case OPENSTACK_SECURITY_GROUP_REMOVED:
Jian Li9d35bd62018-10-13 01:43:24 +09001064 case OPENSTACK_SECURITY_GROUP_CREATED:
sangho6a9ff0d2017-03-27 11:23:37 +09001065 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +09001066 // do nothing
1067 break;
sangho6a9ff0d2017-03-27 11:23:37 +09001068 }
1069 }
Jian Li4d138702018-11-27 17:25:28 +09001070
1071 private void processSgRuleCreate(OpenstackSecurityGroupEvent event) {
1072 if (!isRelevantHelper()) {
1073 return;
1074 }
1075
1076 SecurityGroupRule sgRuleToAdd = event.securityGroupRule();
1077 securityGroupRuleAdded(sgRuleToAdd);
1078 log.info("Applied new security group rule {} to ports", sgRuleToAdd.getId());
1079 }
1080
1081 private void processSgRuleRemove(OpenstackSecurityGroupEvent event) {
1082 if (!isRelevantHelper()) {
1083 return;
1084 }
1085
1086 SecurityGroupRule sgRuleToRemove = event.securityGroupRule();
1087 securityGroupRuleRemoved(sgRuleToRemove);
1088 log.info("Removed security group rule {} from ports", sgRuleToRemove.getId());
1089 }
sangho6a9ff0d2017-03-27 11:23:37 +09001090 }
sangho1aaa7882017-05-31 13:22:47 +09001091
1092 private class InternalNodeListener implements OpenstackNodeListener {
1093
1094 @Override
1095 public boolean isRelevant(OpenstackNodeEvent event) {
sangho1aaa7882017-05-31 13:22:47 +09001096 return event.subject().type() == COMPUTE;
1097 }
1098
Jian Li34220ea2018-11-14 01:30:24 +09001099 private boolean isRelevantHelper() {
1100 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
1101 }
1102
sangho1aaa7882017-05-31 13:22:47 +09001103 @Override
1104 public void event(OpenstackNodeEvent event) {
sangho1aaa7882017-05-31 13:22:47 +09001105 switch (event.type()) {
1106 case OPENSTACK_NODE_COMPLETE:
Jian Li4d138702018-11-27 17:25:28 +09001107 eventExecutor.execute(this::processNodeComplete);
sangho1aaa7882017-05-31 13:22:47 +09001108 break;
1109 case OPENSTACK_NODE_CREATED:
1110 case OPENSTACK_NODE_REMOVED:
1111 case OPENSTACK_NODE_UPDATED:
1112 case OPENSTACK_NODE_INCOMPLETE:
1113 default:
1114 break;
1115 }
1116 }
Jian Li4d138702018-11-27 17:25:28 +09001117
1118 private void processNodeComplete() {
1119 if (!isRelevantHelper()) {
1120 return;
1121 }
1122
1123 OpenstackSecurityGroupHandler.this.resetSecurityGroupRules();
1124 }
sangho1aaa7882017-05-31 13:22:47 +09001125 }
Jian Libcc42282018-09-13 20:59:34 +09001126}