blob: d1629209092fe12accdda15db175162bcb2f47d3 [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;
Jian Li362f9fd2018-11-28 11:14:40 +090089import java.util.List;
sangho2e97be02017-07-03 18:18:27 +090090import java.util.Map;
sangho6a9ff0d2017-03-27 11:23:37 +090091import java.util.Objects;
92import java.util.Set;
93import java.util.concurrent.ExecutorService;
94import java.util.stream.Collectors;
95
96import static java.util.concurrent.Executors.newSingleThreadExecutor;
97import static org.onlab.util.Tools.groupedThreads;
Jian Li1e9cb732018-11-25 23:17:21 +090098import static org.onosproject.openstacknetworking.api.Constants.ACL_EGRESS_TABLE;
99import static org.onosproject.openstacknetworking.api.Constants.ACL_INGRESS_TABLE;
100import static org.onosproject.openstacknetworking.api.Constants.ACL_RECIRC_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900101import static org.onosproject.openstacknetworking.api.Constants.CT_TABLE;
102import static org.onosproject.openstacknetworking.api.Constants.ERROR_TABLE;
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700103import static org.onosproject.openstacknetworking.api.Constants.JUMP_TABLE;
sangho6a9ff0d2017-03-27 11:23:37 +0900104import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Li1e9cb732018-11-25 23:17:21 +0900105import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ACL_INGRESS_RULE;
sangho6a9ff0d2017-03-27 11:23:37 +0900106import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ACL_RULE;
sangho1aaa7882017-05-31 13:22:47 +0900107import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_DROP_RULE;
108import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_HOOK_RULE;
109import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_RULE;
Jian Li4d138702018-11-27 17:25:28 +0900110import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_PRE_REMOVE;
Ray Milkey8e406512018-10-24 15:56:50 -0700111import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.USE_SECURITY_GROUP;
112import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.USE_SECURITY_GROUP_DEFAULT;
Jian Lib8cdcc12018-10-23 01:53:10 +0900113import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Libcc42282018-09-13 20:59:34 +0900114import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtMaskFlag;
115import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtStateFlag;
116import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.niciraConnTrackTreatmentBuilder;
sangho1aaa7882017-05-31 13:22:47 +0900117import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
sangho6a9ff0d2017-03-27 11:23:37 +0900118import static org.slf4j.LoggerFactory.getLogger;
119
120/**
121 * Populates flow rules to handle OpenStack SecurityGroups.
122 */
Ray Milkey8e406512018-10-24 15:56:50 -0700123@Component(
124 immediate = true,
125 property = {
126 USE_SECURITY_GROUP + ":Boolean=" + USE_SECURITY_GROUP_DEFAULT
127 }
128)
sangho6a9ff0d2017-03-27 11:23:37 +0900129public class OpenstackSecurityGroupHandler {
130
131 private final Logger log = getLogger(getClass());
132
Jian Li2b9838c2018-10-28 17:09:42 +0900133 private static final int VM_IP_PREFIX = 32;
134
Jian Li4d138702018-11-27 17:25:28 +0900135 private static final String STR_ZERO = "0";
136 private static final String STR_ONE = "1";
137 private static final String STR_NULL = "null";
138 private static final String STR_PADDING = "0000000000000000";
139 private static final int MASK_BEGIN_IDX = 0;
140 private static final int MASK_MAX_IDX = 16;
141 private static final int MASK_RADIX = 2;
142 private static final int PORT_RADIX = 16;
143
Jian Li900b7232018-12-14 14:04:51 +0900144 /** Apply OpenStack security group rule for VM traffic. */
Ray Milkey8e406512018-10-24 15:56:50 -0700145 private boolean useSecurityGroup = USE_SECURITY_GROUP_DEFAULT;
sangho0248ca22017-05-31 13:22:47 +0900146
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700147 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho6a9ff0d2017-03-27 11:23:37 +0900148 protected CoreService coreService;
149
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700150 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li9d35bd62018-10-13 01:43:24 +0900151 protected InstancePortAdminService instancePortService;
sangho6a9ff0d2017-03-27 11:23:37 +0900152
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700153 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho6a9ff0d2017-03-27 11:23:37 +0900154 protected MastershipService mastershipService;
155
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700156 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moonae51e732017-04-25 17:46:21 +0900157 protected OpenstackNetworkService osNetService;
sangho6a9ff0d2017-03-27 11:23:37 +0900158
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700159 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho6a9ff0d2017-03-27 11:23:37 +0900160 protected OpenstackSecurityGroupService securityGroupService;
161
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700162 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sanghodc375372017-06-08 10:41:30 +0900163 protected OpenstackFlowRuleService osFlowRuleService;
sangho6a9ff0d2017-03-27 11:23:37 +0900164
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700165 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho0248ca22017-05-31 13:22:47 +0900166 protected ComponentConfigService configService;
167
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700168 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho3dd2a8b2017-07-19 15:54:31 +0900169 protected OpenstackNodeService osNodeService;
170
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700171 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho1aaa7882017-05-31 13:22:47 +0900172 protected DriverService driverService;
173
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700174 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho1aaa7882017-05-31 13:22:47 +0900175 protected LeadershipService leadershipService;
176
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700177 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho1aaa7882017-05-31 13:22:47 +0900178 protected ClusterService clusterService;
179
Ray Milkey0b18b722018-10-16 13:19:15 -0700180 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Liac30e272018-10-18 23:08:03 +0900181 protected StorageService storageService;
182
183 private static final KryoNamespace SERIALIZER_PORT = KryoNamespace.newBuilder()
184 .register(KryoNamespaces.API)
185 .register(Port.class)
186 .register(NeutronPort.class)
187 .register(NeutronIP.class)
188 .register(State.class)
189 .register(NeutronAllowedAddressPair.class)
190 .register(NeutronExtraDhcpOptCreate.class)
191 .register(LinkedHashMap.class)
192 .build();
Jian Li9d35bd62018-10-13 01:43:24 +0900193
Jian Libcc42282018-09-13 20:59:34 +0900194 private final InstancePortListener instancePortListener =
Jian Lia70fdb602018-11-05 01:32:22 +0900195 new InternalInstancePortListener();
Jian Libcc42282018-09-13 20:59:34 +0900196 private final OpenstackNetworkListener osNetworkListener =
Jian Lia70fdb602018-11-05 01:32:22 +0900197 new InternalOpenstackNetworkListener();
Jian Libcc42282018-09-13 20:59:34 +0900198 private final OpenstackNetworkListener osPortListener =
Jian Lia70fdb602018-11-05 01:32:22 +0900199 new InternalOpenstackPortListener();
Jian Libcc42282018-09-13 20:59:34 +0900200 private final OpenstackSecurityGroupListener securityGroupListener =
Jian Lia70fdb602018-11-05 01:32:22 +0900201 new InternalSecurityGroupListener();
sangho1aaa7882017-05-31 13:22:47 +0900202 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
Jian Liac30e272018-10-18 23:08:03 +0900203
204 private ConsistentMap<String, Port> removedOsPortStore;
205
sangho6a9ff0d2017-03-27 11:23:37 +0900206 private ApplicationId appId;
sangho1aaa7882017-05-31 13:22:47 +0900207 private NodeId localNodeId;
sangho6a9ff0d2017-03-27 11:23:37 +0900208
209 private final ExecutorService eventExecutor = newSingleThreadExecutor(
210 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
211
212 private static final String PROTO_ICMP = "ICMP";
213 private static final String PROTO_TCP = "TCP";
214 private static final String PROTO_UDP = "UDP";
215 private static final String ETHTYPE_IPV4 = "IPV4";
216 private static final String EGRESS = "EGRESS";
217 private static final String INGRESS = "INGRESS";
218 private static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
219
Jian Lie8b28db2018-10-17 14:04:09 +0900220 private static final String VXLAN = "VXLAN";
Jian Li2d68c192018-12-13 15:52:59 +0900221 private static final String GRE = "GRE";
Jian Lie8b28db2018-10-17 14:04:09 +0900222 private static final String VLAN = "VLAN";
223
sangho1aaa7882017-05-31 13:22:47 +0900224 // We expose pipeline structure to SONA application considering removing pipeline soon.
sanghoshinbbeb31a2018-09-11 17:01:01 +0800225 private static final int GOTO_CONNTRACK_TABLE = CT_TABLE;
226 private static final int GOTO_JUMP_TABLE = JUMP_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900227
228 private static final int CT_COMMIT = 0;
229 private static final int CT_NO_COMMIT = 1;
230 private static final short CT_NO_RECIRC = -1;
231
232 private static final int ACTION_NONE = 0;
233 private static final int ACTION_DROP = -1;
234
sangho6a9ff0d2017-03-27 11:23:37 +0900235 @Activate
236 protected void activate() {
237 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
sangho1aaa7882017-05-31 13:22:47 +0900238 localNodeId = clusterService.getLocalNode().id();
sangho6a9ff0d2017-03-27 11:23:37 +0900239 instancePortService.addListener(instancePortListener);
240 securityGroupService.addListener(securityGroupListener);
Jian Libcc42282018-09-13 20:59:34 +0900241 osNetService.addListener(osPortListener);
242 osNetService.addListener(osNetworkListener);
sangho0248ca22017-05-31 13:22:47 +0900243 configService.registerProperties(getClass());
sangho1aaa7882017-05-31 13:22:47 +0900244 osNodeService.addListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900245
Jian Liac30e272018-10-18 23:08:03 +0900246 removedOsPortStore = storageService.<String, Port>consistentMapBuilder()
247 .withSerializer(Serializer.using(SERIALIZER_PORT))
248 .withName("openstack-removed-portstore")
249 .withApplicationId(appId)
250 .build();
251
sangho6a9ff0d2017-03-27 11:23:37 +0900252 log.info("Started");
253 }
254
255 @Deactivate
256 protected void deactivate() {
257 instancePortService.removeListener(instancePortListener);
258 securityGroupService.removeListener(securityGroupListener);
Jian Libcc42282018-09-13 20:59:34 +0900259 osNetService.removeListener(osNetworkListener);
260 osNetService.removeListener(osPortListener);
sangho0248ca22017-05-31 13:22:47 +0900261 configService.unregisterProperties(getClass(), false);
sangho1aaa7882017-05-31 13:22:47 +0900262 osNodeService.removeListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900263 eventExecutor.shutdown();
264
265 log.info("Stopped");
266 }
267
sangho0248ca22017-05-31 13:22:47 +0900268 @Modified
269 protected void modified(ComponentContext context) {
270 Dictionary<?, ?> properties = context.getProperties();
271 Boolean flag;
272
Ray Milkey8e406512018-10-24 15:56:50 -0700273 flag = Tools.isPropertyEnabled(properties, USE_SECURITY_GROUP);
sangho0248ca22017-05-31 13:22:47 +0900274 if (flag == null) {
275 log.info("useSecurityGroup is not configured, " +
276 "using current value of {}", useSecurityGroup);
277 } else {
278 useSecurityGroup = flag;
279 log.info("Configured. useSecurityGroup is {}",
280 useSecurityGroup ? "enabled" : "disabled");
281 }
282
sanghoe6457a32017-08-24 14:31:19 +0900283 securityGroupService.setSecurityGroupEnabled(useSecurityGroup);
sangho0248ca22017-05-31 13:22:47 +0900284 resetSecurityGroupRules();
285 }
286
sangho1aaa7882017-05-31 13:22:47 +0900287 private void initializeConnTrackTable(DeviceId deviceId, boolean install) {
288
289 //table=1,ip,ct_state=-trk, actions=ct(table:2)
Jian Libcc42282018-09-13 20:59:34 +0900290 long ctState = computeCtStateFlag(false, false, false);
291 long ctMask = computeCtMaskFlag(true, false, false);
sangho1aaa7882017-05-31 13:22:47 +0900292 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, (short) GOTO_CONNTRACK_TABLE,
293 ACTION_NONE, PRIORITY_CT_HOOK_RULE, install);
294
295 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+est,action=goto_table:3
Jian Libcc42282018-09-13 20:59:34 +0900296 ctState = computeCtStateFlag(true, false, true);
297 ctMask = computeCtMaskFlag(true, false, true);
sangho1aaa7882017-05-31 13:22:47 +0900298 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
299 GOTO_JUMP_TABLE, PRIORITY_CT_RULE, install);
300
301 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+new,action=drop
Jian Libcc42282018-09-13 20:59:34 +0900302 ctState = computeCtStateFlag(true, true, false);
303 ctMask = computeCtMaskFlag(true, true, false);
sangho1aaa7882017-05-31 13:22:47 +0900304 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
305 ACTION_DROP, PRIORITY_CT_DROP_RULE, install);
306 }
307
Jian Li1e9cb732018-11-25 23:17:21 +0900308 private void initializeAclTable(DeviceId deviceId, boolean install) {
309
310 ExtensionTreatment ctTreatment =
311 niciraConnTrackTreatmentBuilder(driverService, deviceId)
312 .commit(true)
313 .build();
314
315 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
316 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
317
318 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
319 tBuilder.extension(ctTreatment, deviceId)
320 .transition(JUMP_TABLE);
321
322 osFlowRuleService.setRule(appId,
323 deviceId,
324 sBuilder.build(),
325 tBuilder.build(),
326 PRIORITY_ACL_INGRESS_RULE,
327 ACL_RECIRC_TABLE,
328 install);
329 }
330
331 private void initializeIngressTable(DeviceId deviceId, boolean install) {
332 if (install) {
333 osFlowRuleService.setUpTableMissEntry(deviceId, ACL_INGRESS_TABLE);
334 } else {
335 osFlowRuleService.connectTables(deviceId, ACL_INGRESS_TABLE, JUMP_TABLE);
336 }
337 }
338
Jian Libcc42282018-09-13 20:59:34 +0900339 private void updateSecurityGroupRule(InstancePort instPort, Port port,
340 SecurityGroupRule sgRule, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900341
Daniel Park3a140592018-06-28 18:33:10 +0900342 if (instPort == null || port == null || sgRule == null) {
343 return;
344 }
345
sangho6a9ff0d2017-03-27 11:23:37 +0900346 if (sgRule.getRemoteGroupId() != null && !sgRule.getRemoteGroupId().isEmpty()) {
Jian Lia70fdb602018-11-05 01:32:22 +0900347 getRemoteInstPorts(port, sgRule.getRemoteGroupId(), install)
Jian Libcc42282018-09-13 20:59:34 +0900348 .forEach(rInstPort -> {
Jian Li1e9cb732018-11-25 23:17:21 +0900349 populateSecurityGroupRule(sgRule, instPort,
Jian Libcc42282018-09-13 20:59:34 +0900350 rInstPort.ipAddress().toIpPrefix(), install);
Jian Li1e9cb732018-11-25 23:17:21 +0900351 populateSecurityGroupRule(sgRule, rInstPort,
Jian Libcc42282018-09-13 20:59:34 +0900352 instPort.ipAddress().toIpPrefix(), install);
sangho6a9ff0d2017-03-27 11:23:37 +0900353
Jian Libcc42282018-09-13 20:59:34 +0900354 SecurityGroupRule rSgRule =
355 new NeutronSecurityGroupRule
356 .SecurityGroupRuleConcreteBuilder()
Jian Li28ec77f2018-10-31 07:07:25 +0900357 .from(sgRule)
Jian Li4d138702018-11-27 17:25:28 +0900358 .direction(sgRule.getDirection()
359 .equalsIgnoreCase(EGRESS) ? INGRESS : EGRESS)
Jian Li28ec77f2018-10-31 07:07:25 +0900360 .build();
Jian Li1e9cb732018-11-25 23:17:21 +0900361 populateSecurityGroupRule(rSgRule, instPort,
Jian Libcc42282018-09-13 20:59:34 +0900362 rInstPort.ipAddress().toIpPrefix(), install);
Jian Li1e9cb732018-11-25 23:17:21 +0900363 populateSecurityGroupRule(rSgRule, rInstPort,
Jian Libcc42282018-09-13 20:59:34 +0900364 instPort.ipAddress().toIpPrefix(), install);
365 });
sangho6a9ff0d2017-03-27 11:23:37 +0900366 } else {
Jian Li1e9cb732018-11-25 23:17:21 +0900367 populateSecurityGroupRule(sgRule, instPort,
Jian Libcc42282018-09-13 20:59:34 +0900368 sgRule.getRemoteIpPrefix() == null ? IP_PREFIX_ANY :
Jian Li28ec77f2018-10-31 07:07:25 +0900369 IpPrefix.valueOf(sgRule.getRemoteIpPrefix()), install);
sangho6a9ff0d2017-03-27 11:23:37 +0900370 }
371 }
372
Jian Libcc42282018-09-13 20:59:34 +0900373 private void populateSecurityGroupRule(SecurityGroupRule sgRule,
374 InstancePort instPort,
Jian Lie8b28db2018-10-17 14:04:09 +0900375 IpPrefix remoteIp,
376 boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900377 Set<TrafficSelector> selectors = buildSelectors(sgRule,
Jian Li1e9cb732018-11-25 23:17:21 +0900378 Ip4Address.valueOf(instPort.ipAddress().toInetAddress()),
379 remoteIp, instPort.networkId());
sangho2e97be02017-07-03 18:18:27 +0900380 if (selectors == null || selectors.isEmpty()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900381 return;
382 }
383
Jian Li362f9fd2018-11-28 11:14:40 +0900384 // in case a port is bound to multiple security groups, we do NOT remove
385 // egress rules unless all security groups bound to the port to be removed
386 Port osPort = osNetService.port(instPort.portId());
387 if (!install && osPort != null && sgRule.getDirection().equalsIgnoreCase(EGRESS)) {
388 List<String> sgIds = osPort.getSecurityGroups();
389 if (!sgIds.contains(sgRule.getSecurityGroupId()) && !sgIds.isEmpty()) {
390 return;
391 }
392 }
393
Jian Li28ec77f2018-10-31 07:07:25 +0900394 // XXX All egress traffic needs to go through connection tracking module,
395 // which might hurt its performance.
396 ExtensionTreatment ctTreatment =
397 niciraConnTrackTreatmentBuilder(driverService, instPort.deviceId())
398 .commit(true)
399 .build();
400
Jian Li1e9cb732018-11-25 23:17:21 +0900401 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Jian Li28ec77f2018-10-31 07:07:25 +0900402
Jian Li1e9cb732018-11-25 23:17:21 +0900403 int aclTable;
Jian Li4d138702018-11-27 17:25:28 +0900404 if (sgRule.getDirection().equalsIgnoreCase(EGRESS)) {
Jian Li1e9cb732018-11-25 23:17:21 +0900405 aclTable = ACL_EGRESS_TABLE;
406 tBuilder.transition(ACL_RECIRC_TABLE);
407 } else {
408 aclTable = ACL_INGRESS_TABLE;
409 tBuilder.extension(ctTreatment, instPort.deviceId())
410 .transition(JUMP_TABLE);
411 }
412
413 int finalAclTable = aclTable;
414 selectors.forEach(selector -> {
415 osFlowRuleService.setRule(appId,
416 instPort.deviceId(),
417 selector, tBuilder.build(),
418 PRIORITY_ACL_RULE,
419 finalAclTable,
420 install);
421 });
sangho6a9ff0d2017-03-27 11:23:37 +0900422 }
423
424 /**
sangho1aaa7882017-05-31 13:22:47 +0900425 * Sets connection tracking rule using OVS extension commands.
Jian Li2b9838c2018-10-28 17:09:42 +0900426 * It is not so graceful, but I don't want to make it more general because
427 * it is going to be used only here.
428 * The following is the usage of the function.
sangho1aaa7882017-05-31 13:22:47 +0900429 *
430 * @param deviceId Device ID
Jian Li2b9838c2018-10-28 17:09:42 +0900431 * @param ctState ctState: please use RulePopulatorUtil.computeCtStateFlag()
432 * to build the value
433 * @param ctMask crMask: please use RulePopulatorUtil.computeCtMaskFlag()
434 * to build the value
sangho1aaa7882017-05-31 13:22:47 +0900435 * @param commit CT_COMMIT for commit action, CT_NO_COMMIT otherwise
Jian Li2b9838c2018-10-28 17:09:42 +0900436 * @param recircTable table number for recirculation after CT actions.
437 * CT_NO_RECIRC with no recirculation
438 * @param action Additional actions. ACTION_DROP, ACTION_NONE,
439 * GOTO_XXX_TABLE are supported.
sangho1aaa7882017-05-31 13:22:47 +0900440 * @param priority priority value for the rule
441 * @param install true for insertion, false for removal
442 */
443 private void setConnTrackRule(DeviceId deviceId, long ctState, long ctMask,
444 int commit, short recircTable,
445 int action, int priority, boolean install) {
446
Jian Libcc42282018-09-13 20:59:34 +0900447 ExtensionSelector esCtSate = RulePopulatorUtil
448 .buildCtExtensionSelector(driverService, deviceId, ctState, ctMask);
sangho1aaa7882017-05-31 13:22:47 +0900449 TrafficSelector selector = DefaultTrafficSelector.builder()
450 .extension(esCtSate, deviceId)
451 .matchEthType(Ethernet.TYPE_IPV4)
452 .build();
453
454 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
455
456 if (commit == CT_COMMIT || recircTable > 0) {
457 RulePopulatorUtil.NiriraConnTrackTreatmentBuilder natTreatmentBuilder =
Jian Libcc42282018-09-13 20:59:34 +0900458 niciraConnTrackTreatmentBuilder(driverService, deviceId);
sangho1aaa7882017-05-31 13:22:47 +0900459 natTreatmentBuilder.natAction(false);
460 if (commit == CT_COMMIT) {
461 natTreatmentBuilder.commit(true);
462 } else {
463 natTreatmentBuilder.commit(false);
464 }
465 if (recircTable > 0) {
466 natTreatmentBuilder.table(recircTable);
467 }
468 tb.extension(natTreatmentBuilder.build(), deviceId);
469 } else if (action == ACTION_DROP) {
470 tb.drop();
471 }
472
sanghoshinbbeb31a2018-09-11 17:01:01 +0800473 if (action != ACTION_NONE && action != ACTION_DROP) {
sangho1aaa7882017-05-31 13:22:47 +0900474 tb.transition(action);
475 }
476
477 int tableType = ERROR_TABLE;
478 if (priority == PRIORITY_CT_RULE || priority == PRIORITY_CT_DROP_RULE) {
479 tableType = CT_TABLE;
480 } else if (priority == PRIORITY_CT_HOOK_RULE) {
Jian Li1e9cb732018-11-25 23:17:21 +0900481 tableType = ACL_INGRESS_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900482 } else {
483 log.error("Cannot an appropriate table for the conn track rule.");
484 }
485
486 osFlowRuleService.setRule(
487 appId,
488 deviceId,
489 selector,
490 tb.build(),
491 priority,
492 tableType,
493 install);
494 }
495
496 /**
sangho6a9ff0d2017-03-27 11:23:37 +0900497 * Returns a set of host IP addresses engaged with supplied security group ID.
498 * It only searches a VM in the same tenant boundary.
499 *
Jian Lia70fdb602018-11-05 01:32:22 +0900500 * @param srcPort openstack port
sangho6a9ff0d2017-03-27 11:23:37 +0900501 * @param sgId security group id
502 * @return set of ip addresses
503 */
Jian Lia70fdb602018-11-05 01:32:22 +0900504 private Set<InstancePort> getRemoteInstPorts(Port srcPort,
Jian Li2b9838c2018-10-28 17:09:42 +0900505 String sgId, boolean install) {
sangho6a9ff0d2017-03-27 11:23:37 +0900506 Set<InstancePort> remoteInstPorts;
507
Jian Liac30e272018-10-18 23:08:03 +0900508 Set<Port> removedPorts = Sets.newConcurrentHashSet();
509
510 if (!install) {
511 removedPorts = new HashSet<>(removedOsPortStore.asJavaMap().values());
512 }
513
514 remoteInstPorts = Sets.union(osNetService.ports(), removedPorts).stream()
Jian Lia70fdb602018-11-05 01:32:22 +0900515 .filter(port -> !port.getId().equals(srcPort.getId()))
516 .filter(port -> port.getTenantId().equals(srcPort.getTenantId()))
sangho6a9ff0d2017-03-27 11:23:37 +0900517 .filter(port -> port.getSecurityGroups().contains(sgId))
Jian Lia70fdb602018-11-05 01:32:22 +0900518 .filter(port -> port.getNetworkId().equals(srcPort.getNetworkId()))
sangho6a9ff0d2017-03-27 11:23:37 +0900519 .map(port -> instancePortService.instancePort(port.getId()))
520 .filter(instPort -> instPort != null && instPort.ipAddress() != null)
521 .collect(Collectors.toSet());
522
523 return Collections.unmodifiableSet(remoteInstPorts);
524 }
525
sangho2e97be02017-07-03 18:18:27 +0900526 private Set<TrafficSelector> buildSelectors(SecurityGroupRule sgRule,
527 Ip4Address vmIp,
Jian Lie8b28db2018-10-17 14:04:09 +0900528 IpPrefix remoteIp,
Jian Li1e9cb732018-11-25 23:17:21 +0900529 String netId) {
Jian Li2b9838c2018-10-28 17:09:42 +0900530 if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, VM_IP_PREFIX))) {
sangho2e97be02017-07-03 18:18:27 +0900531 // do nothing if the remote IP is my IP
532 return null;
533 }
534
535 Set<TrafficSelector> selectorSet = Sets.newHashSet();
536
537 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
Jian Li1e9cb732018-11-25 23:17:21 +0900538 buildMatches(sBuilder, sgRule, vmIp, remoteIp, netId);
sangho2e97be02017-07-03 18:18:27 +0900539
540 if (sgRule.getPortRangeMax() != null && sgRule.getPortRangeMin() != null &&
541 sgRule.getPortRangeMin() < sgRule.getPortRangeMax()) {
Jian Libcc42282018-09-13 20:59:34 +0900542 Map<TpPort, TpPort> portRangeMatchMap =
543 buildPortRangeMatches(sgRule.getPortRangeMin(),
Jian Li28ec77f2018-10-31 07:07:25 +0900544 sgRule.getPortRangeMax());
Jian Li2b9838c2018-10-28 17:09:42 +0900545 portRangeMatchMap.forEach((key, value) -> {
sangho2e97be02017-07-03 18:18:27 +0900546
Jian Li4d138702018-11-27 17:25:28 +0900547 if (sgRule.getProtocol().equalsIgnoreCase(PROTO_TCP)) {
548 if (sgRule.getDirection().equalsIgnoreCase(EGRESS)) {
Jian Li2b9838c2018-10-28 17:09:42 +0900549 sBuilder.matchTcpSrcMasked(key, value);
550 } else {
551 sBuilder.matchTcpDstMasked(key, value);
Jian Libcc42282018-09-13 20:59:34 +0900552 }
Jian Li4d138702018-11-27 17:25:28 +0900553 } else if (sgRule.getProtocol().equalsIgnoreCase(PROTO_UDP)) {
554 if (sgRule.getDirection().equalsIgnoreCase(EGRESS)) {
Jian Li2b9838c2018-10-28 17:09:42 +0900555 sBuilder.matchUdpSrcMasked(key, value);
556 } else {
557 sBuilder.matchUdpDstMasked(key, value);
558 }
559 }
560
561 selectorSet.add(sBuilder.build());
562 });
sangho2e97be02017-07-03 18:18:27 +0900563 } else {
564 selectorSet.add(sBuilder.build());
565 }
566
567 return selectorSet;
568 }
569
Jian Libcc42282018-09-13 20:59:34 +0900570 private void buildMatches(TrafficSelector.Builder sBuilder,
Jian Li1e9cb732018-11-25 23:17:21 +0900571 SecurityGroupRule sgRule, Ip4Address vmIp,
572 IpPrefix remoteIp, String netId) {
573 buildTunnelId(sBuilder, netId);
sangho6a9ff0d2017-03-27 11:23:37 +0900574 buildMatchEthType(sBuilder, sgRule.getEtherType());
575 buildMatchDirection(sBuilder, sgRule.getDirection(), vmIp);
576 buildMatchProto(sBuilder, sgRule.getProtocol());
577 buildMatchPort(sBuilder, sgRule.getProtocol(), sgRule.getDirection(),
sangho2e97be02017-07-03 18:18:27 +0900578 sgRule.getPortRangeMin() == null ? 0 : sgRule.getPortRangeMin(),
579 sgRule.getPortRangeMax() == null ? 0 : sgRule.getPortRangeMax());
sangho6a9ff0d2017-03-27 11:23:37 +0900580 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
sangho6a9ff0d2017-03-27 11:23:37 +0900581 }
582
Jian Li1e9cb732018-11-25 23:17:21 +0900583 private void buildTunnelId(TrafficSelector.Builder sBuilder, String netId) {
584 String segId = osNetService.segmentId(netId);
585 String netType = osNetService.networkType(netId);
Jian Lie8b28db2018-10-17 14:04:09 +0900586
587 if (VLAN.equals(netType)) {
588 sBuilder.matchVlanId(VlanId.vlanId(segId));
Jian Li2d68c192018-12-13 15:52:59 +0900589 } else if (VXLAN.equals(netType) || GRE.equals(netType)) {
Jian Lie8b28db2018-10-17 14:04:09 +0900590 sBuilder.matchTunnelId(Long.valueOf(segId));
Jian Li28ec77f2018-10-31 07:07:25 +0900591 } else {
592 log.warn("Cannot tag the VID due to lack of support of virtual network type {}", netType);
Jian Lie8b28db2018-10-17 14:04:09 +0900593 }
594 }
595
sangho6a9ff0d2017-03-27 11:23:37 +0900596 private void buildMatchDirection(TrafficSelector.Builder sBuilder,
597 String direction,
598 Ip4Address vmIp) {
Jian Li4d138702018-11-27 17:25:28 +0900599 if (direction.equalsIgnoreCase(EGRESS)) {
Jian Li2b9838c2018-10-28 17:09:42 +0900600 sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, VM_IP_PREFIX));
sangho6a9ff0d2017-03-27 11:23:37 +0900601 } else {
Jian Li2b9838c2018-10-28 17:09:42 +0900602 sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, VM_IP_PREFIX));
sangho6a9ff0d2017-03-27 11:23:37 +0900603 }
604 }
605
606 private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
607 // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
608 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
Jian Li4d138702018-11-27 17:25:28 +0900609 if (etherType != null && !Objects.equals(etherType, STR_NULL) &&
610 !etherType.equalsIgnoreCase(ETHTYPE_IPV4)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900611 log.debug("EthType {} is not supported yet in Security Group", etherType);
612 }
613 }
614
Jian Libcc42282018-09-13 20:59:34 +0900615 private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder,
616 IpPrefix remoteIpPrefix, String direction) {
617 if (remoteIpPrefix != null &&
618 !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
Jian Li4d138702018-11-27 17:25:28 +0900619 if (direction.equalsIgnoreCase(EGRESS)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900620 sBuilder.matchIPDst(remoteIpPrefix);
621 } else {
622 sBuilder.matchIPSrc(remoteIpPrefix);
623 }
624 }
625 }
626
627 private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
628 if (protocol != null) {
629 switch (protocol.toUpperCase()) {
630 case PROTO_ICMP:
631 sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
632 break;
633 case PROTO_TCP:
634 sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
635 break;
636 case PROTO_UDP:
637 sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
638 break;
639 default:
640 }
641 }
642 }
643
Jian Libcc42282018-09-13 20:59:34 +0900644 private void buildMatchPort(TrafficSelector.Builder sBuilder,
645 String protocol, String direction,
sangho6a9ff0d2017-03-27 11:23:37 +0900646 int portMin, int portMax) {
647 if (portMin > 0 && portMax > 0 && portMin == portMax) {
Jian Li4d138702018-11-27 17:25:28 +0900648 if (protocol.equalsIgnoreCase(PROTO_TCP)) {
649 if (direction.equalsIgnoreCase(EGRESS)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900650 sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
651 } else {
652 sBuilder.matchTcpDst(TpPort.tpPort(portMax));
653 }
Jian Li4d138702018-11-27 17:25:28 +0900654 } else if (protocol.equalsIgnoreCase(PROTO_UDP)) {
655 if (direction.equalsIgnoreCase(EGRESS)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900656 sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
657 } else {
658 sBuilder.matchUdpDst(TpPort.tpPort(portMax));
659 }
660 }
661 }
662 }
663
sangho0248ca22017-05-31 13:22:47 +0900664 private void resetSecurityGroupRules() {
665
666 if (useSecurityGroup) {
Jian Lib6969502018-10-30 20:38:07 +0900667 osNodeService.completeNodes(COMPUTE).forEach(node -> {
Jian Li1e9cb732018-11-25 23:17:21 +0900668 osFlowRuleService.setUpTableMissEntry(node.intgBridge(), ACL_EGRESS_TABLE);
Jian Lib6969502018-10-30 20:38:07 +0900669 initializeConnTrackTable(node.intgBridge(), true);
Jian Li1e9cb732018-11-25 23:17:21 +0900670 initializeAclTable(node.intgBridge(), true);
671 initializeIngressTable(node.intgBridge(), true);
Jian Lib6969502018-10-30 20:38:07 +0900672 });
673
sangho0248ca22017-05-31 13:22:47 +0900674 securityGroupService.securityGroups().forEach(securityGroup ->
675 securityGroup.getRules().forEach(this::securityGroupRuleAdded));
676 } else {
Jian Lib6969502018-10-30 20:38:07 +0900677 osNodeService.completeNodes(COMPUTE).forEach(node -> {
Jian Li1e9cb732018-11-25 23:17:21 +0900678 osFlowRuleService.connectTables(node.intgBridge(), ACL_EGRESS_TABLE, JUMP_TABLE);
Jian Lib6969502018-10-30 20:38:07 +0900679 initializeConnTrackTable(node.intgBridge(), false);
Jian Li1e9cb732018-11-25 23:17:21 +0900680 initializeAclTable(node.intgBridge(), false);
681 initializeIngressTable(node.intgBridge(), false);
Jian Lib6969502018-10-30 20:38:07 +0900682 });
683
sangho0248ca22017-05-31 13:22:47 +0900684 securityGroupService.securityGroups().forEach(securityGroup ->
685 securityGroup.getRules().forEach(this::securityGroupRuleRemoved));
686 }
687
Jian Libcc42282018-09-13 20:59:34 +0900688 log.info("Reset security group info " +
Jian Li28ec77f2018-10-31 07:07:25 +0900689 (useSecurityGroup ? " with " : " without") + " Security Group");
sangho0248ca22017-05-31 13:22:47 +0900690 }
691
692 private void securityGroupRuleAdded(SecurityGroupRule sgRule) {
693 osNetService.ports().stream()
Jian Libcc42282018-09-13 20:59:34 +0900694 .filter(port -> port.getSecurityGroups()
Jian Li28ec77f2018-10-31 07:07:25 +0900695 .contains(sgRule.getSecurityGroupId()))
sangho0248ca22017-05-31 13:22:47 +0900696 .forEach(port -> {
697 updateSecurityGroupRule(
698 instancePortService.instancePort(port.getId()),
699 port, sgRule, true);
700 log.debug("Applied security group rule {} to port {}",
701 sgRule.getId(), port.getId());
702 });
703 }
704
705 private void securityGroupRuleRemoved(SecurityGroupRule sgRule) {
Jian Liac30e272018-10-18 23:08:03 +0900706 Set<Port> removedPorts = new HashSet<>(removedOsPortStore.asJavaMap().values());
707
708 Sets.union(osNetService.ports(), removedPorts).stream()
Jian Libcc42282018-09-13 20:59:34 +0900709 .filter(port -> port.getSecurityGroups()
Jian Li28ec77f2018-10-31 07:07:25 +0900710 .contains(sgRule.getSecurityGroupId()))
sangho0248ca22017-05-31 13:22:47 +0900711 .forEach(port -> {
712 updateSecurityGroupRule(
713 instancePortService.instancePort(port.getId()),
714 port, sgRule, false);
715 log.debug("Removed security group rule {} from port {}",
716 sgRule.getId(), port.getId());
717 });
718 }
719
sangho2e97be02017-07-03 18:18:27 +0900720 private int binLower(String binStr, int bits) {
Jian Li4d138702018-11-27 17:25:28 +0900721 StringBuilder outBin = new StringBuilder(
722 binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
sangho2e97be02017-07-03 18:18:27 +0900723 for (int i = 0; i < bits; i++) {
Jian Li4d138702018-11-27 17:25:28 +0900724 outBin.append(STR_ZERO);
sangho2e97be02017-07-03 18:18:27 +0900725 }
726
Jian Li4d138702018-11-27 17:25:28 +0900727 return Integer.parseInt(outBin.toString(), MASK_RADIX);
sangho2e97be02017-07-03 18:18:27 +0900728 }
729
730 private int binHigher(String binStr, int bits) {
Jian Li4d138702018-11-27 17:25:28 +0900731 StringBuilder outBin = new StringBuilder(
732 binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
sangho2e97be02017-07-03 18:18:27 +0900733 for (int i = 0; i < bits; i++) {
Jian Li4d138702018-11-27 17:25:28 +0900734 outBin.append(STR_ONE);
sangho2e97be02017-07-03 18:18:27 +0900735 }
736
Jian Li4d138702018-11-27 17:25:28 +0900737 return Integer.parseInt(outBin.toString(), MASK_RADIX);
sangho2e97be02017-07-03 18:18:27 +0900738 }
739
740 private int testMasks(String binStr, int start, int end) {
Jian Li4d138702018-11-27 17:25:28 +0900741 int mask = MASK_BEGIN_IDX;
742 for (; mask <= MASK_MAX_IDX; mask++) {
sangho2e97be02017-07-03 18:18:27 +0900743 int maskStart = binLower(binStr, mask);
744 int maskEnd = binHigher(binStr, mask);
745 if (maskStart < start || maskEnd > end) {
746 return mask - 1;
747 }
748 }
749
750 return mask;
751 }
752
753 private String getMask(int bits) {
754 switch (bits) {
755 case 0: return "ffff";
756 case 1: return "fffe";
757 case 2: return "fffc";
758 case 3: return "fff8";
759 case 4: return "fff0";
760 case 5: return "ffe0";
761 case 6: return "ffc0";
762 case 7: return "ff80";
763 case 8: return "ff00";
764 case 9: return "fe00";
765 case 10: return "fc00";
766 case 11: return "f800";
767 case 12: return "f000";
768 case 13: return "e000";
769 case 14: return "c000";
770 case 15: return "8000";
771 case 16: return "0000";
772 default: return null;
773 }
774 }
775
776 private Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
777
778 boolean processing = true;
779 int start = portMin;
780 Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
781 while (processing) {
782 String minStr = Integer.toBinaryString(start);
Jian Li4d138702018-11-27 17:25:28 +0900783 String binStrMinPadded = STR_PADDING.substring(minStr.length()) + minStr;
sangho2e97be02017-07-03 18:18:27 +0900784
785 int mask = testMasks(binStrMinPadded, start, portMax);
786 int maskStart = binLower(binStrMinPadded, mask);
787 int maskEnd = binHigher(binStrMinPadded, mask);
788
789 log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
Jian Libcc42282018-09-13 20:59:34 +0900790 portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(
Jian Li4d138702018-11-27 17:25:28 +0900791 Integer.parseInt(Objects.requireNonNull(getMask(mask)), PORT_RADIX)));
sangho2e97be02017-07-03 18:18:27 +0900792
793 start = maskEnd + 1;
794 if (start > portMax) {
795 processing = false;
796 }
797 }
798
799 return portMaskMap;
800 }
801
sangho6a9ff0d2017-03-27 11:23:37 +0900802 private class InternalInstancePortListener implements InstancePortListener {
803
804 @Override
805 public boolean isRelevant(InstancePortEvent event) {
Jian Li34220ea2018-11-14 01:30:24 +0900806 return useSecurityGroup;
807 }
808
809 private boolean isRelevantHelper(InstancePortEvent event) {
810 return mastershipService.isLocalMaster(event.subject().deviceId());
sangho6a9ff0d2017-03-27 11:23:37 +0900811 }
812
813 @Override
814 public void event(InstancePortEvent event) {
sangho6a9ff0d2017-03-27 11:23:37 +0900815 switch (event.type()) {
Jian Liec5c32b2018-07-13 14:28:58 +0900816 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Lie8b28db2018-10-17 14:04:09 +0900817 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liac30e272018-10-18 23:08:03 +0900818 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
Jian Li4d138702018-11-27 17:25:28 +0900819 eventExecutor.execute(() -> processInstanceMigrationStart(event));
Jian Liac30e272018-10-18 23:08:03 +0900820 break;
821 case OPENSTACK_INSTANCE_PORT_VANISHED:
Jian Li4d138702018-11-27 17:25:28 +0900822 eventExecutor.execute(() -> processInstancePortVanish(event));
sangho6a9ff0d2017-03-27 11:23:37 +0900823 break;
Jian Lib8cdcc12018-10-23 01:53:10 +0900824 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Li4d138702018-11-27 17:25:28 +0900825 eventExecutor.execute(() -> processInstanceMigrationEnd(event));
sangho6a9ff0d2017-03-27 11:23:37 +0900826 break;
sangho6a9ff0d2017-03-27 11:23:37 +0900827 default:
828 break;
829 }
830 }
Jian Liac30e272018-10-18 23:08:03 +0900831
Jian Li4d138702018-11-27 17:25:28 +0900832 private void processInstanceMigrationStart(InstancePortEvent event) {
833 if (!isRelevantHelper(event)) {
834 return;
835 }
836
837 InstancePort instPort = event.subject();
838 installSecurityGroupRules(event, instPort);
839 setAclRecircRules(instPort, true);
840 }
841
842 private void processInstancePortVanish(InstancePortEvent event) {
843 if (!isRelevantHelper(event)) {
844 return;
845 }
846
847 InstancePort instPort = event.subject();
848 Port osPort = removedOsPortStore.asJavaMap().get(instPort.portId());
849 setSecurityGroupRules(instPort, osPort, false);
850 removedOsPortStore.remove(instPort.portId());
851 setAclRecircRules(instPort, false);
852 }
853
854 private void processInstanceMigrationEnd(InstancePortEvent event) {
855 if (!isRelevantHelper(event)) {
856 return;
857 }
858
859 InstancePort instPort = event.subject();
860 InstancePort revisedInstPort = swapStaleLocation(instPort);
861 Port port = osNetService.port(instPort.portId());
862 setSecurityGroupRules(revisedInstPort, port, false);
863 setAclRecircRules(revisedInstPort, false);
864 }
865
Jian Liac30e272018-10-18 23:08:03 +0900866 private void installSecurityGroupRules(InstancePortEvent event,
867 InstancePort instPort) {
868 log.debug("Instance port detected/updated MAC:{} IP:{}",
869 instPort.macAddress(),
870 instPort.ipAddress());
Jian Li2b9838c2018-10-28 17:09:42 +0900871 eventExecutor.execute(() ->
872 setSecurityGroupRules(instPort,
873 osNetService.port(event.subject().portId()), true));
Jian Liac30e272018-10-18 23:08:03 +0900874 }
Jian Li1e9cb732018-11-25 23:17:21 +0900875
Jian Li4d138702018-11-27 17:25:28 +0900876 private void setSecurityGroupRules(InstancePort instPort,
877 Port port, boolean install) {
878 Port osPort = port;
879
880 if (!install) {
881 Port rmvPort = removedOsPortStore.asJavaMap().get(instPort.portId());
882 if (osPort == null && rmvPort == null) {
883 return;
884 }
885
886 if (port == null) {
887 osPort = rmvPort;
888 }
889 }
890
891 final Port finalPort = osPort;
892
893 osPort.getSecurityGroups().forEach(sgId -> {
894 SecurityGroup sg = securityGroupService.securityGroup(sgId);
895 if (sg == null) {
896 log.error("Security Group {} not found", sgId);
897 return;
898 }
899 sg.getRules().forEach(sgRule ->
900 updateSecurityGroupRule(instPort, finalPort, sgRule, install));
901 final String action = install ? "Installed " : "Removed ";
902 log.debug(action + "Security Group Rule ID : " + sgId);
903 });
904 }
905
Jian Li1e9cb732018-11-25 23:17:21 +0900906 private void setAclRecircRules(InstancePort instPort, boolean install) {
907 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
908
909 Network net = osNetService.network(instPort.networkId());
910 NetworkType netType = net.getNetworkType();
911 String segId = net.getProviderSegID();
912
913 switch (netType) {
914 case VXLAN:
Jian Li2d68c192018-12-13 15:52:59 +0900915 case GRE:
Jian Li1e9cb732018-11-25 23:17:21 +0900916 sBuilder.matchTunnelId(Long.valueOf(segId));
917 break;
918 case VLAN:
919 sBuilder.matchVlanId(VlanId.vlanId(segId));
920 break;
921 default:
922 break;
923 }
924
925 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
926 sBuilder.matchIPDst(IpPrefix.valueOf(instPort.ipAddress(), VM_IP_PREFIX));
927
928 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
929 tBuilder.transition(ACL_INGRESS_TABLE);
930
931 osFlowRuleService.setRule(
932 appId,
933 instPort.deviceId(),
934 sBuilder.build(),
935 tBuilder.build(),
936 PRIORITY_ACL_RULE,
937 ACL_RECIRC_TABLE,
938 install);
939 }
sangho6a9ff0d2017-03-27 11:23:37 +0900940 }
941
942 private class InternalOpenstackPortListener implements OpenstackNetworkListener {
943
944 @Override
945 public boolean isRelevant(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +0800946 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
sangho6a9ff0d2017-03-27 11:23:37 +0900947 return false;
948 }
Jian Libcc42282018-09-13 20:59:34 +0900949
Jian Li34220ea2018-11-14 01:30:24 +0900950 return useSecurityGroup;
951 }
952
953 private boolean isRelevantHelper(OpenstackNetworkEvent event) {
Jian Libcc42282018-09-13 20:59:34 +0900954 InstancePort instPort = instancePortService.instancePort(event.port().getId());
955
956 if (instPort == null) {
957 return false;
958 }
959
Jian Li34220ea2018-11-14 01:30:24 +0900960 return mastershipService.isLocalMaster(instPort.deviceId());
Jian Libcc42282018-09-13 20:59:34 +0900961 }
962
963 @Override
964 public void event(OpenstackNetworkEvent event) {
965 log.debug("openstack port event received {}", event);
Jian Libcc42282018-09-13 20:59:34 +0900966
Jian Li4d138702018-11-27 17:25:28 +0900967 if (event.type() == OPENSTACK_PORT_PRE_REMOVE) {
968 eventExecutor.execute(() -> processPortPreRemove(event));
Jian Libcc42282018-09-13 20:59:34 +0900969 }
970 }
Jian Li4d138702018-11-27 17:25:28 +0900971
972 private void processPortPreRemove(OpenstackNetworkEvent event) {
973 if (!isRelevantHelper(event)) {
974 return;
975 }
976
977 Port osPort = event.port();
978 removedOsPortStore.put(osPort.getId(), osPort);
979 }
Jian Libcc42282018-09-13 20:59:34 +0900980 }
981
Jian Li34220ea2018-11-14 01:30:24 +0900982 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
Jian Libcc42282018-09-13 20:59:34 +0900983
984 @Override
985 public boolean isRelevant(OpenstackNetworkEvent event) {
986 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
987 return false;
988 }
Jian Li2b9838c2018-10-28 17:09:42 +0900989
Jian Li34220ea2018-11-14 01:30:24 +0900990 return useSecurityGroup;
991 }
992
993 private boolean isRelevantHelper(OpenstackNetworkEvent event) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900994 if (event.securityGroupId() == null ||
995 securityGroupService.securityGroup(event.securityGroupId()) == null) {
996 return false;
997 }
Jian Li2b9838c2018-10-28 17:09:42 +0900998
999 InstancePort instPort = instancePortService.instancePort(event.port().getId());
1000
1001 if (instPort == null) {
Hyunsun Moonae51e732017-04-25 17:46:21 +09001002 return false;
1003 }
Jian Li2b9838c2018-10-28 17:09:42 +09001004
Jian Li34220ea2018-11-14 01:30:24 +09001005 return mastershipService.isLocalMaster(instPort.deviceId());
sangho6a9ff0d2017-03-27 11:23:37 +09001006 }
1007
1008 @Override
1009 public void event(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +08001010 log.debug("security group event received {}", event);
Hyunsun Moonae51e732017-04-25 17:46:21 +09001011
sangho6a9ff0d2017-03-27 11:23:37 +09001012 switch (event.type()) {
Hyunsun Moonae51e732017-04-25 17:46:21 +09001013 case OPENSTACK_PORT_SECURITY_GROUP_ADDED:
Jian Li4d138702018-11-27 17:25:28 +09001014 eventExecutor.execute(() -> processPortSgAdd(event));
sangho6a9ff0d2017-03-27 11:23:37 +09001015 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +09001016 case OPENSTACK_PORT_SECURITY_GROUP_REMOVED:
Jian Li4d138702018-11-27 17:25:28 +09001017 eventExecutor.execute(() -> processPortSgRemove(event));
sangho6a9ff0d2017-03-27 11:23:37 +09001018 break;
1019 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +09001020 // do nothing for the other events
sangho6a9ff0d2017-03-27 11:23:37 +09001021 break;
1022 }
1023 }
Jian Li4d138702018-11-27 17:25:28 +09001024
1025 private void processPortSgAdd(OpenstackNetworkEvent event) {
1026 if (!isRelevantHelper(event)) {
1027 return;
1028 }
1029
1030 InstancePort instPort = instancePortService.instancePort(event.port().getId());
1031 SecurityGroup osSg = securityGroupService.securityGroup(event.securityGroupId());
1032
1033 osSg.getRules().forEach(sgRule -> {
1034 updateSecurityGroupRule(instPort, event.port(), sgRule, true);
1035 });
1036 log.info("Added security group {} to port {}",
1037 event.securityGroupId(), event.port().getId());
1038 }
1039
1040 private void processPortSgRemove(OpenstackNetworkEvent event) {
1041 if (!isRelevantHelper(event)) {
1042 return;
1043 }
1044
1045 InstancePort instPort = instancePortService.instancePort(event.port().getId());
1046 SecurityGroup osSg = securityGroupService.securityGroup(event.securityGroupId());
1047
1048 osSg.getRules().forEach(sgRule -> {
1049 updateSecurityGroupRule(instPort, event.port(), sgRule, false);
1050 });
1051 log.info("Removed security group {} from port {}",
1052 event.securityGroupId(), event.port().getId());
1053 }
sangho6a9ff0d2017-03-27 11:23:37 +09001054 }
1055
Jian Li34220ea2018-11-14 01:30:24 +09001056 private class InternalSecurityGroupListener implements OpenstackSecurityGroupListener {
sangho6a9ff0d2017-03-27 11:23:37 +09001057
1058 @Override
sangho0248ca22017-05-31 13:22:47 +09001059 public boolean isRelevant(OpenstackSecurityGroupEvent event) {
Jian Libcc42282018-09-13 20:59:34 +09001060 return useSecurityGroup;
sangho0248ca22017-05-31 13:22:47 +09001061 }
1062
Jian Li34220ea2018-11-14 01:30:24 +09001063 private boolean isRelevantHelper() {
1064 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
1065 }
1066
sangho0248ca22017-05-31 13:22:47 +09001067 @Override
sangho6a9ff0d2017-03-27 11:23:37 +09001068 public void event(OpenstackSecurityGroupEvent event) {
1069 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +09001070 case OPENSTACK_SECURITY_GROUP_RULE_CREATED:
Jian Li4d138702018-11-27 17:25:28 +09001071 eventExecutor.execute(() -> processSgRuleCreate(event));
sangho6a9ff0d2017-03-27 11:23:37 +09001072 break;
sangho6a9ff0d2017-03-27 11:23:37 +09001073 case OPENSTACK_SECURITY_GROUP_RULE_REMOVED:
Jian Li4d138702018-11-27 17:25:28 +09001074 eventExecutor.execute(() -> processSgRuleRemove(event));
sangho6a9ff0d2017-03-27 11:23:37 +09001075 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +09001076 case OPENSTACK_SECURITY_GROUP_REMOVED:
Jian Li9d35bd62018-10-13 01:43:24 +09001077 case OPENSTACK_SECURITY_GROUP_CREATED:
sangho6a9ff0d2017-03-27 11:23:37 +09001078 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +09001079 // do nothing
1080 break;
sangho6a9ff0d2017-03-27 11:23:37 +09001081 }
1082 }
Jian Li4d138702018-11-27 17:25:28 +09001083
1084 private void processSgRuleCreate(OpenstackSecurityGroupEvent event) {
1085 if (!isRelevantHelper()) {
1086 return;
1087 }
1088
1089 SecurityGroupRule sgRuleToAdd = event.securityGroupRule();
1090 securityGroupRuleAdded(sgRuleToAdd);
1091 log.info("Applied new security group rule {} to ports", sgRuleToAdd.getId());
1092 }
1093
1094 private void processSgRuleRemove(OpenstackSecurityGroupEvent event) {
1095 if (!isRelevantHelper()) {
1096 return;
1097 }
1098
1099 SecurityGroupRule sgRuleToRemove = event.securityGroupRule();
1100 securityGroupRuleRemoved(sgRuleToRemove);
1101 log.info("Removed security group rule {} from ports", sgRuleToRemove.getId());
1102 }
sangho6a9ff0d2017-03-27 11:23:37 +09001103 }
sangho1aaa7882017-05-31 13:22:47 +09001104
1105 private class InternalNodeListener implements OpenstackNodeListener {
1106
1107 @Override
1108 public boolean isRelevant(OpenstackNodeEvent event) {
sangho1aaa7882017-05-31 13:22:47 +09001109 return event.subject().type() == COMPUTE;
1110 }
1111
Jian Li34220ea2018-11-14 01:30:24 +09001112 private boolean isRelevantHelper() {
1113 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
1114 }
1115
sangho1aaa7882017-05-31 13:22:47 +09001116 @Override
1117 public void event(OpenstackNodeEvent event) {
sangho1aaa7882017-05-31 13:22:47 +09001118 switch (event.type()) {
1119 case OPENSTACK_NODE_COMPLETE:
Jian Li4d138702018-11-27 17:25:28 +09001120 eventExecutor.execute(this::processNodeComplete);
sangho1aaa7882017-05-31 13:22:47 +09001121 break;
1122 case OPENSTACK_NODE_CREATED:
1123 case OPENSTACK_NODE_REMOVED:
1124 case OPENSTACK_NODE_UPDATED:
1125 case OPENSTACK_NODE_INCOMPLETE:
1126 default:
1127 break;
1128 }
1129 }
Jian Li4d138702018-11-27 17:25:28 +09001130
1131 private void processNodeComplete() {
1132 if (!isRelevantHelper()) {
1133 return;
1134 }
1135
1136 OpenstackSecurityGroupHandler.this.resetSecurityGroupRules();
1137 }
sangho1aaa7882017-05-31 13:22:47 +09001138 }
Jian Libcc42282018-09-13 20:59:34 +09001139}