blob: 833027641d0cac5418950c2710322d89e0f68806 [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;
Jian Li857f1b02019-09-20 16:58:19 +090039import org.onosproject.net.device.DeviceService;
sangho1aaa7882017-05-31 13:22:47 +090040import org.onosproject.net.driver.DriverService;
sangho6a9ff0d2017-03-27 11:23:37 +090041import org.onosproject.net.flow.DefaultTrafficSelector;
42import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.TrafficSelector;
sangho1aaa7882017-05-31 13:22:47 +090044import org.onosproject.net.flow.TrafficTreatment;
45import org.onosproject.net.flow.criteria.ExtensionSelector;
Jian Li28ec77f2018-10-31 07:07:25 +090046import org.onosproject.net.flow.instructions.ExtensionTreatment;
sangho6a9ff0d2017-03-27 11:23:37 +090047import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li9d35bd62018-10-13 01:43:24 +090048import org.onosproject.openstacknetworking.api.InstancePortAdminService;
sangho6a9ff0d2017-03-27 11:23:37 +090049import org.onosproject.openstacknetworking.api.InstancePortEvent;
50import org.onosproject.openstacknetworking.api.InstancePortListener;
sanghodc375372017-06-08 10:41:30 +090051import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
SONA Project6bc5c4a2018-12-14 23:49:52 +090052import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type;
sangho6a9ff0d2017-03-27 11:23:37 +090053import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
54import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
55import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
56import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupEvent;
57import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupListener;
58import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
Jian Li26949762018-03-30 15:46:37 +090059import org.onosproject.openstacknetworking.util.RulePopulatorUtil;
sangho1aaa7882017-05-31 13:22:47 +090060import org.onosproject.openstacknode.api.OpenstackNodeEvent;
61import org.onosproject.openstacknode.api.OpenstackNodeListener;
sangho3dd2a8b2017-07-19 15:54:31 +090062import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Liac30e272018-10-18 23:08:03 +090063import org.onosproject.store.serializers.KryoNamespaces;
64import org.onosproject.store.service.ConsistentMap;
65import org.onosproject.store.service.Serializer;
66import org.onosproject.store.service.StorageService;
Jian Li1e9cb732018-11-25 23:17:21 +090067import org.openstack4j.model.network.Network;
sangho6a9ff0d2017-03-27 11:23:37 +090068import org.openstack4j.model.network.Port;
69import org.openstack4j.model.network.SecurityGroup;
70import org.openstack4j.model.network.SecurityGroupRule;
Jian Liac30e272018-10-18 23:08:03 +090071import org.openstack4j.model.network.State;
72import org.openstack4j.openstack.networking.domain.NeutronAllowedAddressPair;
73import org.openstack4j.openstack.networking.domain.NeutronExtraDhcpOptCreate;
74import org.openstack4j.openstack.networking.domain.NeutronIP;
75import org.openstack4j.openstack.networking.domain.NeutronPort;
sangho6a9ff0d2017-03-27 11:23:37 +090076import org.openstack4j.openstack.networking.domain.NeutronSecurityGroupRule;
sangho0248ca22017-05-31 13:22:47 +090077import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070078import org.osgi.service.component.annotations.Activate;
79import org.osgi.service.component.annotations.Component;
80import org.osgi.service.component.annotations.Deactivate;
81import org.osgi.service.component.annotations.Modified;
82import org.osgi.service.component.annotations.Reference;
83import org.osgi.service.component.annotations.ReferenceCardinality;
sangho6a9ff0d2017-03-27 11:23:37 +090084import org.slf4j.Logger;
85
sangho6a9ff0d2017-03-27 11:23:37 +090086import java.util.Collections;
sangho0248ca22017-05-31 13:22:47 +090087import java.util.Dictionary;
Jian Liac30e272018-10-18 23:08:03 +090088import java.util.HashSet;
89import java.util.LinkedHashMap;
Jian Li362f9fd2018-11-28 11:14:40 +090090import java.util.List;
sangho2e97be02017-07-03 18:18:27 +090091import java.util.Map;
sangho6a9ff0d2017-03-27 11:23:37 +090092import java.util.Objects;
93import java.util.Set;
94import java.util.concurrent.ExecutorService;
95import java.util.stream.Collectors;
96
97import static java.util.concurrent.Executors.newSingleThreadExecutor;
98import static org.onlab.util.Tools.groupedThreads;
Jian Li1e9cb732018-11-25 23:17:21 +090099import static org.onosproject.openstacknetworking.api.Constants.ACL_EGRESS_TABLE;
100import static org.onosproject.openstacknetworking.api.Constants.ACL_INGRESS_TABLE;
101import static org.onosproject.openstacknetworking.api.Constants.ACL_RECIRC_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900102import static org.onosproject.openstacknetworking.api.Constants.CT_TABLE;
103import static org.onosproject.openstacknetworking.api.Constants.ERROR_TABLE;
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700104import static org.onosproject.openstacknetworking.api.Constants.JUMP_TABLE;
sangho6a9ff0d2017-03-27 11:23:37 +0900105import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Li1e9cb732018-11-25 23:17:21 +0900106import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ACL_INGRESS_RULE;
sangho6a9ff0d2017-03-27 11:23:37 +0900107import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ACL_RULE;
sangho1aaa7882017-05-31 13:22:47 +0900108import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_DROP_RULE;
109import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_HOOK_RULE;
110import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_RULE;
Jian Li621f73c2018-12-15 01:49:22 +0900111import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.GENEVE;
SONA Project6bc5c4a2018-12-14 23:49:52 +0900112import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.GRE;
113import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.VLAN;
114import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.VXLAN;
Jian Li4d138702018-11-27 17:25:28 +0900115import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_PRE_REMOVE;
Ray Milkey8e406512018-10-24 15:56:50 -0700116import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.USE_SECURITY_GROUP;
117import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.USE_SECURITY_GROUP_DEFAULT;
Jian Lib8cdcc12018-10-23 01:53:10 +0900118import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Libcc42282018-09-13 20:59:34 +0900119import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtMaskFlag;
120import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtStateFlag;
121import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.niciraConnTrackTreatmentBuilder;
sangho1aaa7882017-05-31 13:22:47 +0900122import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
sangho6a9ff0d2017-03-27 11:23:37 +0900123import static org.slf4j.LoggerFactory.getLogger;
124
125/**
126 * Populates flow rules to handle OpenStack SecurityGroups.
127 */
Ray Milkey8e406512018-10-24 15:56:50 -0700128@Component(
129 immediate = true,
130 property = {
131 USE_SECURITY_GROUP + ":Boolean=" + USE_SECURITY_GROUP_DEFAULT
132 }
133)
sangho6a9ff0d2017-03-27 11:23:37 +0900134public class OpenstackSecurityGroupHandler {
135
136 private final Logger log = getLogger(getClass());
137
Jian Li2b9838c2018-10-28 17:09:42 +0900138 private static final int VM_IP_PREFIX = 32;
139
Jian Li4d138702018-11-27 17:25:28 +0900140 private static final String STR_ZERO = "0";
141 private static final String STR_ONE = "1";
142 private static final String STR_NULL = "null";
143 private static final String STR_PADDING = "0000000000000000";
144 private static final int MASK_BEGIN_IDX = 0;
145 private static final int MASK_MAX_IDX = 16;
146 private static final int MASK_RADIX = 2;
147 private static final int PORT_RADIX = 16;
148
Jian Li900b7232018-12-14 14:04:51 +0900149 /** Apply OpenStack security group rule for VM traffic. */
Ray Milkey8e406512018-10-24 15:56:50 -0700150 private boolean useSecurityGroup = USE_SECURITY_GROUP_DEFAULT;
sangho0248ca22017-05-31 13:22:47 +0900151
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700152 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho6a9ff0d2017-03-27 11:23:37 +0900153 protected CoreService coreService;
154
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li9d35bd62018-10-13 01:43:24 +0900156 protected InstancePortAdminService instancePortService;
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 MastershipService mastershipService;
160
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moonae51e732017-04-25 17:46:21 +0900162 protected OpenstackNetworkService osNetService;
sangho6a9ff0d2017-03-27 11:23:37 +0900163
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho6a9ff0d2017-03-27 11:23:37 +0900165 protected OpenstackSecurityGroupService securityGroupService;
166
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sanghodc375372017-06-08 10:41:30 +0900168 protected OpenstackFlowRuleService osFlowRuleService;
sangho6a9ff0d2017-03-27 11:23:37 +0900169
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho0248ca22017-05-31 13:22:47 +0900171 protected ComponentConfigService configService;
172
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho3dd2a8b2017-07-19 15:54:31 +0900174 protected OpenstackNodeService osNodeService;
175
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho1aaa7882017-05-31 13:22:47 +0900177 protected DriverService driverService;
178
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700179 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li857f1b02019-09-20 16:58:19 +0900180 protected DeviceService deviceService;
181
182 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho1aaa7882017-05-31 13:22:47 +0900183 protected LeadershipService leadershipService;
184
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700185 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho1aaa7882017-05-31 13:22:47 +0900186 protected ClusterService clusterService;
187
Ray Milkey0b18b722018-10-16 13:19:15 -0700188 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Liac30e272018-10-18 23:08:03 +0900189 protected StorageService storageService;
190
191 private static final KryoNamespace SERIALIZER_PORT = KryoNamespace.newBuilder()
192 .register(KryoNamespaces.API)
193 .register(Port.class)
194 .register(NeutronPort.class)
195 .register(NeutronIP.class)
196 .register(State.class)
197 .register(NeutronAllowedAddressPair.class)
198 .register(NeutronExtraDhcpOptCreate.class)
199 .register(LinkedHashMap.class)
200 .build();
Jian Li9d35bd62018-10-13 01:43:24 +0900201
Jian Libcc42282018-09-13 20:59:34 +0900202 private final InstancePortListener instancePortListener =
Jian Lia70fdb602018-11-05 01:32:22 +0900203 new InternalInstancePortListener();
Jian Libcc42282018-09-13 20:59:34 +0900204 private final OpenstackNetworkListener osNetworkListener =
Jian Lia70fdb602018-11-05 01:32:22 +0900205 new InternalOpenstackNetworkListener();
Jian Libcc42282018-09-13 20:59:34 +0900206 private final OpenstackNetworkListener osPortListener =
Jian Lia70fdb602018-11-05 01:32:22 +0900207 new InternalOpenstackPortListener();
Jian Libcc42282018-09-13 20:59:34 +0900208 private final OpenstackSecurityGroupListener securityGroupListener =
Jian Lia70fdb602018-11-05 01:32:22 +0900209 new InternalSecurityGroupListener();
sangho1aaa7882017-05-31 13:22:47 +0900210 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
Jian Liac30e272018-10-18 23:08:03 +0900211
212 private ConsistentMap<String, Port> removedOsPortStore;
213
sangho6a9ff0d2017-03-27 11:23:37 +0900214 private ApplicationId appId;
sangho1aaa7882017-05-31 13:22:47 +0900215 private NodeId localNodeId;
sangho6a9ff0d2017-03-27 11:23:37 +0900216
217 private final ExecutorService eventExecutor = newSingleThreadExecutor(
218 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
219
220 private static final String PROTO_ICMP = "ICMP";
221 private static final String PROTO_TCP = "TCP";
222 private static final String PROTO_UDP = "UDP";
223 private static final String ETHTYPE_IPV4 = "IPV4";
224 private static final String EGRESS = "EGRESS";
225 private static final String INGRESS = "INGRESS";
226 private static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
227
sangho1aaa7882017-05-31 13:22:47 +0900228 // We expose pipeline structure to SONA application considering removing pipeline soon.
sanghoshinbbeb31a2018-09-11 17:01:01 +0800229 private static final int GOTO_CONNTRACK_TABLE = CT_TABLE;
230 private static final int GOTO_JUMP_TABLE = JUMP_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900231
232 private static final int CT_COMMIT = 0;
233 private static final int CT_NO_COMMIT = 1;
234 private static final short CT_NO_RECIRC = -1;
235
236 private static final int ACTION_NONE = 0;
237 private static final int ACTION_DROP = -1;
238
sangho6a9ff0d2017-03-27 11:23:37 +0900239 @Activate
240 protected void activate() {
241 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
sangho1aaa7882017-05-31 13:22:47 +0900242 localNodeId = clusterService.getLocalNode().id();
sangho6a9ff0d2017-03-27 11:23:37 +0900243 instancePortService.addListener(instancePortListener);
244 securityGroupService.addListener(securityGroupListener);
Jian Libcc42282018-09-13 20:59:34 +0900245 osNetService.addListener(osPortListener);
246 osNetService.addListener(osNetworkListener);
sangho0248ca22017-05-31 13:22:47 +0900247 configService.registerProperties(getClass());
sangho1aaa7882017-05-31 13:22:47 +0900248 osNodeService.addListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900249
Jian Liac30e272018-10-18 23:08:03 +0900250 removedOsPortStore = storageService.<String, Port>consistentMapBuilder()
251 .withSerializer(Serializer.using(SERIALIZER_PORT))
252 .withName("openstack-removed-portstore")
253 .withApplicationId(appId)
254 .build();
255
sangho6a9ff0d2017-03-27 11:23:37 +0900256 log.info("Started");
257 }
258
259 @Deactivate
260 protected void deactivate() {
261 instancePortService.removeListener(instancePortListener);
262 securityGroupService.removeListener(securityGroupListener);
Jian Libcc42282018-09-13 20:59:34 +0900263 osNetService.removeListener(osNetworkListener);
264 osNetService.removeListener(osPortListener);
sangho0248ca22017-05-31 13:22:47 +0900265 configService.unregisterProperties(getClass(), false);
sangho1aaa7882017-05-31 13:22:47 +0900266 osNodeService.removeListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900267 eventExecutor.shutdown();
268
269 log.info("Stopped");
270 }
271
sangho0248ca22017-05-31 13:22:47 +0900272 @Modified
273 protected void modified(ComponentContext context) {
274 Dictionary<?, ?> properties = context.getProperties();
275 Boolean flag;
276
Ray Milkey8e406512018-10-24 15:56:50 -0700277 flag = Tools.isPropertyEnabled(properties, USE_SECURITY_GROUP);
sangho0248ca22017-05-31 13:22:47 +0900278 if (flag == null) {
279 log.info("useSecurityGroup is not configured, " +
280 "using current value of {}", useSecurityGroup);
281 } else {
282 useSecurityGroup = flag;
283 log.info("Configured. useSecurityGroup is {}",
284 useSecurityGroup ? "enabled" : "disabled");
285 }
286
sanghoe6457a32017-08-24 14:31:19 +0900287 securityGroupService.setSecurityGroupEnabled(useSecurityGroup);
sangho0248ca22017-05-31 13:22:47 +0900288 resetSecurityGroupRules();
289 }
290
sangho1aaa7882017-05-31 13:22:47 +0900291 private void initializeConnTrackTable(DeviceId deviceId, boolean install) {
292
293 //table=1,ip,ct_state=-trk, actions=ct(table:2)
Jian Libcc42282018-09-13 20:59:34 +0900294 long ctState = computeCtStateFlag(false, false, false);
295 long ctMask = computeCtMaskFlag(true, false, false);
sangho1aaa7882017-05-31 13:22:47 +0900296 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, (short) GOTO_CONNTRACK_TABLE,
297 ACTION_NONE, PRIORITY_CT_HOOK_RULE, install);
298
299 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+est,action=goto_table:3
Jian Libcc42282018-09-13 20:59:34 +0900300 ctState = computeCtStateFlag(true, false, true);
301 ctMask = computeCtMaskFlag(true, false, true);
sangho1aaa7882017-05-31 13:22:47 +0900302 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
303 GOTO_JUMP_TABLE, PRIORITY_CT_RULE, install);
304
305 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+new,action=drop
Jian Libcc42282018-09-13 20:59:34 +0900306 ctState = computeCtStateFlag(true, true, false);
307 ctMask = computeCtMaskFlag(true, true, false);
sangho1aaa7882017-05-31 13:22:47 +0900308 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
309 ACTION_DROP, PRIORITY_CT_DROP_RULE, install);
310 }
311
Jian Li1e9cb732018-11-25 23:17:21 +0900312 private void initializeAclTable(DeviceId deviceId, boolean install) {
313
314 ExtensionTreatment ctTreatment =
315 niciraConnTrackTreatmentBuilder(driverService, deviceId)
316 .commit(true)
317 .build();
318
319 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
320 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
321
322 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
323 tBuilder.extension(ctTreatment, deviceId)
324 .transition(JUMP_TABLE);
325
326 osFlowRuleService.setRule(appId,
327 deviceId,
328 sBuilder.build(),
329 tBuilder.build(),
330 PRIORITY_ACL_INGRESS_RULE,
331 ACL_RECIRC_TABLE,
332 install);
333 }
334
335 private void initializeIngressTable(DeviceId deviceId, boolean install) {
336 if (install) {
337 osFlowRuleService.setUpTableMissEntry(deviceId, ACL_INGRESS_TABLE);
338 } else {
339 osFlowRuleService.connectTables(deviceId, ACL_INGRESS_TABLE, JUMP_TABLE);
340 }
341 }
342
Jian Libcc42282018-09-13 20:59:34 +0900343 private void updateSecurityGroupRule(InstancePort instPort, Port port,
344 SecurityGroupRule sgRule, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900345
Daniel Park3a140592018-06-28 18:33:10 +0900346 if (instPort == null || port == null || sgRule == null) {
347 return;
348 }
349
sangho6a9ff0d2017-03-27 11:23:37 +0900350 if (sgRule.getRemoteGroupId() != null && !sgRule.getRemoteGroupId().isEmpty()) {
Jian Lia70fdb602018-11-05 01:32:22 +0900351 getRemoteInstPorts(port, sgRule.getRemoteGroupId(), install)
Jian Libcc42282018-09-13 20:59:34 +0900352 .forEach(rInstPort -> {
Jian Li1e9cb732018-11-25 23:17:21 +0900353 populateSecurityGroupRule(sgRule, instPort,
Jian Libcc42282018-09-13 20:59:34 +0900354 rInstPort.ipAddress().toIpPrefix(), install);
Jian Li1e9cb732018-11-25 23:17:21 +0900355 populateSecurityGroupRule(sgRule, rInstPort,
Jian Libcc42282018-09-13 20:59:34 +0900356 instPort.ipAddress().toIpPrefix(), install);
sangho6a9ff0d2017-03-27 11:23:37 +0900357
Jian Libcc42282018-09-13 20:59:34 +0900358 SecurityGroupRule rSgRule =
359 new NeutronSecurityGroupRule
360 .SecurityGroupRuleConcreteBuilder()
Jian Li28ec77f2018-10-31 07:07:25 +0900361 .from(sgRule)
Jian Li4d138702018-11-27 17:25:28 +0900362 .direction(sgRule.getDirection()
363 .equalsIgnoreCase(EGRESS) ? INGRESS : EGRESS)
Jian Li28ec77f2018-10-31 07:07:25 +0900364 .build();
Jian Li1e9cb732018-11-25 23:17:21 +0900365 populateSecurityGroupRule(rSgRule, instPort,
Jian Libcc42282018-09-13 20:59:34 +0900366 rInstPort.ipAddress().toIpPrefix(), install);
Jian Li1e9cb732018-11-25 23:17:21 +0900367 populateSecurityGroupRule(rSgRule, rInstPort,
Jian Libcc42282018-09-13 20:59:34 +0900368 instPort.ipAddress().toIpPrefix(), install);
369 });
sangho6a9ff0d2017-03-27 11:23:37 +0900370 } else {
Jian Li1e9cb732018-11-25 23:17:21 +0900371 populateSecurityGroupRule(sgRule, instPort,
Jian Libcc42282018-09-13 20:59:34 +0900372 sgRule.getRemoteIpPrefix() == null ? IP_PREFIX_ANY :
Jian Li28ec77f2018-10-31 07:07:25 +0900373 IpPrefix.valueOf(sgRule.getRemoteIpPrefix()), install);
sangho6a9ff0d2017-03-27 11:23:37 +0900374 }
375 }
376
Jian Libcc42282018-09-13 20:59:34 +0900377 private void populateSecurityGroupRule(SecurityGroupRule sgRule,
378 InstancePort instPort,
Jian Lie8b28db2018-10-17 14:04:09 +0900379 IpPrefix remoteIp,
380 boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900381 Set<TrafficSelector> selectors = buildSelectors(sgRule,
Jian Li1e9cb732018-11-25 23:17:21 +0900382 Ip4Address.valueOf(instPort.ipAddress().toInetAddress()),
383 remoteIp, instPort.networkId());
sangho2e97be02017-07-03 18:18:27 +0900384 if (selectors == null || selectors.isEmpty()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900385 return;
386 }
387
Jian Li857f1b02019-09-20 16:58:19 +0900388 // if the device is not available we do not perform any action
389 if (instPort.deviceId() == null || !deviceService.isAvailable(instPort.deviceId())) {
390 return;
391 }
392
Jian Li362f9fd2018-11-28 11:14:40 +0900393 // in case a port is bound to multiple security groups, we do NOT remove
394 // egress rules unless all security groups bound to the port to be removed
395 Port osPort = osNetService.port(instPort.portId());
396 if (!install && osPort != null && sgRule.getDirection().equalsIgnoreCase(EGRESS)) {
397 List<String> sgIds = osPort.getSecurityGroups();
398 if (!sgIds.contains(sgRule.getSecurityGroupId()) && !sgIds.isEmpty()) {
399 return;
400 }
401 }
402
Jian Li28ec77f2018-10-31 07:07:25 +0900403 // XXX All egress traffic needs to go through connection tracking module,
404 // which might hurt its performance.
405 ExtensionTreatment ctTreatment =
406 niciraConnTrackTreatmentBuilder(driverService, instPort.deviceId())
407 .commit(true)
408 .build();
409
Jian Li1e9cb732018-11-25 23:17:21 +0900410 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Jian Li28ec77f2018-10-31 07:07:25 +0900411
Jian Li1e9cb732018-11-25 23:17:21 +0900412 int aclTable;
Jian Li4d138702018-11-27 17:25:28 +0900413 if (sgRule.getDirection().equalsIgnoreCase(EGRESS)) {
Jian Li1e9cb732018-11-25 23:17:21 +0900414 aclTable = ACL_EGRESS_TABLE;
415 tBuilder.transition(ACL_RECIRC_TABLE);
416 } else {
417 aclTable = ACL_INGRESS_TABLE;
418 tBuilder.extension(ctTreatment, instPort.deviceId())
419 .transition(JUMP_TABLE);
420 }
421
422 int finalAclTable = aclTable;
423 selectors.forEach(selector -> {
424 osFlowRuleService.setRule(appId,
425 instPort.deviceId(),
426 selector, tBuilder.build(),
427 PRIORITY_ACL_RULE,
428 finalAclTable,
429 install);
430 });
sangho6a9ff0d2017-03-27 11:23:37 +0900431 }
432
433 /**
sangho1aaa7882017-05-31 13:22:47 +0900434 * Sets connection tracking rule using OVS extension commands.
Jian Li2b9838c2018-10-28 17:09:42 +0900435 * It is not so graceful, but I don't want to make it more general because
436 * it is going to be used only here.
437 * The following is the usage of the function.
sangho1aaa7882017-05-31 13:22:47 +0900438 *
439 * @param deviceId Device ID
Jian Li2b9838c2018-10-28 17:09:42 +0900440 * @param ctState ctState: please use RulePopulatorUtil.computeCtStateFlag()
441 * to build the value
442 * @param ctMask crMask: please use RulePopulatorUtil.computeCtMaskFlag()
443 * to build the value
sangho1aaa7882017-05-31 13:22:47 +0900444 * @param commit CT_COMMIT for commit action, CT_NO_COMMIT otherwise
Jian Li2b9838c2018-10-28 17:09:42 +0900445 * @param recircTable table number for recirculation after CT actions.
446 * CT_NO_RECIRC with no recirculation
447 * @param action Additional actions. ACTION_DROP, ACTION_NONE,
448 * GOTO_XXX_TABLE are supported.
sangho1aaa7882017-05-31 13:22:47 +0900449 * @param priority priority value for the rule
450 * @param install true for insertion, false for removal
451 */
452 private void setConnTrackRule(DeviceId deviceId, long ctState, long ctMask,
453 int commit, short recircTable,
454 int action, int priority, boolean install) {
455
Jian Libcc42282018-09-13 20:59:34 +0900456 ExtensionSelector esCtSate = RulePopulatorUtil
457 .buildCtExtensionSelector(driverService, deviceId, ctState, ctMask);
sangho1aaa7882017-05-31 13:22:47 +0900458 TrafficSelector selector = DefaultTrafficSelector.builder()
459 .extension(esCtSate, deviceId)
460 .matchEthType(Ethernet.TYPE_IPV4)
461 .build();
462
463 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
464
465 if (commit == CT_COMMIT || recircTable > 0) {
Jian Li5a26ab32019-03-21 15:20:01 +0900466 RulePopulatorUtil.NiciraConnTrackTreatmentBuilder natTreatmentBuilder =
Jian Libcc42282018-09-13 20:59:34 +0900467 niciraConnTrackTreatmentBuilder(driverService, deviceId);
sangho1aaa7882017-05-31 13:22:47 +0900468 natTreatmentBuilder.natAction(false);
469 if (commit == CT_COMMIT) {
470 natTreatmentBuilder.commit(true);
471 } else {
472 natTreatmentBuilder.commit(false);
473 }
474 if (recircTable > 0) {
475 natTreatmentBuilder.table(recircTable);
476 }
477 tb.extension(natTreatmentBuilder.build(), deviceId);
478 } else if (action == ACTION_DROP) {
479 tb.drop();
480 }
481
sanghoshinbbeb31a2018-09-11 17:01:01 +0800482 if (action != ACTION_NONE && action != ACTION_DROP) {
sangho1aaa7882017-05-31 13:22:47 +0900483 tb.transition(action);
484 }
485
486 int tableType = ERROR_TABLE;
487 if (priority == PRIORITY_CT_RULE || priority == PRIORITY_CT_DROP_RULE) {
488 tableType = CT_TABLE;
489 } else if (priority == PRIORITY_CT_HOOK_RULE) {
Jian Li1e9cb732018-11-25 23:17:21 +0900490 tableType = ACL_INGRESS_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900491 } else {
492 log.error("Cannot an appropriate table for the conn track rule.");
493 }
494
495 osFlowRuleService.setRule(
496 appId,
497 deviceId,
498 selector,
499 tb.build(),
500 priority,
501 tableType,
502 install);
503 }
504
505 /**
sangho6a9ff0d2017-03-27 11:23:37 +0900506 * Returns a set of host IP addresses engaged with supplied security group ID.
507 * It only searches a VM in the same tenant boundary.
508 *
Jian Lia70fdb602018-11-05 01:32:22 +0900509 * @param srcPort openstack port
sangho6a9ff0d2017-03-27 11:23:37 +0900510 * @param sgId security group id
511 * @return set of ip addresses
512 */
Jian Lia70fdb602018-11-05 01:32:22 +0900513 private Set<InstancePort> getRemoteInstPorts(Port srcPort,
Jian Li2b9838c2018-10-28 17:09:42 +0900514 String sgId, boolean install) {
sangho6a9ff0d2017-03-27 11:23:37 +0900515 Set<InstancePort> remoteInstPorts;
516
Jian Liac30e272018-10-18 23:08:03 +0900517 Set<Port> removedPorts = Sets.newConcurrentHashSet();
518
519 if (!install) {
520 removedPorts = new HashSet<>(removedOsPortStore.asJavaMap().values());
521 }
522
523 remoteInstPorts = Sets.union(osNetService.ports(), removedPorts).stream()
Jian Lia70fdb602018-11-05 01:32:22 +0900524 .filter(port -> !port.getId().equals(srcPort.getId()))
525 .filter(port -> port.getTenantId().equals(srcPort.getTenantId()))
sangho6a9ff0d2017-03-27 11:23:37 +0900526 .filter(port -> port.getSecurityGroups().contains(sgId))
Jian Lia70fdb602018-11-05 01:32:22 +0900527 .filter(port -> port.getNetworkId().equals(srcPort.getNetworkId()))
sangho6a9ff0d2017-03-27 11:23:37 +0900528 .map(port -> instancePortService.instancePort(port.getId()))
529 .filter(instPort -> instPort != null && instPort.ipAddress() != null)
530 .collect(Collectors.toSet());
531
532 return Collections.unmodifiableSet(remoteInstPorts);
533 }
534
sangho2e97be02017-07-03 18:18:27 +0900535 private Set<TrafficSelector> buildSelectors(SecurityGroupRule sgRule,
536 Ip4Address vmIp,
Jian Lie8b28db2018-10-17 14:04:09 +0900537 IpPrefix remoteIp,
Jian Li1e9cb732018-11-25 23:17:21 +0900538 String netId) {
Jian Li2b9838c2018-10-28 17:09:42 +0900539 if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, VM_IP_PREFIX))) {
sangho2e97be02017-07-03 18:18:27 +0900540 // do nothing if the remote IP is my IP
541 return null;
542 }
543
544 Set<TrafficSelector> selectorSet = Sets.newHashSet();
545
546 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
Jian Li1e9cb732018-11-25 23:17:21 +0900547 buildMatches(sBuilder, sgRule, vmIp, remoteIp, netId);
sangho2e97be02017-07-03 18:18:27 +0900548
549 if (sgRule.getPortRangeMax() != null && sgRule.getPortRangeMin() != null &&
550 sgRule.getPortRangeMin() < sgRule.getPortRangeMax()) {
Jian Libcc42282018-09-13 20:59:34 +0900551 Map<TpPort, TpPort> portRangeMatchMap =
552 buildPortRangeMatches(sgRule.getPortRangeMin(),
Jian Li28ec77f2018-10-31 07:07:25 +0900553 sgRule.getPortRangeMax());
Jian Li2b9838c2018-10-28 17:09:42 +0900554 portRangeMatchMap.forEach((key, value) -> {
sangho2e97be02017-07-03 18:18:27 +0900555
Jian Li4d138702018-11-27 17:25:28 +0900556 if (sgRule.getProtocol().equalsIgnoreCase(PROTO_TCP)) {
557 if (sgRule.getDirection().equalsIgnoreCase(EGRESS)) {
Jian Li2b9838c2018-10-28 17:09:42 +0900558 sBuilder.matchTcpSrcMasked(key, value);
559 } else {
560 sBuilder.matchTcpDstMasked(key, value);
Jian Libcc42282018-09-13 20:59:34 +0900561 }
Jian Li4d138702018-11-27 17:25:28 +0900562 } else if (sgRule.getProtocol().equalsIgnoreCase(PROTO_UDP)) {
563 if (sgRule.getDirection().equalsIgnoreCase(EGRESS)) {
Jian Li2b9838c2018-10-28 17:09:42 +0900564 sBuilder.matchUdpSrcMasked(key, value);
565 } else {
566 sBuilder.matchUdpDstMasked(key, value);
567 }
568 }
569
570 selectorSet.add(sBuilder.build());
571 });
sangho2e97be02017-07-03 18:18:27 +0900572 } else {
573 selectorSet.add(sBuilder.build());
574 }
575
576 return selectorSet;
577 }
578
Jian Libcc42282018-09-13 20:59:34 +0900579 private void buildMatches(TrafficSelector.Builder sBuilder,
Jian Li1e9cb732018-11-25 23:17:21 +0900580 SecurityGroupRule sgRule, Ip4Address vmIp,
581 IpPrefix remoteIp, String netId) {
582 buildTunnelId(sBuilder, netId);
sangho6a9ff0d2017-03-27 11:23:37 +0900583 buildMatchEthType(sBuilder, sgRule.getEtherType());
584 buildMatchDirection(sBuilder, sgRule.getDirection(), vmIp);
585 buildMatchProto(sBuilder, sgRule.getProtocol());
586 buildMatchPort(sBuilder, sgRule.getProtocol(), sgRule.getDirection(),
sangho2e97be02017-07-03 18:18:27 +0900587 sgRule.getPortRangeMin() == null ? 0 : sgRule.getPortRangeMin(),
588 sgRule.getPortRangeMax() == null ? 0 : sgRule.getPortRangeMax());
sangho6a9ff0d2017-03-27 11:23:37 +0900589 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
sangho6a9ff0d2017-03-27 11:23:37 +0900590 }
591
Jian Li1e9cb732018-11-25 23:17:21 +0900592 private void buildTunnelId(TrafficSelector.Builder sBuilder, String netId) {
593 String segId = osNetService.segmentId(netId);
SONA Project6bc5c4a2018-12-14 23:49:52 +0900594 Type netType = osNetService.networkType(netId);
Jian Lie8b28db2018-10-17 14:04:09 +0900595
SONA Project6bc5c4a2018-12-14 23:49:52 +0900596 if (netType == VLAN) {
Jian Lie8b28db2018-10-17 14:04:09 +0900597 sBuilder.matchVlanId(VlanId.vlanId(segId));
Jian Li621f73c2018-12-15 01:49:22 +0900598 } else if (netType == VXLAN || netType == GRE || netType == GENEVE) {
Jian Lie8b28db2018-10-17 14:04:09 +0900599 sBuilder.matchTunnelId(Long.valueOf(segId));
Jian Li28ec77f2018-10-31 07:07:25 +0900600 } else {
Jian Li2dc37592019-03-14 17:32:15 +0900601 log.debug("Cannot tag the VID due to lack of support of virtual network type {}", netType);
Jian Lie8b28db2018-10-17 14:04:09 +0900602 }
603 }
604
sangho6a9ff0d2017-03-27 11:23:37 +0900605 private void buildMatchDirection(TrafficSelector.Builder sBuilder,
606 String direction,
607 Ip4Address vmIp) {
Jian Li4d138702018-11-27 17:25:28 +0900608 if (direction.equalsIgnoreCase(EGRESS)) {
Jian Li2b9838c2018-10-28 17:09:42 +0900609 sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, VM_IP_PREFIX));
sangho6a9ff0d2017-03-27 11:23:37 +0900610 } else {
Jian Li2b9838c2018-10-28 17:09:42 +0900611 sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, VM_IP_PREFIX));
sangho6a9ff0d2017-03-27 11:23:37 +0900612 }
613 }
614
615 private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
616 // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
617 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
Jian Li4d138702018-11-27 17:25:28 +0900618 if (etherType != null && !Objects.equals(etherType, STR_NULL) &&
619 !etherType.equalsIgnoreCase(ETHTYPE_IPV4)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900620 log.debug("EthType {} is not supported yet in Security Group", etherType);
621 }
622 }
623
Jian Libcc42282018-09-13 20:59:34 +0900624 private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder,
625 IpPrefix remoteIpPrefix, String direction) {
626 if (remoteIpPrefix != null &&
627 !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
Jian Li4d138702018-11-27 17:25:28 +0900628 if (direction.equalsIgnoreCase(EGRESS)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900629 sBuilder.matchIPDst(remoteIpPrefix);
630 } else {
631 sBuilder.matchIPSrc(remoteIpPrefix);
632 }
633 }
634 }
635
636 private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
637 if (protocol != null) {
638 switch (protocol.toUpperCase()) {
639 case PROTO_ICMP:
640 sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
641 break;
642 case PROTO_TCP:
643 sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
644 break;
645 case PROTO_UDP:
646 sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
647 break;
648 default:
649 }
650 }
651 }
652
Jian Libcc42282018-09-13 20:59:34 +0900653 private void buildMatchPort(TrafficSelector.Builder sBuilder,
654 String protocol, String direction,
sangho6a9ff0d2017-03-27 11:23:37 +0900655 int portMin, int portMax) {
656 if (portMin > 0 && portMax > 0 && portMin == portMax) {
Jian Li4d138702018-11-27 17:25:28 +0900657 if (protocol.equalsIgnoreCase(PROTO_TCP)) {
658 if (direction.equalsIgnoreCase(EGRESS)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900659 sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
660 } else {
661 sBuilder.matchTcpDst(TpPort.tpPort(portMax));
662 }
Jian Li4d138702018-11-27 17:25:28 +0900663 } else if (protocol.equalsIgnoreCase(PROTO_UDP)) {
664 if (direction.equalsIgnoreCase(EGRESS)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900665 sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
666 } else {
667 sBuilder.matchUdpDst(TpPort.tpPort(portMax));
668 }
669 }
670 }
671 }
672
sangho0248ca22017-05-31 13:22:47 +0900673 private void resetSecurityGroupRules() {
674
675 if (useSecurityGroup) {
Jian Lib6969502018-10-30 20:38:07 +0900676 osNodeService.completeNodes(COMPUTE).forEach(node -> {
Jian Li1e9cb732018-11-25 23:17:21 +0900677 osFlowRuleService.setUpTableMissEntry(node.intgBridge(), ACL_EGRESS_TABLE);
Jian Lib6969502018-10-30 20:38:07 +0900678 initializeConnTrackTable(node.intgBridge(), true);
Jian Li1e9cb732018-11-25 23:17:21 +0900679 initializeAclTable(node.intgBridge(), true);
680 initializeIngressTable(node.intgBridge(), true);
Jian Lib6969502018-10-30 20:38:07 +0900681 });
682
sangho0248ca22017-05-31 13:22:47 +0900683 securityGroupService.securityGroups().forEach(securityGroup ->
684 securityGroup.getRules().forEach(this::securityGroupRuleAdded));
685 } else {
Jian Lib6969502018-10-30 20:38:07 +0900686 osNodeService.completeNodes(COMPUTE).forEach(node -> {
Jian Li1e9cb732018-11-25 23:17:21 +0900687 osFlowRuleService.connectTables(node.intgBridge(), ACL_EGRESS_TABLE, JUMP_TABLE);
Jian Lib6969502018-10-30 20:38:07 +0900688 initializeConnTrackTable(node.intgBridge(), false);
Jian Li1e9cb732018-11-25 23:17:21 +0900689 initializeAclTable(node.intgBridge(), false);
690 initializeIngressTable(node.intgBridge(), false);
Jian Lib6969502018-10-30 20:38:07 +0900691 });
692
sangho0248ca22017-05-31 13:22:47 +0900693 securityGroupService.securityGroups().forEach(securityGroup ->
694 securityGroup.getRules().forEach(this::securityGroupRuleRemoved));
695 }
696
Jian Libcc42282018-09-13 20:59:34 +0900697 log.info("Reset security group info " +
Jian Li28ec77f2018-10-31 07:07:25 +0900698 (useSecurityGroup ? " with " : " without") + " Security Group");
sangho0248ca22017-05-31 13:22:47 +0900699 }
700
701 private void securityGroupRuleAdded(SecurityGroupRule sgRule) {
702 osNetService.ports().stream()
Jian Libcc42282018-09-13 20:59:34 +0900703 .filter(port -> port.getSecurityGroups()
Jian Li28ec77f2018-10-31 07:07:25 +0900704 .contains(sgRule.getSecurityGroupId()))
sangho0248ca22017-05-31 13:22:47 +0900705 .forEach(port -> {
706 updateSecurityGroupRule(
707 instancePortService.instancePort(port.getId()),
708 port, sgRule, true);
709 log.debug("Applied security group rule {} to port {}",
710 sgRule.getId(), port.getId());
711 });
712 }
713
714 private void securityGroupRuleRemoved(SecurityGroupRule sgRule) {
Jian Liac30e272018-10-18 23:08:03 +0900715 Set<Port> removedPorts = new HashSet<>(removedOsPortStore.asJavaMap().values());
716
717 Sets.union(osNetService.ports(), removedPorts).stream()
Jian Libcc42282018-09-13 20:59:34 +0900718 .filter(port -> port.getSecurityGroups()
Jian Li28ec77f2018-10-31 07:07:25 +0900719 .contains(sgRule.getSecurityGroupId()))
sangho0248ca22017-05-31 13:22:47 +0900720 .forEach(port -> {
721 updateSecurityGroupRule(
722 instancePortService.instancePort(port.getId()),
723 port, sgRule, false);
724 log.debug("Removed security group rule {} from port {}",
725 sgRule.getId(), port.getId());
726 });
727 }
728
sangho2e97be02017-07-03 18:18:27 +0900729 private int binLower(String binStr, int bits) {
Jian Li4d138702018-11-27 17:25:28 +0900730 StringBuilder outBin = new StringBuilder(
731 binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
sangho2e97be02017-07-03 18:18:27 +0900732 for (int i = 0; i < bits; i++) {
Jian Li4d138702018-11-27 17:25:28 +0900733 outBin.append(STR_ZERO);
sangho2e97be02017-07-03 18:18:27 +0900734 }
735
Jian Li4d138702018-11-27 17:25:28 +0900736 return Integer.parseInt(outBin.toString(), MASK_RADIX);
sangho2e97be02017-07-03 18:18:27 +0900737 }
738
739 private int binHigher(String binStr, int bits) {
Jian Li4d138702018-11-27 17:25:28 +0900740 StringBuilder outBin = new StringBuilder(
741 binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
sangho2e97be02017-07-03 18:18:27 +0900742 for (int i = 0; i < bits; i++) {
Jian Li4d138702018-11-27 17:25:28 +0900743 outBin.append(STR_ONE);
sangho2e97be02017-07-03 18:18:27 +0900744 }
745
Jian Li4d138702018-11-27 17:25:28 +0900746 return Integer.parseInt(outBin.toString(), MASK_RADIX);
sangho2e97be02017-07-03 18:18:27 +0900747 }
748
749 private int testMasks(String binStr, int start, int end) {
Jian Li4d138702018-11-27 17:25:28 +0900750 int mask = MASK_BEGIN_IDX;
751 for (; mask <= MASK_MAX_IDX; mask++) {
sangho2e97be02017-07-03 18:18:27 +0900752 int maskStart = binLower(binStr, mask);
753 int maskEnd = binHigher(binStr, mask);
754 if (maskStart < start || maskEnd > end) {
755 return mask - 1;
756 }
757 }
758
759 return mask;
760 }
761
762 private String getMask(int bits) {
763 switch (bits) {
764 case 0: return "ffff";
765 case 1: return "fffe";
766 case 2: return "fffc";
767 case 3: return "fff8";
768 case 4: return "fff0";
769 case 5: return "ffe0";
770 case 6: return "ffc0";
771 case 7: return "ff80";
772 case 8: return "ff00";
773 case 9: return "fe00";
774 case 10: return "fc00";
775 case 11: return "f800";
776 case 12: return "f000";
777 case 13: return "e000";
778 case 14: return "c000";
779 case 15: return "8000";
780 case 16: return "0000";
781 default: return null;
782 }
783 }
784
785 private Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
786
787 boolean processing = true;
788 int start = portMin;
789 Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
790 while (processing) {
791 String minStr = Integer.toBinaryString(start);
Jian Li4d138702018-11-27 17:25:28 +0900792 String binStrMinPadded = STR_PADDING.substring(minStr.length()) + minStr;
sangho2e97be02017-07-03 18:18:27 +0900793
794 int mask = testMasks(binStrMinPadded, start, portMax);
795 int maskStart = binLower(binStrMinPadded, mask);
796 int maskEnd = binHigher(binStrMinPadded, mask);
797
798 log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
Jian Libcc42282018-09-13 20:59:34 +0900799 portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(
Jian Li4d138702018-11-27 17:25:28 +0900800 Integer.parseInt(Objects.requireNonNull(getMask(mask)), PORT_RADIX)));
sangho2e97be02017-07-03 18:18:27 +0900801
802 start = maskEnd + 1;
803 if (start > portMax) {
804 processing = false;
805 }
806 }
807
808 return portMaskMap;
809 }
810
sangho6a9ff0d2017-03-27 11:23:37 +0900811 private class InternalInstancePortListener implements InstancePortListener {
812
813 @Override
814 public boolean isRelevant(InstancePortEvent event) {
Jian Li34220ea2018-11-14 01:30:24 +0900815 return useSecurityGroup;
816 }
817
818 private boolean isRelevantHelper(InstancePortEvent event) {
819 return mastershipService.isLocalMaster(event.subject().deviceId());
sangho6a9ff0d2017-03-27 11:23:37 +0900820 }
821
822 @Override
823 public void event(InstancePortEvent event) {
sangho6a9ff0d2017-03-27 11:23:37 +0900824 switch (event.type()) {
Jian Liec5c32b2018-07-13 14:28:58 +0900825 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Lie8b28db2018-10-17 14:04:09 +0900826 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liac30e272018-10-18 23:08:03 +0900827 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
Jian Li4d138702018-11-27 17:25:28 +0900828 eventExecutor.execute(() -> processInstanceMigrationStart(event));
Jian Liac30e272018-10-18 23:08:03 +0900829 break;
830 case OPENSTACK_INSTANCE_PORT_VANISHED:
Jian Li4d138702018-11-27 17:25:28 +0900831 eventExecutor.execute(() -> processInstancePortVanish(event));
sangho6a9ff0d2017-03-27 11:23:37 +0900832 break;
Jian Lib8cdcc12018-10-23 01:53:10 +0900833 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Li4d138702018-11-27 17:25:28 +0900834 eventExecutor.execute(() -> processInstanceMigrationEnd(event));
sangho6a9ff0d2017-03-27 11:23:37 +0900835 break;
sangho6a9ff0d2017-03-27 11:23:37 +0900836 default:
837 break;
838 }
839 }
Jian Liac30e272018-10-18 23:08:03 +0900840
Jian Li4d138702018-11-27 17:25:28 +0900841 private void processInstanceMigrationStart(InstancePortEvent event) {
842 if (!isRelevantHelper(event)) {
843 return;
844 }
845
846 InstancePort instPort = event.subject();
847 installSecurityGroupRules(event, instPort);
848 setAclRecircRules(instPort, true);
849 }
850
851 private void processInstancePortVanish(InstancePortEvent event) {
852 if (!isRelevantHelper(event)) {
853 return;
854 }
855
856 InstancePort instPort = event.subject();
857 Port osPort = removedOsPortStore.asJavaMap().get(instPort.portId());
858 setSecurityGroupRules(instPort, osPort, false);
859 removedOsPortStore.remove(instPort.portId());
860 setAclRecircRules(instPort, false);
861 }
862
863 private void processInstanceMigrationEnd(InstancePortEvent event) {
864 if (!isRelevantHelper(event)) {
865 return;
866 }
867
868 InstancePort instPort = event.subject();
869 InstancePort revisedInstPort = swapStaleLocation(instPort);
870 Port port = osNetService.port(instPort.portId());
871 setSecurityGroupRules(revisedInstPort, port, false);
872 setAclRecircRules(revisedInstPort, false);
873 }
874
Jian Liac30e272018-10-18 23:08:03 +0900875 private void installSecurityGroupRules(InstancePortEvent event,
876 InstancePort instPort) {
877 log.debug("Instance port detected/updated MAC:{} IP:{}",
878 instPort.macAddress(),
879 instPort.ipAddress());
Jian Li2b9838c2018-10-28 17:09:42 +0900880 eventExecutor.execute(() ->
881 setSecurityGroupRules(instPort,
882 osNetService.port(event.subject().portId()), true));
Jian Liac30e272018-10-18 23:08:03 +0900883 }
Jian Li1e9cb732018-11-25 23:17:21 +0900884
Jian Li4d138702018-11-27 17:25:28 +0900885 private void setSecurityGroupRules(InstancePort instPort,
886 Port port, boolean install) {
887 Port osPort = port;
888
889 if (!install) {
890 Port rmvPort = removedOsPortStore.asJavaMap().get(instPort.portId());
891 if (osPort == null && rmvPort == null) {
892 return;
893 }
894
895 if (port == null) {
896 osPort = rmvPort;
897 }
898 }
899
900 final Port finalPort = osPort;
901
902 osPort.getSecurityGroups().forEach(sgId -> {
903 SecurityGroup sg = securityGroupService.securityGroup(sgId);
904 if (sg == null) {
905 log.error("Security Group {} not found", sgId);
906 return;
907 }
908 sg.getRules().forEach(sgRule ->
909 updateSecurityGroupRule(instPort, finalPort, sgRule, install));
910 final String action = install ? "Installed " : "Removed ";
911 log.debug(action + "Security Group Rule ID : " + sgId);
912 });
913 }
914
Jian Li1e9cb732018-11-25 23:17:21 +0900915 private void setAclRecircRules(InstancePort instPort, boolean install) {
916 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
917
918 Network net = osNetService.network(instPort.networkId());
SONA Project6bc5c4a2018-12-14 23:49:52 +0900919 Type netType = osNetService.networkType(instPort.networkId());
Jian Li1e9cb732018-11-25 23:17:21 +0900920 String segId = net.getProviderSegID();
921
922 switch (netType) {
923 case VXLAN:
Jian Li2d68c192018-12-13 15:52:59 +0900924 case GRE:
Jian Li621f73c2018-12-15 01:49:22 +0900925 case GENEVE:
Jian Li1e9cb732018-11-25 23:17:21 +0900926 sBuilder.matchTunnelId(Long.valueOf(segId));
927 break;
928 case VLAN:
929 sBuilder.matchVlanId(VlanId.vlanId(segId));
930 break;
931 default:
932 break;
933 }
934
935 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
936 sBuilder.matchIPDst(IpPrefix.valueOf(instPort.ipAddress(), VM_IP_PREFIX));
937
938 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
939 tBuilder.transition(ACL_INGRESS_TABLE);
940
941 osFlowRuleService.setRule(
942 appId,
943 instPort.deviceId(),
944 sBuilder.build(),
945 tBuilder.build(),
946 PRIORITY_ACL_RULE,
947 ACL_RECIRC_TABLE,
948 install);
949 }
sangho6a9ff0d2017-03-27 11:23:37 +0900950 }
951
952 private class InternalOpenstackPortListener implements OpenstackNetworkListener {
953
954 @Override
955 public boolean isRelevant(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +0800956 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
sangho6a9ff0d2017-03-27 11:23:37 +0900957 return false;
958 }
Jian Libcc42282018-09-13 20:59:34 +0900959
Jian Li34220ea2018-11-14 01:30:24 +0900960 return useSecurityGroup;
961 }
962
963 private boolean isRelevantHelper(OpenstackNetworkEvent event) {
Jian Libcc42282018-09-13 20:59:34 +0900964 InstancePort instPort = instancePortService.instancePort(event.port().getId());
965
966 if (instPort == null) {
967 return false;
968 }
969
Jian Li34220ea2018-11-14 01:30:24 +0900970 return mastershipService.isLocalMaster(instPort.deviceId());
Jian Libcc42282018-09-13 20:59:34 +0900971 }
972
973 @Override
974 public void event(OpenstackNetworkEvent event) {
975 log.debug("openstack port event received {}", event);
Jian Libcc42282018-09-13 20:59:34 +0900976
Jian Li4d138702018-11-27 17:25:28 +0900977 if (event.type() == OPENSTACK_PORT_PRE_REMOVE) {
978 eventExecutor.execute(() -> processPortPreRemove(event));
Jian Libcc42282018-09-13 20:59:34 +0900979 }
980 }
Jian Li4d138702018-11-27 17:25:28 +0900981
982 private void processPortPreRemove(OpenstackNetworkEvent event) {
983 if (!isRelevantHelper(event)) {
984 return;
985 }
986
987 Port osPort = event.port();
988 removedOsPortStore.put(osPort.getId(), osPort);
989 }
Jian Libcc42282018-09-13 20:59:34 +0900990 }
991
Jian Li34220ea2018-11-14 01:30:24 +0900992 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
Jian Libcc42282018-09-13 20:59:34 +0900993
994 @Override
995 public boolean isRelevant(OpenstackNetworkEvent event) {
996 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
997 return false;
998 }
Jian Li2b9838c2018-10-28 17:09:42 +0900999
Jian Li34220ea2018-11-14 01:30:24 +09001000 return useSecurityGroup;
1001 }
1002
1003 private boolean isRelevantHelper(OpenstackNetworkEvent event) {
Hyunsun Moonae51e732017-04-25 17:46:21 +09001004 if (event.securityGroupId() == null ||
1005 securityGroupService.securityGroup(event.securityGroupId()) == null) {
1006 return false;
1007 }
Jian Li2b9838c2018-10-28 17:09:42 +09001008
1009 InstancePort instPort = instancePortService.instancePort(event.port().getId());
1010
1011 if (instPort == null) {
Hyunsun Moonae51e732017-04-25 17:46:21 +09001012 return false;
1013 }
Jian Li2b9838c2018-10-28 17:09:42 +09001014
Jian Li34220ea2018-11-14 01:30:24 +09001015 return mastershipService.isLocalMaster(instPort.deviceId());
sangho6a9ff0d2017-03-27 11:23:37 +09001016 }
1017
1018 @Override
1019 public void event(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +08001020 log.debug("security group event received {}", event);
Hyunsun Moonae51e732017-04-25 17:46:21 +09001021
sangho6a9ff0d2017-03-27 11:23:37 +09001022 switch (event.type()) {
Hyunsun Moonae51e732017-04-25 17:46:21 +09001023 case OPENSTACK_PORT_SECURITY_GROUP_ADDED:
Jian Li4d138702018-11-27 17:25:28 +09001024 eventExecutor.execute(() -> processPortSgAdd(event));
sangho6a9ff0d2017-03-27 11:23:37 +09001025 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +09001026 case OPENSTACK_PORT_SECURITY_GROUP_REMOVED:
Jian Li4d138702018-11-27 17:25:28 +09001027 eventExecutor.execute(() -> processPortSgRemove(event));
sangho6a9ff0d2017-03-27 11:23:37 +09001028 break;
1029 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +09001030 // do nothing for the other events
sangho6a9ff0d2017-03-27 11:23:37 +09001031 break;
1032 }
1033 }
Jian Li4d138702018-11-27 17:25:28 +09001034
1035 private void processPortSgAdd(OpenstackNetworkEvent event) {
1036 if (!isRelevantHelper(event)) {
1037 return;
1038 }
1039
1040 InstancePort instPort = instancePortService.instancePort(event.port().getId());
1041 SecurityGroup osSg = securityGroupService.securityGroup(event.securityGroupId());
1042
1043 osSg.getRules().forEach(sgRule -> {
1044 updateSecurityGroupRule(instPort, event.port(), sgRule, true);
1045 });
1046 log.info("Added security group {} to port {}",
1047 event.securityGroupId(), event.port().getId());
1048 }
1049
1050 private void processPortSgRemove(OpenstackNetworkEvent event) {
1051 if (!isRelevantHelper(event)) {
1052 return;
1053 }
1054
1055 InstancePort instPort = instancePortService.instancePort(event.port().getId());
1056 SecurityGroup osSg = securityGroupService.securityGroup(event.securityGroupId());
1057
1058 osSg.getRules().forEach(sgRule -> {
1059 updateSecurityGroupRule(instPort, event.port(), sgRule, false);
1060 });
1061 log.info("Removed security group {} from port {}",
1062 event.securityGroupId(), event.port().getId());
1063 }
sangho6a9ff0d2017-03-27 11:23:37 +09001064 }
1065
Jian Li34220ea2018-11-14 01:30:24 +09001066 private class InternalSecurityGroupListener implements OpenstackSecurityGroupListener {
sangho6a9ff0d2017-03-27 11:23:37 +09001067
1068 @Override
sangho0248ca22017-05-31 13:22:47 +09001069 public boolean isRelevant(OpenstackSecurityGroupEvent event) {
Jian Libcc42282018-09-13 20:59:34 +09001070 return useSecurityGroup;
sangho0248ca22017-05-31 13:22:47 +09001071 }
1072
Jian Li34220ea2018-11-14 01:30:24 +09001073 private boolean isRelevantHelper() {
1074 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
1075 }
1076
sangho0248ca22017-05-31 13:22:47 +09001077 @Override
sangho6a9ff0d2017-03-27 11:23:37 +09001078 public void event(OpenstackSecurityGroupEvent event) {
1079 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +09001080 case OPENSTACK_SECURITY_GROUP_RULE_CREATED:
Jian Li4d138702018-11-27 17:25:28 +09001081 eventExecutor.execute(() -> processSgRuleCreate(event));
sangho6a9ff0d2017-03-27 11:23:37 +09001082 break;
sangho6a9ff0d2017-03-27 11:23:37 +09001083 case OPENSTACK_SECURITY_GROUP_RULE_REMOVED:
Jian Li4d138702018-11-27 17:25:28 +09001084 eventExecutor.execute(() -> processSgRuleRemove(event));
sangho6a9ff0d2017-03-27 11:23:37 +09001085 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +09001086 case OPENSTACK_SECURITY_GROUP_REMOVED:
Jian Li9d35bd62018-10-13 01:43:24 +09001087 case OPENSTACK_SECURITY_GROUP_CREATED:
sangho6a9ff0d2017-03-27 11:23:37 +09001088 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +09001089 // do nothing
1090 break;
sangho6a9ff0d2017-03-27 11:23:37 +09001091 }
1092 }
Jian Li4d138702018-11-27 17:25:28 +09001093
1094 private void processSgRuleCreate(OpenstackSecurityGroupEvent event) {
1095 if (!isRelevantHelper()) {
1096 return;
1097 }
1098
1099 SecurityGroupRule sgRuleToAdd = event.securityGroupRule();
1100 securityGroupRuleAdded(sgRuleToAdd);
1101 log.info("Applied new security group rule {} to ports", sgRuleToAdd.getId());
1102 }
1103
1104 private void processSgRuleRemove(OpenstackSecurityGroupEvent event) {
1105 if (!isRelevantHelper()) {
1106 return;
1107 }
1108
1109 SecurityGroupRule sgRuleToRemove = event.securityGroupRule();
1110 securityGroupRuleRemoved(sgRuleToRemove);
1111 log.info("Removed security group rule {} from ports", sgRuleToRemove.getId());
1112 }
sangho6a9ff0d2017-03-27 11:23:37 +09001113 }
sangho1aaa7882017-05-31 13:22:47 +09001114
1115 private class InternalNodeListener implements OpenstackNodeListener {
1116
1117 @Override
1118 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Li2dc37592019-03-14 17:32:15 +09001119 return event.subject().type() == COMPUTE;
sangho1aaa7882017-05-31 13:22:47 +09001120 }
1121
Jian Li34220ea2018-11-14 01:30:24 +09001122 private boolean isRelevantHelper() {
1123 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
1124 }
1125
sangho1aaa7882017-05-31 13:22:47 +09001126 @Override
1127 public void event(OpenstackNodeEvent event) {
sangho1aaa7882017-05-31 13:22:47 +09001128 switch (event.type()) {
1129 case OPENSTACK_NODE_COMPLETE:
Jian Li4d138702018-11-27 17:25:28 +09001130 eventExecutor.execute(this::processNodeComplete);
sangho1aaa7882017-05-31 13:22:47 +09001131 break;
1132 case OPENSTACK_NODE_CREATED:
1133 case OPENSTACK_NODE_REMOVED:
1134 case OPENSTACK_NODE_UPDATED:
1135 case OPENSTACK_NODE_INCOMPLETE:
1136 default:
1137 break;
1138 }
1139 }
Jian Li4d138702018-11-27 17:25:28 +09001140
1141 private void processNodeComplete() {
1142 if (!isRelevantHelper()) {
1143 return;
1144 }
1145
1146 OpenstackSecurityGroupHandler.this.resetSecurityGroupRules();
1147 }
sangho1aaa7882017-05-31 13:22:47 +09001148 }
Jian Libcc42282018-09-13 20:59:34 +09001149}