blob: 114cc44933dc8813bc6f802716c112c240f5bfac [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;
sangho0248ca22017-05-31 13:22:47 +090028import org.onlab.util.Tools;
29import org.onosproject.cfg.ComponentConfigService;
sangho1aaa7882017-05-31 13:22:47 +090030import org.onosproject.cluster.ClusterService;
31import org.onosproject.cluster.LeadershipService;
32import org.onosproject.cluster.NodeId;
sangho6a9ff0d2017-03-27 11:23:37 +090033import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
35import org.onosproject.mastership.MastershipService;
sangho1aaa7882017-05-31 13:22:47 +090036import org.onosproject.net.DeviceId;
37import org.onosproject.net.driver.DriverService;
sangho6a9ff0d2017-03-27 11:23:37 +090038import org.onosproject.net.flow.DefaultTrafficSelector;
39import org.onosproject.net.flow.DefaultTrafficTreatment;
40import org.onosproject.net.flow.TrafficSelector;
sangho1aaa7882017-05-31 13:22:47 +090041import org.onosproject.net.flow.TrafficTreatment;
42import org.onosproject.net.flow.criteria.ExtensionSelector;
sangho6a9ff0d2017-03-27 11:23:37 +090043import org.onosproject.openstacknetworking.api.InstancePort;
44import org.onosproject.openstacknetworking.api.InstancePortEvent;
45import org.onosproject.openstacknetworking.api.InstancePortListener;
46import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090047import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
sangho6a9ff0d2017-03-27 11:23:37 +090048import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
49import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
50import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
51import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupEvent;
52import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupListener;
53import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
Jian Li26949762018-03-30 15:46:37 +090054import org.onosproject.openstacknetworking.util.RulePopulatorUtil;
sangho3dd2a8b2017-07-19 15:54:31 +090055import org.onosproject.openstacknode.api.OpenstackNode;
sangho1aaa7882017-05-31 13:22:47 +090056import org.onosproject.openstacknode.api.OpenstackNodeEvent;
57import org.onosproject.openstacknode.api.OpenstackNodeListener;
sangho3dd2a8b2017-07-19 15:54:31 +090058import org.onosproject.openstacknode.api.OpenstackNodeService;
sangho6a9ff0d2017-03-27 11:23:37 +090059import org.openstack4j.model.network.Port;
60import org.openstack4j.model.network.SecurityGroup;
61import org.openstack4j.model.network.SecurityGroupRule;
62import org.openstack4j.openstack.networking.domain.NeutronSecurityGroupRule;
sangho0248ca22017-05-31 13:22:47 +090063import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070064import org.osgi.service.component.annotations.Activate;
65import org.osgi.service.component.annotations.Component;
66import org.osgi.service.component.annotations.Deactivate;
67import org.osgi.service.component.annotations.Modified;
68import org.osgi.service.component.annotations.Reference;
69import org.osgi.service.component.annotations.ReferenceCardinality;
sangho6a9ff0d2017-03-27 11:23:37 +090070import org.slf4j.Logger;
71
sangho6a9ff0d2017-03-27 11:23:37 +090072import java.util.Collections;
sangho0248ca22017-05-31 13:22:47 +090073import java.util.Dictionary;
sangho2e97be02017-07-03 18:18:27 +090074import java.util.Map;
sangho6a9ff0d2017-03-27 11:23:37 +090075import java.util.Objects;
76import java.util.Set;
77import java.util.concurrent.ExecutorService;
78import java.util.stream.Collectors;
79
80import static java.util.concurrent.Executors.newSingleThreadExecutor;
81import static org.onlab.util.Tools.groupedThreads;
sanghodc375372017-06-08 10:41:30 +090082import static org.onosproject.openstacknetworking.api.Constants.ACL_TABLE;
sangho1aaa7882017-05-31 13:22:47 +090083import static org.onosproject.openstacknetworking.api.Constants.CT_TABLE;
84import static org.onosproject.openstacknetworking.api.Constants.ERROR_TABLE;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070085import static org.onosproject.openstacknetworking.api.Constants.JUMP_TABLE;
sangho6a9ff0d2017-03-27 11:23:37 +090086import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
87import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ACL_RULE;
sangho1aaa7882017-05-31 13:22:47 +090088import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_DROP_RULE;
89import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_HOOK_RULE;
90import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_RULE;
Jian Libcc42282018-09-13 20:59:34 +090091import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtMaskFlag;
92import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtStateFlag;
93import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.niciraConnTrackTreatmentBuilder;
sangho1aaa7882017-05-31 13:22:47 +090094import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
sangho6a9ff0d2017-03-27 11:23:37 +090095import static org.slf4j.LoggerFactory.getLogger;
96
97/**
98 * Populates flow rules to handle OpenStack SecurityGroups.
99 */
100@Component(immediate = true)
101public class OpenstackSecurityGroupHandler {
102
103 private final Logger log = getLogger(getClass());
104
sangho0248ca22017-05-31 13:22:47 +0900105 private static final boolean USE_SECURITY_GROUP = false;
106
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700107 //@Property(name = "useSecurityGroup", boolValue = USE_SECURITY_GROUP,
108 // label = "Apply OpenStack security group rule for VM traffic")
sangho0248ca22017-05-31 13:22:47 +0900109 private boolean useSecurityGroup = USE_SECURITY_GROUP;
110
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho6a9ff0d2017-03-27 11:23:37 +0900112 protected CoreService coreService;
113
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho6a9ff0d2017-03-27 11:23:37 +0900115 protected InstancePortService instancePortService;
116
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho6a9ff0d2017-03-27 11:23:37 +0900118 protected MastershipService mastershipService;
119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moonae51e732017-04-25 17:46:21 +0900121 protected OpenstackNetworkService osNetService;
sangho6a9ff0d2017-03-27 11:23:37 +0900122
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho6a9ff0d2017-03-27 11:23:37 +0900124 protected OpenstackSecurityGroupService securityGroupService;
125
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sanghodc375372017-06-08 10:41:30 +0900127 protected OpenstackFlowRuleService osFlowRuleService;
sangho6a9ff0d2017-03-27 11:23:37 +0900128
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho0248ca22017-05-31 13:22:47 +0900130 protected ComponentConfigService configService;
131
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho3dd2a8b2017-07-19 15:54:31 +0900133 protected OpenstackNodeService osNodeService;
134
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho1aaa7882017-05-31 13:22:47 +0900136 protected DriverService driverService;
137
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho1aaa7882017-05-31 13:22:47 +0900139 protected LeadershipService leadershipService;
140
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho1aaa7882017-05-31 13:22:47 +0900142 protected ClusterService clusterService;
143
Jian Libcc42282018-09-13 20:59:34 +0900144 private final InstancePortListener instancePortListener =
145 new InternalInstancePortListener();
146 private final OpenstackNetworkListener osNetworkListener =
147 new InternalOpenstackNetworkListener();
148 private final OpenstackNetworkListener osPortListener =
149 new InternalOpenstackPortListener();
150 private final OpenstackSecurityGroupListener securityGroupListener =
151 new InternalSecurityGroupListener();
sangho1aaa7882017-05-31 13:22:47 +0900152 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
sangho6a9ff0d2017-03-27 11:23:37 +0900153 private ApplicationId appId;
sangho1aaa7882017-05-31 13:22:47 +0900154 private NodeId localNodeId;
sangho6a9ff0d2017-03-27 11:23:37 +0900155
156 private final ExecutorService eventExecutor = newSingleThreadExecutor(
157 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
158
159 private static final String PROTO_ICMP = "ICMP";
160 private static final String PROTO_TCP = "TCP";
161 private static final String PROTO_UDP = "UDP";
162 private static final String ETHTYPE_IPV4 = "IPV4";
163 private static final String EGRESS = "EGRESS";
164 private static final String INGRESS = "INGRESS";
165 private static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
166
sangho1aaa7882017-05-31 13:22:47 +0900167 // We expose pipeline structure to SONA application considering removing pipeline soon.
sanghoshinbbeb31a2018-09-11 17:01:01 +0800168 private static final int GOTO_CONNTRACK_TABLE = CT_TABLE;
169 private static final int GOTO_JUMP_TABLE = JUMP_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900170
171 private static final int CT_COMMIT = 0;
172 private static final int CT_NO_COMMIT = 1;
173 private static final short CT_NO_RECIRC = -1;
174
175 private static final int ACTION_NONE = 0;
176 private static final int ACTION_DROP = -1;
177
sangho6a9ff0d2017-03-27 11:23:37 +0900178 @Activate
179 protected void activate() {
180 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
sangho1aaa7882017-05-31 13:22:47 +0900181 localNodeId = clusterService.getLocalNode().id();
sangho6a9ff0d2017-03-27 11:23:37 +0900182 instancePortService.addListener(instancePortListener);
183 securityGroupService.addListener(securityGroupListener);
Jian Libcc42282018-09-13 20:59:34 +0900184 osNetService.addListener(osPortListener);
185 osNetService.addListener(osNetworkListener);
sangho0248ca22017-05-31 13:22:47 +0900186 configService.registerProperties(getClass());
sangho1aaa7882017-05-31 13:22:47 +0900187 osNodeService.addListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900188
189 log.info("Started");
190 }
191
192 @Deactivate
193 protected void deactivate() {
194 instancePortService.removeListener(instancePortListener);
195 securityGroupService.removeListener(securityGroupListener);
Jian Libcc42282018-09-13 20:59:34 +0900196 osNetService.removeListener(osNetworkListener);
197 osNetService.removeListener(osPortListener);
sangho0248ca22017-05-31 13:22:47 +0900198 configService.unregisterProperties(getClass(), false);
sangho1aaa7882017-05-31 13:22:47 +0900199 osNodeService.removeListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900200 eventExecutor.shutdown();
201
202 log.info("Stopped");
203 }
204
sangho0248ca22017-05-31 13:22:47 +0900205 @Modified
206 protected void modified(ComponentContext context) {
207 Dictionary<?, ?> properties = context.getProperties();
208 Boolean flag;
209
210 flag = Tools.isPropertyEnabled(properties, "useSecurityGroup");
211 if (flag == null) {
212 log.info("useSecurityGroup is not configured, " +
213 "using current value of {}", useSecurityGroup);
214 } else {
215 useSecurityGroup = flag;
216 log.info("Configured. useSecurityGroup is {}",
217 useSecurityGroup ? "enabled" : "disabled");
218 }
219
sanghoe6457a32017-08-24 14:31:19 +0900220 securityGroupService.setSecurityGroupEnabled(useSecurityGroup);
sangho0248ca22017-05-31 13:22:47 +0900221 resetSecurityGroupRules();
222 }
223
sangho1aaa7882017-05-31 13:22:47 +0900224 private void initializeConnTrackTable(DeviceId deviceId, boolean install) {
225
226 //table=1,ip,ct_state=-trk, actions=ct(table:2)
Jian Libcc42282018-09-13 20:59:34 +0900227 long ctState = computeCtStateFlag(false, false, false);
228 long ctMask = computeCtMaskFlag(true, false, false);
sangho1aaa7882017-05-31 13:22:47 +0900229 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, (short) GOTO_CONNTRACK_TABLE,
230 ACTION_NONE, PRIORITY_CT_HOOK_RULE, install);
231
232 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+est,action=goto_table:3
Jian Libcc42282018-09-13 20:59:34 +0900233 ctState = computeCtStateFlag(true, false, true);
234 ctMask = computeCtMaskFlag(true, false, true);
sangho1aaa7882017-05-31 13:22:47 +0900235 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
236 GOTO_JUMP_TABLE, PRIORITY_CT_RULE, install);
237
238 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+new,action=drop
Jian Libcc42282018-09-13 20:59:34 +0900239 ctState = computeCtStateFlag(true, true, false);
240 ctMask = computeCtMaskFlag(true, true, false);
sangho1aaa7882017-05-31 13:22:47 +0900241 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
242 ACTION_DROP, PRIORITY_CT_DROP_RULE, install);
243 }
244
Jian Libcc42282018-09-13 20:59:34 +0900245 private void setSecurityGroupRules(InstancePort instPort,
246 Port port, boolean install) {
sangho6a9ff0d2017-03-27 11:23:37 +0900247 port.getSecurityGroups().forEach(sgId -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900248 SecurityGroup sg = securityGroupService.securityGroup(sgId);
249 if (sg == null) {
250 log.error("Security Group Not Found : {}", sgId);
251 return;
252 }
Jian Libcc42282018-09-13 20:59:34 +0900253 sg.getRules().forEach(sgRule ->
254 updateSecurityGroupRule(instPort, port, sgRule, install));
Hyunsun Moonae51e732017-04-25 17:46:21 +0900255 final String action = install ? "Installed " : "Removed ";
256 log.debug(action + "security group rule ID : " + sgId);
sangho6a9ff0d2017-03-27 11:23:37 +0900257 });
258 }
259
Jian Libcc42282018-09-13 20:59:34 +0900260 private void updateSecurityGroupRule(InstancePort instPort, Port port,
261 SecurityGroupRule sgRule, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900262
Daniel Park3a140592018-06-28 18:33:10 +0900263 if (instPort == null || port == null || sgRule == null) {
264 return;
265 }
266
sangho6a9ff0d2017-03-27 11:23:37 +0900267 if (sgRule.getRemoteGroupId() != null && !sgRule.getRemoteGroupId().isEmpty()) {
268 getRemoteInstPorts(port.getTenantId(), sgRule.getRemoteGroupId())
Jian Libcc42282018-09-13 20:59:34 +0900269 .forEach(rInstPort -> {
270 populateSecurityGroupRule(sgRule, instPort,
271 rInstPort.ipAddress().toIpPrefix(), install);
272 populateSecurityGroupRule(sgRule, rInstPort,
273 instPort.ipAddress().toIpPrefix(), install);
sangho6a9ff0d2017-03-27 11:23:37 +0900274
Jian Libcc42282018-09-13 20:59:34 +0900275 SecurityGroupRule rSgRule =
276 new NeutronSecurityGroupRule
277 .SecurityGroupRuleConcreteBuilder()
278 .from(sgRule)
279 .direction(sgRule.getDirection().toUpperCase()
280 .equals(EGRESS) ? INGRESS : EGRESS)
281 .build();
282 populateSecurityGroupRule(rSgRule, instPort,
283 rInstPort.ipAddress().toIpPrefix(), install);
284 populateSecurityGroupRule(rSgRule, rInstPort,
285 instPort.ipAddress().toIpPrefix(), install);
286 });
sangho6a9ff0d2017-03-27 11:23:37 +0900287 } else {
Jian Libcc42282018-09-13 20:59:34 +0900288 populateSecurityGroupRule(sgRule, instPort,
289 sgRule.getRemoteIpPrefix() == null ? IP_PREFIX_ANY :
sangho6a9ff0d2017-03-27 11:23:37 +0900290 IpPrefix.valueOf(sgRule.getRemoteIpPrefix()), install);
291 }
292 }
293
Jian Libcc42282018-09-13 20:59:34 +0900294 private void populateSecurityGroupRule(SecurityGroupRule sgRule,
295 InstancePort instPort,
sangho6a9ff0d2017-03-27 11:23:37 +0900296 IpPrefix remoteIp, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900297 Set<TrafficSelector> selectors = buildSelectors(sgRule,
298 Ip4Address.valueOf(instPort.ipAddress().toInetAddress()), remoteIp);
299 if (selectors == null || selectors.isEmpty()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900300 return;
301 }
302
sangho2e97be02017-07-03 18:18:27 +0900303 selectors.forEach(selector -> {
304 osFlowRuleService.setRule(appId,
305 instPort.deviceId(),
306 selector,
sangho3dd2a8b2017-07-19 15:54:31 +0900307 DefaultTrafficTreatment.builder().transition(JUMP_TABLE).build(),
sangho2e97be02017-07-03 18:18:27 +0900308 PRIORITY_ACL_RULE,
309 ACL_TABLE,
310 install);
311 });
sangho6a9ff0d2017-03-27 11:23:37 +0900312 }
313
314 /**
sangho1aaa7882017-05-31 13:22:47 +0900315 * Sets connection tracking rule using OVS extension commands.
316 * It is not so graceful, but I don't want to make it more general because it is going to be used
Ray Milkeyc108a6b2017-08-23 15:23:50 -0700317 * only here. The following is the usage of the function.
sangho1aaa7882017-05-31 13:22:47 +0900318 *
319 * @param deviceId Device ID
320 * @param ctState ctState: please use RulePopulatorUtil.computeCtStateFlag() to build the value
321 * @param ctMask crMask: please use RulePopulatorUtil.computeCtMaskFlag() to build the value
322 * @param commit CT_COMMIT for commit action, CT_NO_COMMIT otherwise
323 * @param recircTable table number for recirculation after CT actions. CT_NO_RECIRC with no recirculation
324 * @param action Additional actions. ACTION_DROP, ACTION_NONE, GOTO_XXX_TABLE are supported.
325 * @param priority priority value for the rule
326 * @param install true for insertion, false for removal
327 */
328 private void setConnTrackRule(DeviceId deviceId, long ctState, long ctMask,
329 int commit, short recircTable,
330 int action, int priority, boolean install) {
331
Jian Libcc42282018-09-13 20:59:34 +0900332 ExtensionSelector esCtSate = RulePopulatorUtil
333 .buildCtExtensionSelector(driverService, deviceId, ctState, ctMask);
sangho1aaa7882017-05-31 13:22:47 +0900334 TrafficSelector selector = DefaultTrafficSelector.builder()
335 .extension(esCtSate, deviceId)
336 .matchEthType(Ethernet.TYPE_IPV4)
337 .build();
338
339 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
340
341 if (commit == CT_COMMIT || recircTable > 0) {
342 RulePopulatorUtil.NiriraConnTrackTreatmentBuilder natTreatmentBuilder =
Jian Libcc42282018-09-13 20:59:34 +0900343 niciraConnTrackTreatmentBuilder(driverService, deviceId);
sangho1aaa7882017-05-31 13:22:47 +0900344 natTreatmentBuilder.natAction(false);
345 if (commit == CT_COMMIT) {
346 natTreatmentBuilder.commit(true);
347 } else {
348 natTreatmentBuilder.commit(false);
349 }
350 if (recircTable > 0) {
351 natTreatmentBuilder.table(recircTable);
352 }
353 tb.extension(natTreatmentBuilder.build(), deviceId);
354 } else if (action == ACTION_DROP) {
355 tb.drop();
356 }
357
sanghoshinbbeb31a2018-09-11 17:01:01 +0800358 if (action != ACTION_NONE && action != ACTION_DROP) {
sangho1aaa7882017-05-31 13:22:47 +0900359 tb.transition(action);
360 }
361
362 int tableType = ERROR_TABLE;
363 if (priority == PRIORITY_CT_RULE || priority == PRIORITY_CT_DROP_RULE) {
364 tableType = CT_TABLE;
365 } else if (priority == PRIORITY_CT_HOOK_RULE) {
366 tableType = ACL_TABLE;
367 } else {
368 log.error("Cannot an appropriate table for the conn track rule.");
369 }
370
371 osFlowRuleService.setRule(
372 appId,
373 deviceId,
374 selector,
375 tb.build(),
376 priority,
377 tableType,
378 install);
379 }
380
381 /**
sangho6a9ff0d2017-03-27 11:23:37 +0900382 * Returns a set of host IP addresses engaged with supplied security group ID.
383 * It only searches a VM in the same tenant boundary.
384 *
385 * @param tenantId tenant id
386 * @param sgId security group id
387 * @return set of ip addresses
388 */
389 private Set<InstancePort> getRemoteInstPorts(String tenantId, String sgId) {
390 Set<InstancePort> remoteInstPorts;
391
Hyunsun Moonae51e732017-04-25 17:46:21 +0900392 remoteInstPorts = osNetService.ports().stream()
sangho6a9ff0d2017-03-27 11:23:37 +0900393 .filter(port -> port.getTenantId().equals(tenantId))
394 .filter(port -> port.getSecurityGroups().contains(sgId))
395 .map(port -> instancePortService.instancePort(port.getId()))
396 .filter(instPort -> instPort != null && instPort.ipAddress() != null)
397 .collect(Collectors.toSet());
398
399 return Collections.unmodifiableSet(remoteInstPorts);
400 }
401
sangho2e97be02017-07-03 18:18:27 +0900402 private Set<TrafficSelector> buildSelectors(SecurityGroupRule sgRule,
403 Ip4Address vmIp,
404 IpPrefix remoteIp) {
405 if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, 32))) {
406 // do nothing if the remote IP is my IP
407 return null;
408 }
409
410 Set<TrafficSelector> selectorSet = Sets.newHashSet();
411
412 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
Jian Libcc42282018-09-13 20:59:34 +0900413 buildMatches(sBuilder, sgRule, vmIp, remoteIp);
sangho2e97be02017-07-03 18:18:27 +0900414
415 if (sgRule.getPortRangeMax() != null && sgRule.getPortRangeMin() != null &&
416 sgRule.getPortRangeMin() < sgRule.getPortRangeMax()) {
Jian Libcc42282018-09-13 20:59:34 +0900417 Map<TpPort, TpPort> portRangeMatchMap =
418 buildPortRangeMatches(sgRule.getPortRangeMin(),
sangho2e97be02017-07-03 18:18:27 +0900419 sgRule.getPortRangeMax());
420 portRangeMatchMap.entrySet().forEach(entry -> {
421
Jian Libcc42282018-09-13 20:59:34 +0900422 if (sgRule.getProtocol().toUpperCase().equals(PROTO_TCP)) {
423 if (sgRule.getDirection().toUpperCase().equals(EGRESS)) {
424 sBuilder.matchTcpSrcMasked(entry.getKey(), entry.getValue());
425 } else {
426 sBuilder.matchTcpDstMasked(entry.getKey(), entry.getValue());
427 }
428 } else if (sgRule.getProtocol().toUpperCase().equals(PROTO_UDP)) {
429 if (sgRule.getDirection().toUpperCase().equals(EGRESS)) {
430 sBuilder.matchUdpSrcMasked(entry.getKey(), entry.getValue());
431 } else {
432 sBuilder.matchUdpDstMasked(entry.getKey(), entry.getValue());
433 }
434 }
sangho2e97be02017-07-03 18:18:27 +0900435
Jian Libcc42282018-09-13 20:59:34 +0900436 selectorSet.add(sBuilder.build());
437 }
sangho2e97be02017-07-03 18:18:27 +0900438 );
439 } else {
440 selectorSet.add(sBuilder.build());
441 }
442
443 return selectorSet;
444 }
445
Jian Libcc42282018-09-13 20:59:34 +0900446 private void buildMatches(TrafficSelector.Builder sBuilder,
447 SecurityGroupRule sgRule,
448 Ip4Address vmIp, IpPrefix remoteIp) {
sangho6a9ff0d2017-03-27 11:23:37 +0900449 buildMatchEthType(sBuilder, sgRule.getEtherType());
450 buildMatchDirection(sBuilder, sgRule.getDirection(), vmIp);
451 buildMatchProto(sBuilder, sgRule.getProtocol());
452 buildMatchPort(sBuilder, sgRule.getProtocol(), sgRule.getDirection(),
sangho2e97be02017-07-03 18:18:27 +0900453 sgRule.getPortRangeMin() == null ? 0 : sgRule.getPortRangeMin(),
454 sgRule.getPortRangeMax() == null ? 0 : sgRule.getPortRangeMax());
sangho6a9ff0d2017-03-27 11:23:37 +0900455 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
456 if (sgRule.getRemoteGroupId() != null && sgRule.getRemoteGroupId().isEmpty()) {
457 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
458 }
459 }
460
461 private void buildMatchDirection(TrafficSelector.Builder sBuilder,
462 String direction,
463 Ip4Address vmIp) {
464 if (direction.toUpperCase().equals(EGRESS)) {
465 sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, 32));
466 } else {
467 sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, 32));
468 }
469 }
470
471 private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
472 // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
473 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
474 if (etherType != null && !Objects.equals(etherType, "null") &&
475 !etherType.toUpperCase().equals(ETHTYPE_IPV4)) {
476 log.debug("EthType {} is not supported yet in Security Group", etherType);
477 }
478 }
479
Jian Libcc42282018-09-13 20:59:34 +0900480 private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder,
481 IpPrefix remoteIpPrefix, String direction) {
482 if (remoteIpPrefix != null &&
483 !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900484 if (direction.toUpperCase().equals(EGRESS)) {
485 sBuilder.matchIPDst(remoteIpPrefix);
486 } else {
487 sBuilder.matchIPSrc(remoteIpPrefix);
488 }
489 }
490 }
491
492 private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
493 if (protocol != null) {
494 switch (protocol.toUpperCase()) {
495 case PROTO_ICMP:
496 sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
497 break;
498 case PROTO_TCP:
499 sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
500 break;
501 case PROTO_UDP:
502 sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
503 break;
504 default:
505 }
506 }
507 }
508
Jian Libcc42282018-09-13 20:59:34 +0900509 private void buildMatchPort(TrafficSelector.Builder sBuilder,
510 String protocol, String direction,
sangho6a9ff0d2017-03-27 11:23:37 +0900511 int portMin, int portMax) {
512 if (portMin > 0 && portMax > 0 && portMin == portMax) {
513 if (protocol.toUpperCase().equals(PROTO_TCP)) {
514 if (direction.toUpperCase().equals(EGRESS)) {
515 sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
516 } else {
517 sBuilder.matchTcpDst(TpPort.tpPort(portMax));
518 }
519 } else if (protocol.toUpperCase().equals(PROTO_UDP)) {
520 if (direction.toUpperCase().equals(EGRESS)) {
521 sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
522 } else {
523 sBuilder.matchUdpDst(TpPort.tpPort(portMax));
524 }
525 }
526 }
527 }
528
sangho0248ca22017-05-31 13:22:47 +0900529 private void resetSecurityGroupRules() {
530
531 if (useSecurityGroup) {
sangho3dd2a8b2017-07-19 15:54:31 +0900532 osNodeService.completeNodes(OpenstackNode.NodeType.COMPUTE)
Jian Libcc42282018-09-13 20:59:34 +0900533 .forEach(node -> osFlowRuleService
534 .setUpTableMissEntry(node.intgBridge(), ACL_TABLE));
sangho0248ca22017-05-31 13:22:47 +0900535 securityGroupService.securityGroups().forEach(securityGroup ->
536 securityGroup.getRules().forEach(this::securityGroupRuleAdded));
sangho1aaa7882017-05-31 13:22:47 +0900537 osNodeService.nodes().stream()
538 .filter(node -> node.type().equals(OpenstackNode.NodeType.COMPUTE))
539 .forEach(node -> initializeConnTrackTable(node .intgBridge(), true));
sangho0248ca22017-05-31 13:22:47 +0900540 } else {
sangho3dd2a8b2017-07-19 15:54:31 +0900541 osNodeService.completeNodes(OpenstackNode.NodeType.COMPUTE)
Jian Libcc42282018-09-13 20:59:34 +0900542 .forEach(node -> osFlowRuleService
543 .connectTables(node.intgBridge(), ACL_TABLE, JUMP_TABLE));
sangho0248ca22017-05-31 13:22:47 +0900544 securityGroupService.securityGroups().forEach(securityGroup ->
545 securityGroup.getRules().forEach(this::securityGroupRuleRemoved));
sangho1aaa7882017-05-31 13:22:47 +0900546 osNodeService.nodes().stream()
547 .filter(node -> node.type().equals(OpenstackNode.NodeType.COMPUTE))
548 .forEach(node -> initializeConnTrackTable(node.intgBridge(), false));
sangho0248ca22017-05-31 13:22:47 +0900549 }
550
Jian Libcc42282018-09-13 20:59:34 +0900551 log.info("Reset security group info " +
552 (useSecurityGroup ? " with " : " without") + " Security Group");
sangho0248ca22017-05-31 13:22:47 +0900553 }
554
555 private void securityGroupRuleAdded(SecurityGroupRule sgRule) {
556 osNetService.ports().stream()
Jian Libcc42282018-09-13 20:59:34 +0900557 .filter(port -> port.getSecurityGroups()
558 .contains(sgRule.getSecurityGroupId()))
sangho0248ca22017-05-31 13:22:47 +0900559 .forEach(port -> {
560 updateSecurityGroupRule(
561 instancePortService.instancePort(port.getId()),
562 port, sgRule, true);
563 log.debug("Applied security group rule {} to port {}",
564 sgRule.getId(), port.getId());
565 });
566 }
567
568 private void securityGroupRuleRemoved(SecurityGroupRule sgRule) {
569 osNetService.ports().stream()
Jian Libcc42282018-09-13 20:59:34 +0900570 .filter(port -> port.getSecurityGroups()
571 .contains(sgRule.getSecurityGroupId()))
sangho0248ca22017-05-31 13:22:47 +0900572 .forEach(port -> {
573 updateSecurityGroupRule(
574 instancePortService.instancePort(port.getId()),
575 port, sgRule, false);
576 log.debug("Removed security group rule {} from port {}",
577 sgRule.getId(), port.getId());
578 });
579 }
580
sangho2e97be02017-07-03 18:18:27 +0900581 private int binLower(String binStr, int bits) {
582 String outBin = binStr.substring(0, 16 - bits);
583 for (int i = 0; i < bits; i++) {
584 outBin += "0";
585 }
586
587 return Integer.parseInt(outBin, 2);
588 }
589
590 private int binHigher(String binStr, int bits) {
591 String outBin = binStr.substring(0, 16 - bits);
592 for (int i = 0; i < bits; i++) {
593 outBin += "1";
594 }
595
596 return Integer.parseInt(outBin, 2);
597 }
598
599 private int testMasks(String binStr, int start, int end) {
600 int mask = 0;
601 for (; mask <= 16; mask++) {
602 int maskStart = binLower(binStr, mask);
603 int maskEnd = binHigher(binStr, mask);
604 if (maskStart < start || maskEnd > end) {
605 return mask - 1;
606 }
607 }
608
609 return mask;
610 }
611
612 private String getMask(int bits) {
613 switch (bits) {
614 case 0: return "ffff";
615 case 1: return "fffe";
616 case 2: return "fffc";
617 case 3: return "fff8";
618 case 4: return "fff0";
619 case 5: return "ffe0";
620 case 6: return "ffc0";
621 case 7: return "ff80";
622 case 8: return "ff00";
623 case 9: return "fe00";
624 case 10: return "fc00";
625 case 11: return "f800";
626 case 12: return "f000";
627 case 13: return "e000";
628 case 14: return "c000";
629 case 15: return "8000";
630 case 16: return "0000";
631 default: return null;
632 }
633 }
634
635 private Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
636
637 boolean processing = true;
638 int start = portMin;
639 Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
640 while (processing) {
641 String minStr = Integer.toBinaryString(start);
642 String binStrMinPadded = "0000000000000000".substring(minStr.length()) + minStr;
643
644 int mask = testMasks(binStrMinPadded, start, portMax);
645 int maskStart = binLower(binStrMinPadded, mask);
646 int maskEnd = binHigher(binStrMinPadded, mask);
647
648 log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
Jian Libcc42282018-09-13 20:59:34 +0900649 portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(
650 Integer.parseInt(Objects.requireNonNull(getMask(mask)), 16)));
sangho2e97be02017-07-03 18:18:27 +0900651
652 start = maskEnd + 1;
653 if (start > portMax) {
654 processing = false;
655 }
656 }
657
658 return portMaskMap;
659 }
660
sangho6a9ff0d2017-03-27 11:23:37 +0900661 private class InternalInstancePortListener implements InstancePortListener {
662
663 @Override
664 public boolean isRelevant(InstancePortEvent event) {
665 InstancePort instPort = event.subject();
sangho0248ca22017-05-31 13:22:47 +0900666 if (!useSecurityGroup) {
667 return false;
668 }
sangho6a9ff0d2017-03-27 11:23:37 +0900669 return mastershipService.isLocalMaster(instPort.deviceId());
670 }
671
672 @Override
673 public void event(InstancePortEvent event) {
674 InstancePort instPort = event.subject();
675 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900676 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900677 case OPENSTACK_INSTANCE_PORT_UPDATED:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900678 log.debug("Instance port detected MAC:{} IP:{}",
679 instPort.macAddress(),
680 instPort.ipAddress());
sangho6a9ff0d2017-03-27 11:23:37 +0900681 eventExecutor.execute(() -> {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900682 setSecurityGroupRules(instPort,
683 osNetService.port(event.subject().portId()),
684 true);
sangho6a9ff0d2017-03-27 11:23:37 +0900685 });
686 break;
sangho6a9ff0d2017-03-27 11:23:37 +0900687 default:
688 break;
689 }
690 }
sangho6a9ff0d2017-03-27 11:23:37 +0900691 }
692
693 private class InternalOpenstackPortListener implements OpenstackNetworkListener {
694
695 @Override
696 public boolean isRelevant(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +0800697 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
sangho6a9ff0d2017-03-27 11:23:37 +0900698 return false;
699 }
Jian Libcc42282018-09-13 20:59:34 +0900700
701 InstancePort instPort = instancePortService.instancePort(event.port().getId());
702
703 if (instPort == null) {
704 return false;
705 }
706
707 return useSecurityGroup && mastershipService.isLocalMaster(instPort.deviceId());
708 }
709
710 @Override
711 public void event(OpenstackNetworkEvent event) {
712 log.debug("openstack port event received {}", event);
713 Port osPort = event.port();
714 InstancePort instPort = instancePortService.instancePort(osPort.getId());
715
716 switch (event.type()) {
717 case OPENSTACK_PORT_PRE_REMOVE:
718 eventExecutor.execute(() -> {
719 setSecurityGroupRules(instPort, osPort, false);
720 });
721 break;
722 default:
723 // do nothing for the other events
724 break;
725 }
726 }
727 }
728
729 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
730
731 @Override
732 public boolean isRelevant(OpenstackNetworkEvent event) {
733 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
734 return false;
735 }
Hyunsun Moonae51e732017-04-25 17:46:21 +0900736 if (event.securityGroupId() == null ||
737 securityGroupService.securityGroup(event.securityGroupId()) == null) {
738 return false;
739 }
740 if (instancePortService.instancePort(event.port().getId()) == null) {
741 return false;
742 }
Jian Libcc42282018-09-13 20:59:34 +0900743 return useSecurityGroup;
sangho6a9ff0d2017-03-27 11:23:37 +0900744 }
745
746 @Override
747 public void event(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +0800748 log.debug("security group event received {}", event);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900749 Port osPort = event.port();
750 InstancePort instPort = instancePortService.instancePort(osPort.getId());
751 SecurityGroup osSg = securityGroupService.securityGroup(event.securityGroupId());
752
sangho6a9ff0d2017-03-27 11:23:37 +0900753 switch (event.type()) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900754 case OPENSTACK_PORT_SECURITY_GROUP_ADDED:
755 eventExecutor.execute(() -> {
756 osSg.getRules().forEach(sgRule -> {
757 updateSecurityGroupRule(instPort, osPort, sgRule, true);
758 });
759 log.info("Added security group {} to port {}",
760 event.securityGroupId(), event.port().getId());
761 });
sangho6a9ff0d2017-03-27 11:23:37 +0900762 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +0900763 case OPENSTACK_PORT_SECURITY_GROUP_REMOVED:
764 eventExecutor.execute(() -> {
765 osSg.getRules().forEach(sgRule -> {
766 updateSecurityGroupRule(instPort, osPort, sgRule, false);
767 });
768 log.info("Removed security group {} from port {}",
769 event.securityGroupId(), event.port().getId());
770 });
sangho6a9ff0d2017-03-27 11:23:37 +0900771 break;
772 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900773 // do nothing for the other events
sangho6a9ff0d2017-03-27 11:23:37 +0900774 break;
775 }
776 }
sangho6a9ff0d2017-03-27 11:23:37 +0900777 }
778
779 private class InternalSecurityGroupListener implements OpenstackSecurityGroupListener {
780
781 @Override
sangho0248ca22017-05-31 13:22:47 +0900782 public boolean isRelevant(OpenstackSecurityGroupEvent event) {
Jian Libcc42282018-09-13 20:59:34 +0900783 return useSecurityGroup;
sangho0248ca22017-05-31 13:22:47 +0900784 }
785
786 @Override
sangho6a9ff0d2017-03-27 11:23:37 +0900787 public void event(OpenstackSecurityGroupEvent event) {
788 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900789 case OPENSTACK_SECURITY_GROUP_RULE_CREATED:
790 SecurityGroupRule securityGroupRuleToAdd = event.securityGroupRule();
791 eventExecutor.execute(() -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900792 securityGroupRuleAdded(securityGroupRuleToAdd);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900793 log.info("Applied new security group rule {} to ports",
794 securityGroupRuleToAdd.getId());
sangho6a9ff0d2017-03-27 11:23:37 +0900795 });
796 break;
797
798 case OPENSTACK_SECURITY_GROUP_RULE_REMOVED:
799 SecurityGroupRule securityGroupRuleToRemove = event.securityGroupRule();
800 eventExecutor.execute(() -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900801 securityGroupRuleRemoved(securityGroupRuleToRemove);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900802 log.info("Removed security group rule {} from ports",
803 securityGroupRuleToRemove.getId());
sangho6a9ff0d2017-03-27 11:23:37 +0900804 });
805 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +0900806 case OPENSTACK_SECURITY_GROUP_CREATED:
807 case OPENSTACK_SECURITY_GROUP_REMOVED:
sangho6a9ff0d2017-03-27 11:23:37 +0900808 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900809 // do nothing
810 break;
sangho6a9ff0d2017-03-27 11:23:37 +0900811 }
812 }
sangho6a9ff0d2017-03-27 11:23:37 +0900813 }
sangho1aaa7882017-05-31 13:22:47 +0900814
815 private class InternalNodeListener implements OpenstackNodeListener {
816
817 @Override
818 public boolean isRelevant(OpenstackNodeEvent event) {
819 // do not allow to proceed without leadership
820 NodeId leader = leadershipService.getLeader(appId.name());
821 if (!Objects.equals(localNodeId, leader)) {
822 return false;
823 }
824 return event.subject().type() == COMPUTE;
825 }
826
827 @Override
828 public void event(OpenstackNodeEvent event) {
829 OpenstackNode osNode = event.subject();
830
831 switch (event.type()) {
832 case OPENSTACK_NODE_COMPLETE:
833 eventExecutor.execute(() -> {
834 try {
835 if (useSecurityGroup) {
836 initializeConnTrackTable(osNode.intgBridge(), true);
Jian Li0488c732018-09-14 20:53:07 +0900837 log.info("SG table initialization : {} is done",
Jian Libcc42282018-09-13 20:59:34 +0900838 osNode.intgBridge());
sangho1aaa7882017-05-31 13:22:47 +0900839 }
840 } catch (IllegalArgumentException e) {
Jian Libcc42282018-09-13 20:59:34 +0900841 log.error("ACL table initialization error : {}",
842 e.getMessage());
sangho1aaa7882017-05-31 13:22:47 +0900843 }
844 });
845 break;
846 case OPENSTACK_NODE_CREATED:
847 case OPENSTACK_NODE_REMOVED:
848 case OPENSTACK_NODE_UPDATED:
849 case OPENSTACK_NODE_INCOMPLETE:
850 default:
851 break;
852 }
853 }
854 }
Jian Libcc42282018-09-13 20:59:34 +0900855}