blob: 55f9c459ff9fb97e39945b756229792d35cf3fc9 [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.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
sangho0248ca22017-05-31 13:22:47 +090025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
sangho6a9ff0d2017-03-27 11:23:37 +090027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import org.onlab.packet.Ethernet;
30import org.onlab.packet.IPv4;
31import org.onlab.packet.Ip4Address;
32import org.onlab.packet.Ip4Prefix;
33import org.onlab.packet.IpPrefix;
34import org.onlab.packet.TpPort;
sangho0248ca22017-05-31 13:22:47 +090035import org.onlab.util.Tools;
36import org.onosproject.cfg.ComponentConfigService;
sangho1aaa7882017-05-31 13:22:47 +090037import org.onosproject.cluster.ClusterService;
38import org.onosproject.cluster.LeadershipService;
39import org.onosproject.cluster.NodeId;
sangho6a9ff0d2017-03-27 11:23:37 +090040import org.onosproject.core.ApplicationId;
41import org.onosproject.core.CoreService;
42import org.onosproject.mastership.MastershipService;
sangho1aaa7882017-05-31 13:22:47 +090043import org.onosproject.net.DeviceId;
44import org.onosproject.net.driver.DriverService;
sangho6a9ff0d2017-03-27 11:23:37 +090045import org.onosproject.net.flow.DefaultTrafficSelector;
46import org.onosproject.net.flow.DefaultTrafficTreatment;
47import org.onosproject.net.flow.TrafficSelector;
sangho1aaa7882017-05-31 13:22:47 +090048import org.onosproject.net.flow.TrafficTreatment;
49import org.onosproject.net.flow.criteria.ExtensionSelector;
sangho6a9ff0d2017-03-27 11:23:37 +090050import org.onosproject.openstacknetworking.api.InstancePort;
51import org.onosproject.openstacknetworking.api.InstancePortEvent;
52import org.onosproject.openstacknetworking.api.InstancePortListener;
53import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090054import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
sangho6a9ff0d2017-03-27 11:23:37 +090055import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
56import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
57import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
58import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupEvent;
59import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupListener;
60import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
Jian Li26949762018-03-30 15:46:37 +090061import org.onosproject.openstacknetworking.util.RulePopulatorUtil;
sangho3dd2a8b2017-07-19 15:54:31 +090062import org.onosproject.openstacknode.api.OpenstackNode;
sangho1aaa7882017-05-31 13:22:47 +090063import org.onosproject.openstacknode.api.OpenstackNodeEvent;
64import org.onosproject.openstacknode.api.OpenstackNodeListener;
sangho3dd2a8b2017-07-19 15:54:31 +090065import org.onosproject.openstacknode.api.OpenstackNodeService;
sangho6a9ff0d2017-03-27 11:23:37 +090066import org.openstack4j.model.network.Port;
67import org.openstack4j.model.network.SecurityGroup;
68import org.openstack4j.model.network.SecurityGroupRule;
69import org.openstack4j.openstack.networking.domain.NeutronSecurityGroupRule;
sangho0248ca22017-05-31 13:22:47 +090070import org.osgi.service.component.ComponentContext;
sangho6a9ff0d2017-03-27 11:23:37 +090071import org.slf4j.Logger;
72
sangho6a9ff0d2017-03-27 11:23:37 +090073import java.util.Collections;
sangho0248ca22017-05-31 13:22:47 +090074import java.util.Dictionary;
sangho2e97be02017-07-03 18:18:27 +090075import java.util.Map;
sangho6a9ff0d2017-03-27 11:23:37 +090076import java.util.Objects;
77import java.util.Set;
78import java.util.concurrent.ExecutorService;
79import java.util.stream.Collectors;
80
81import static java.util.concurrent.Executors.newSingleThreadExecutor;
82import static org.onlab.util.Tools.groupedThreads;
sanghodc375372017-06-08 10:41:30 +090083import static org.onosproject.openstacknetworking.api.Constants.ACL_TABLE;
sangho3dd2a8b2017-07-19 15:54:31 +090084import static org.onosproject.openstacknetworking.api.Constants.JUMP_TABLE;
sangho1aaa7882017-05-31 13:22:47 +090085import static org.onosproject.openstacknetworking.api.Constants.CT_TABLE;
86import static org.onosproject.openstacknetworking.api.Constants.ERROR_TABLE;
sangho6a9ff0d2017-03-27 11:23:37 +090087import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
88import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ACL_RULE;
sangho1aaa7882017-05-31 13:22:47 +090089import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_DROP_RULE;
90import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_HOOK_RULE;
91import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_RULE;
Jian Libcc42282018-09-13 20:59:34 +090092import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtMaskFlag;
93import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtStateFlag;
94import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.niciraConnTrackTreatmentBuilder;
sangho1aaa7882017-05-31 13:22:47 +090095import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
sangho6a9ff0d2017-03-27 11:23:37 +090096import static org.slf4j.LoggerFactory.getLogger;
97
98/**
99 * Populates flow rules to handle OpenStack SecurityGroups.
100 */
101@Component(immediate = true)
102public class OpenstackSecurityGroupHandler {
103
104 private final Logger log = getLogger(getClass());
105
sangho0248ca22017-05-31 13:22:47 +0900106 private static final boolean USE_SECURITY_GROUP = false;
107
108 @Property(name = "useSecurityGroup", boolValue = USE_SECURITY_GROUP,
109 label = "Apply OpenStack security group rule for VM traffic")
110 private boolean useSecurityGroup = USE_SECURITY_GROUP;
111
sangho6a9ff0d2017-03-27 11:23:37 +0900112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected CoreService coreService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected InstancePortService instancePortService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected MastershipService mastershipService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moonae51e732017-04-25 17:46:21 +0900122 protected OpenstackNetworkService osNetService;
sangho6a9ff0d2017-03-27 11:23:37 +0900123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected OpenstackSecurityGroupService securityGroupService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sanghodc375372017-06-08 10:41:30 +0900128 protected OpenstackFlowRuleService osFlowRuleService;
sangho6a9ff0d2017-03-27 11:23:37 +0900129
sangho0248ca22017-05-31 13:22:47 +0900130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected ComponentConfigService configService;
132
sangho3dd2a8b2017-07-19 15:54:31 +0900133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected OpenstackNodeService osNodeService;
135
sanghoe6457a32017-08-24 14:31:19 +0900136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho1aaa7882017-05-31 13:22:47 +0900137 protected DriverService driverService;
138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected LeadershipService leadershipService;
141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
143 protected ClusterService clusterService;
144
Jian Libcc42282018-09-13 20:59:34 +0900145 private final InstancePortListener instancePortListener =
146 new InternalInstancePortListener();
147 private final OpenstackNetworkListener osNetworkListener =
148 new InternalOpenstackNetworkListener();
149 private final OpenstackNetworkListener osPortListener =
150 new InternalOpenstackPortListener();
151 private final OpenstackSecurityGroupListener securityGroupListener =
152 new InternalSecurityGroupListener();
sangho1aaa7882017-05-31 13:22:47 +0900153 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
sangho6a9ff0d2017-03-27 11:23:37 +0900154 private ApplicationId appId;
sangho1aaa7882017-05-31 13:22:47 +0900155 private NodeId localNodeId;
sangho6a9ff0d2017-03-27 11:23:37 +0900156
157 private final ExecutorService eventExecutor = newSingleThreadExecutor(
158 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
159
160 private static final String PROTO_ICMP = "ICMP";
161 private static final String PROTO_TCP = "TCP";
162 private static final String PROTO_UDP = "UDP";
163 private static final String ETHTYPE_IPV4 = "IPV4";
164 private static final String EGRESS = "EGRESS";
165 private static final String INGRESS = "INGRESS";
166 private static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
167
sangho1aaa7882017-05-31 13:22:47 +0900168 // We expose pipeline structure to SONA application considering removing pipeline soon.
sanghoshinbbeb31a2018-09-11 17:01:01 +0800169 private static final int GOTO_CONNTRACK_TABLE = CT_TABLE;
170 private static final int GOTO_JUMP_TABLE = JUMP_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900171
172 private static final int CT_COMMIT = 0;
173 private static final int CT_NO_COMMIT = 1;
174 private static final short CT_NO_RECIRC = -1;
175
176 private static final int ACTION_NONE = 0;
177 private static final int ACTION_DROP = -1;
178
sangho6a9ff0d2017-03-27 11:23:37 +0900179 @Activate
180 protected void activate() {
181 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
sangho1aaa7882017-05-31 13:22:47 +0900182 localNodeId = clusterService.getLocalNode().id();
sangho6a9ff0d2017-03-27 11:23:37 +0900183 instancePortService.addListener(instancePortListener);
184 securityGroupService.addListener(securityGroupListener);
Jian Libcc42282018-09-13 20:59:34 +0900185 osNetService.addListener(osPortListener);
186 osNetService.addListener(osNetworkListener);
sangho0248ca22017-05-31 13:22:47 +0900187 configService.registerProperties(getClass());
sangho1aaa7882017-05-31 13:22:47 +0900188 osNodeService.addListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900189
190 log.info("Started");
191 }
192
193 @Deactivate
194 protected void deactivate() {
195 instancePortService.removeListener(instancePortListener);
196 securityGroupService.removeListener(securityGroupListener);
Jian Libcc42282018-09-13 20:59:34 +0900197 osNetService.removeListener(osNetworkListener);
198 osNetService.removeListener(osPortListener);
sangho0248ca22017-05-31 13:22:47 +0900199 configService.unregisterProperties(getClass(), false);
sangho1aaa7882017-05-31 13:22:47 +0900200 osNodeService.removeListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900201 eventExecutor.shutdown();
202
203 log.info("Stopped");
204 }
205
sangho0248ca22017-05-31 13:22:47 +0900206 @Modified
207 protected void modified(ComponentContext context) {
208 Dictionary<?, ?> properties = context.getProperties();
209 Boolean flag;
210
211 flag = Tools.isPropertyEnabled(properties, "useSecurityGroup");
212 if (flag == null) {
213 log.info("useSecurityGroup is not configured, " +
214 "using current value of {}", useSecurityGroup);
215 } else {
216 useSecurityGroup = flag;
217 log.info("Configured. useSecurityGroup is {}",
218 useSecurityGroup ? "enabled" : "disabled");
219 }
220
sanghoe6457a32017-08-24 14:31:19 +0900221 securityGroupService.setSecurityGroupEnabled(useSecurityGroup);
sangho0248ca22017-05-31 13:22:47 +0900222 resetSecurityGroupRules();
223 }
224
sangho1aaa7882017-05-31 13:22:47 +0900225 private void initializeConnTrackTable(DeviceId deviceId, boolean install) {
226
227 //table=1,ip,ct_state=-trk, actions=ct(table:2)
Jian Libcc42282018-09-13 20:59:34 +0900228 long ctState = computeCtStateFlag(false, false, false);
229 long ctMask = computeCtMaskFlag(true, false, false);
sangho1aaa7882017-05-31 13:22:47 +0900230 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, (short) GOTO_CONNTRACK_TABLE,
231 ACTION_NONE, PRIORITY_CT_HOOK_RULE, install);
232
233 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+est,action=goto_table:3
Jian Libcc42282018-09-13 20:59:34 +0900234 ctState = computeCtStateFlag(true, false, true);
235 ctMask = computeCtMaskFlag(true, false, true);
sangho1aaa7882017-05-31 13:22:47 +0900236 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
237 GOTO_JUMP_TABLE, PRIORITY_CT_RULE, install);
238
239 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+new,action=drop
Jian Libcc42282018-09-13 20:59:34 +0900240 ctState = computeCtStateFlag(true, true, false);
241 ctMask = computeCtMaskFlag(true, true, false);
sangho1aaa7882017-05-31 13:22:47 +0900242 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
243 ACTION_DROP, PRIORITY_CT_DROP_RULE, install);
244 }
245
Jian Libcc42282018-09-13 20:59:34 +0900246 private void setSecurityGroupRules(InstancePort instPort,
247 Port port, boolean install) {
sangho6a9ff0d2017-03-27 11:23:37 +0900248 port.getSecurityGroups().forEach(sgId -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900249 SecurityGroup sg = securityGroupService.securityGroup(sgId);
250 if (sg == null) {
251 log.error("Security Group Not Found : {}", sgId);
252 return;
253 }
Jian Libcc42282018-09-13 20:59:34 +0900254 sg.getRules().forEach(sgRule ->
255 updateSecurityGroupRule(instPort, port, sgRule, install));
Hyunsun Moonae51e732017-04-25 17:46:21 +0900256 final String action = install ? "Installed " : "Removed ";
257 log.debug(action + "security group rule ID : " + sgId);
sangho6a9ff0d2017-03-27 11:23:37 +0900258 });
259 }
260
Jian Libcc42282018-09-13 20:59:34 +0900261 private void updateSecurityGroupRule(InstancePort instPort, Port port,
262 SecurityGroupRule sgRule, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900263
Daniel Park3a140592018-06-28 18:33:10 +0900264 if (instPort == null || port == null || sgRule == null) {
265 return;
266 }
267
sangho6a9ff0d2017-03-27 11:23:37 +0900268 if (sgRule.getRemoteGroupId() != null && !sgRule.getRemoteGroupId().isEmpty()) {
269 getRemoteInstPorts(port.getTenantId(), sgRule.getRemoteGroupId())
Jian Libcc42282018-09-13 20:59:34 +0900270 .forEach(rInstPort -> {
271 populateSecurityGroupRule(sgRule, instPort,
272 rInstPort.ipAddress().toIpPrefix(), install);
273 populateSecurityGroupRule(sgRule, rInstPort,
274 instPort.ipAddress().toIpPrefix(), install);
sangho6a9ff0d2017-03-27 11:23:37 +0900275
Jian Libcc42282018-09-13 20:59:34 +0900276 SecurityGroupRule rSgRule =
277 new NeutronSecurityGroupRule
278 .SecurityGroupRuleConcreteBuilder()
279 .from(sgRule)
280 .direction(sgRule.getDirection().toUpperCase()
281 .equals(EGRESS) ? INGRESS : EGRESS)
282 .build();
283 populateSecurityGroupRule(rSgRule, instPort,
284 rInstPort.ipAddress().toIpPrefix(), install);
285 populateSecurityGroupRule(rSgRule, rInstPort,
286 instPort.ipAddress().toIpPrefix(), install);
287 });
sangho6a9ff0d2017-03-27 11:23:37 +0900288 } else {
Jian Libcc42282018-09-13 20:59:34 +0900289 populateSecurityGroupRule(sgRule, instPort,
290 sgRule.getRemoteIpPrefix() == null ? IP_PREFIX_ANY :
sangho6a9ff0d2017-03-27 11:23:37 +0900291 IpPrefix.valueOf(sgRule.getRemoteIpPrefix()), install);
292 }
293 }
294
Jian Libcc42282018-09-13 20:59:34 +0900295 private void populateSecurityGroupRule(SecurityGroupRule sgRule,
296 InstancePort instPort,
sangho6a9ff0d2017-03-27 11:23:37 +0900297 IpPrefix remoteIp, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900298 Set<TrafficSelector> selectors = buildSelectors(sgRule,
299 Ip4Address.valueOf(instPort.ipAddress().toInetAddress()), remoteIp);
300 if (selectors == null || selectors.isEmpty()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900301 return;
302 }
303
sangho2e97be02017-07-03 18:18:27 +0900304 selectors.forEach(selector -> {
305 osFlowRuleService.setRule(appId,
306 instPort.deviceId(),
307 selector,
sangho3dd2a8b2017-07-19 15:54:31 +0900308 DefaultTrafficTreatment.builder().transition(JUMP_TABLE).build(),
sangho2e97be02017-07-03 18:18:27 +0900309 PRIORITY_ACL_RULE,
310 ACL_TABLE,
311 install);
312 });
sangho6a9ff0d2017-03-27 11:23:37 +0900313 }
314
315 /**
sangho1aaa7882017-05-31 13:22:47 +0900316 * Sets connection tracking rule using OVS extension commands.
317 * 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 -0700318 * only here. The following is the usage of the function.
sangho1aaa7882017-05-31 13:22:47 +0900319 *
320 * @param deviceId Device ID
321 * @param ctState ctState: please use RulePopulatorUtil.computeCtStateFlag() to build the value
322 * @param ctMask crMask: please use RulePopulatorUtil.computeCtMaskFlag() to build the value
323 * @param commit CT_COMMIT for commit action, CT_NO_COMMIT otherwise
324 * @param recircTable table number for recirculation after CT actions. CT_NO_RECIRC with no recirculation
325 * @param action Additional actions. ACTION_DROP, ACTION_NONE, GOTO_XXX_TABLE are supported.
326 * @param priority priority value for the rule
327 * @param install true for insertion, false for removal
328 */
329 private void setConnTrackRule(DeviceId deviceId, long ctState, long ctMask,
330 int commit, short recircTable,
331 int action, int priority, boolean install) {
332
Jian Libcc42282018-09-13 20:59:34 +0900333 ExtensionSelector esCtSate = RulePopulatorUtil
334 .buildCtExtensionSelector(driverService, deviceId, ctState, ctMask);
sangho1aaa7882017-05-31 13:22:47 +0900335 TrafficSelector selector = DefaultTrafficSelector.builder()
336 .extension(esCtSate, deviceId)
337 .matchEthType(Ethernet.TYPE_IPV4)
338 .build();
339
340 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
341
342 if (commit == CT_COMMIT || recircTable > 0) {
343 RulePopulatorUtil.NiriraConnTrackTreatmentBuilder natTreatmentBuilder =
Jian Libcc42282018-09-13 20:59:34 +0900344 niciraConnTrackTreatmentBuilder(driverService, deviceId);
sangho1aaa7882017-05-31 13:22:47 +0900345 natTreatmentBuilder.natAction(false);
346 if (commit == CT_COMMIT) {
347 natTreatmentBuilder.commit(true);
348 } else {
349 natTreatmentBuilder.commit(false);
350 }
351 if (recircTable > 0) {
352 natTreatmentBuilder.table(recircTable);
353 }
354 tb.extension(natTreatmentBuilder.build(), deviceId);
355 } else if (action == ACTION_DROP) {
356 tb.drop();
357 }
358
sanghoshinbbeb31a2018-09-11 17:01:01 +0800359 if (action != ACTION_NONE && action != ACTION_DROP) {
sangho1aaa7882017-05-31 13:22:47 +0900360 tb.transition(action);
361 }
362
363 int tableType = ERROR_TABLE;
364 if (priority == PRIORITY_CT_RULE || priority == PRIORITY_CT_DROP_RULE) {
365 tableType = CT_TABLE;
366 } else if (priority == PRIORITY_CT_HOOK_RULE) {
367 tableType = ACL_TABLE;
368 } else {
369 log.error("Cannot an appropriate table for the conn track rule.");
370 }
371
372 osFlowRuleService.setRule(
373 appId,
374 deviceId,
375 selector,
376 tb.build(),
377 priority,
378 tableType,
379 install);
380 }
381
382 /**
sangho6a9ff0d2017-03-27 11:23:37 +0900383 * Returns a set of host IP addresses engaged with supplied security group ID.
384 * It only searches a VM in the same tenant boundary.
385 *
386 * @param tenantId tenant id
387 * @param sgId security group id
388 * @return set of ip addresses
389 */
390 private Set<InstancePort> getRemoteInstPorts(String tenantId, String sgId) {
391 Set<InstancePort> remoteInstPorts;
392
Hyunsun Moonae51e732017-04-25 17:46:21 +0900393 remoteInstPorts = osNetService.ports().stream()
sangho6a9ff0d2017-03-27 11:23:37 +0900394 .filter(port -> port.getTenantId().equals(tenantId))
395 .filter(port -> port.getSecurityGroups().contains(sgId))
396 .map(port -> instancePortService.instancePort(port.getId()))
397 .filter(instPort -> instPort != null && instPort.ipAddress() != null)
398 .collect(Collectors.toSet());
399
400 return Collections.unmodifiableSet(remoteInstPorts);
401 }
402
sangho2e97be02017-07-03 18:18:27 +0900403 private Set<TrafficSelector> buildSelectors(SecurityGroupRule sgRule,
404 Ip4Address vmIp,
405 IpPrefix remoteIp) {
406 if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, 32))) {
407 // do nothing if the remote IP is my IP
408 return null;
409 }
410
411 Set<TrafficSelector> selectorSet = Sets.newHashSet();
412
413 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
Jian Libcc42282018-09-13 20:59:34 +0900414 buildMatches(sBuilder, sgRule, vmIp, remoteIp);
sangho2e97be02017-07-03 18:18:27 +0900415
416 if (sgRule.getPortRangeMax() != null && sgRule.getPortRangeMin() != null &&
417 sgRule.getPortRangeMin() < sgRule.getPortRangeMax()) {
Jian Libcc42282018-09-13 20:59:34 +0900418 Map<TpPort, TpPort> portRangeMatchMap =
419 buildPortRangeMatches(sgRule.getPortRangeMin(),
sangho2e97be02017-07-03 18:18:27 +0900420 sgRule.getPortRangeMax());
421 portRangeMatchMap.entrySet().forEach(entry -> {
422
Jian Libcc42282018-09-13 20:59:34 +0900423 if (sgRule.getProtocol().toUpperCase().equals(PROTO_TCP)) {
424 if (sgRule.getDirection().toUpperCase().equals(EGRESS)) {
425 sBuilder.matchTcpSrcMasked(entry.getKey(), entry.getValue());
426 } else {
427 sBuilder.matchTcpDstMasked(entry.getKey(), entry.getValue());
428 }
429 } else if (sgRule.getProtocol().toUpperCase().equals(PROTO_UDP)) {
430 if (sgRule.getDirection().toUpperCase().equals(EGRESS)) {
431 sBuilder.matchUdpSrcMasked(entry.getKey(), entry.getValue());
432 } else {
433 sBuilder.matchUdpDstMasked(entry.getKey(), entry.getValue());
434 }
435 }
sangho2e97be02017-07-03 18:18:27 +0900436
Jian Libcc42282018-09-13 20:59:34 +0900437 selectorSet.add(sBuilder.build());
438 }
sangho2e97be02017-07-03 18:18:27 +0900439 );
440 } else {
441 selectorSet.add(sBuilder.build());
442 }
443
444 return selectorSet;
445 }
446
Jian Libcc42282018-09-13 20:59:34 +0900447 private void buildMatches(TrafficSelector.Builder sBuilder,
448 SecurityGroupRule sgRule,
449 Ip4Address vmIp, IpPrefix remoteIp) {
sangho6a9ff0d2017-03-27 11:23:37 +0900450 buildMatchEthType(sBuilder, sgRule.getEtherType());
451 buildMatchDirection(sBuilder, sgRule.getDirection(), vmIp);
452 buildMatchProto(sBuilder, sgRule.getProtocol());
453 buildMatchPort(sBuilder, sgRule.getProtocol(), sgRule.getDirection(),
sangho2e97be02017-07-03 18:18:27 +0900454 sgRule.getPortRangeMin() == null ? 0 : sgRule.getPortRangeMin(),
455 sgRule.getPortRangeMax() == null ? 0 : sgRule.getPortRangeMax());
sangho6a9ff0d2017-03-27 11:23:37 +0900456 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
457 if (sgRule.getRemoteGroupId() != null && sgRule.getRemoteGroupId().isEmpty()) {
458 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
459 }
460 }
461
462 private void buildMatchDirection(TrafficSelector.Builder sBuilder,
463 String direction,
464 Ip4Address vmIp) {
465 if (direction.toUpperCase().equals(EGRESS)) {
466 sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, 32));
467 } else {
468 sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, 32));
469 }
470 }
471
472 private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
473 // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
474 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
475 if (etherType != null && !Objects.equals(etherType, "null") &&
476 !etherType.toUpperCase().equals(ETHTYPE_IPV4)) {
477 log.debug("EthType {} is not supported yet in Security Group", etherType);
478 }
479 }
480
Jian Libcc42282018-09-13 20:59:34 +0900481 private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder,
482 IpPrefix remoteIpPrefix, String direction) {
483 if (remoteIpPrefix != null &&
484 !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900485 if (direction.toUpperCase().equals(EGRESS)) {
486 sBuilder.matchIPDst(remoteIpPrefix);
487 } else {
488 sBuilder.matchIPSrc(remoteIpPrefix);
489 }
490 }
491 }
492
493 private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
494 if (protocol != null) {
495 switch (protocol.toUpperCase()) {
496 case PROTO_ICMP:
497 sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
498 break;
499 case PROTO_TCP:
500 sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
501 break;
502 case PROTO_UDP:
503 sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
504 break;
505 default:
506 }
507 }
508 }
509
Jian Libcc42282018-09-13 20:59:34 +0900510 private void buildMatchPort(TrafficSelector.Builder sBuilder,
511 String protocol, String direction,
sangho6a9ff0d2017-03-27 11:23:37 +0900512 int portMin, int portMax) {
513 if (portMin > 0 && portMax > 0 && portMin == portMax) {
514 if (protocol.toUpperCase().equals(PROTO_TCP)) {
515 if (direction.toUpperCase().equals(EGRESS)) {
516 sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
517 } else {
518 sBuilder.matchTcpDst(TpPort.tpPort(portMax));
519 }
520 } else if (protocol.toUpperCase().equals(PROTO_UDP)) {
521 if (direction.toUpperCase().equals(EGRESS)) {
522 sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
523 } else {
524 sBuilder.matchUdpDst(TpPort.tpPort(portMax));
525 }
526 }
527 }
528 }
529
sangho0248ca22017-05-31 13:22:47 +0900530 private void resetSecurityGroupRules() {
531
532 if (useSecurityGroup) {
sangho3dd2a8b2017-07-19 15:54:31 +0900533 osNodeService.completeNodes(OpenstackNode.NodeType.COMPUTE)
Jian Libcc42282018-09-13 20:59:34 +0900534 .forEach(node -> osFlowRuleService
535 .setUpTableMissEntry(node.intgBridge(), ACL_TABLE));
sangho0248ca22017-05-31 13:22:47 +0900536 securityGroupService.securityGroups().forEach(securityGroup ->
537 securityGroup.getRules().forEach(this::securityGroupRuleAdded));
sangho1aaa7882017-05-31 13:22:47 +0900538 osNodeService.nodes().stream()
539 .filter(node -> node.type().equals(OpenstackNode.NodeType.COMPUTE))
540 .forEach(node -> initializeConnTrackTable(node .intgBridge(), true));
sangho0248ca22017-05-31 13:22:47 +0900541 } else {
sangho3dd2a8b2017-07-19 15:54:31 +0900542 osNodeService.completeNodes(OpenstackNode.NodeType.COMPUTE)
Jian Libcc42282018-09-13 20:59:34 +0900543 .forEach(node -> osFlowRuleService
544 .connectTables(node.intgBridge(), ACL_TABLE, JUMP_TABLE));
sangho0248ca22017-05-31 13:22:47 +0900545 securityGroupService.securityGroups().forEach(securityGroup ->
546 securityGroup.getRules().forEach(this::securityGroupRuleRemoved));
sangho1aaa7882017-05-31 13:22:47 +0900547 osNodeService.nodes().stream()
548 .filter(node -> node.type().equals(OpenstackNode.NodeType.COMPUTE))
549 .forEach(node -> initializeConnTrackTable(node.intgBridge(), false));
sangho0248ca22017-05-31 13:22:47 +0900550 }
551
Jian Libcc42282018-09-13 20:59:34 +0900552 log.info("Reset security group info " +
553 (useSecurityGroup ? " with " : " without") + " Security Group");
sangho0248ca22017-05-31 13:22:47 +0900554 }
555
556 private void securityGroupRuleAdded(SecurityGroupRule sgRule) {
557 osNetService.ports().stream()
Jian Libcc42282018-09-13 20:59:34 +0900558 .filter(port -> port.getSecurityGroups()
559 .contains(sgRule.getSecurityGroupId()))
sangho0248ca22017-05-31 13:22:47 +0900560 .forEach(port -> {
561 updateSecurityGroupRule(
562 instancePortService.instancePort(port.getId()),
563 port, sgRule, true);
564 log.debug("Applied security group rule {} to port {}",
565 sgRule.getId(), port.getId());
566 });
567 }
568
569 private void securityGroupRuleRemoved(SecurityGroupRule sgRule) {
570 osNetService.ports().stream()
Jian Libcc42282018-09-13 20:59:34 +0900571 .filter(port -> port.getSecurityGroups()
572 .contains(sgRule.getSecurityGroupId()))
sangho0248ca22017-05-31 13:22:47 +0900573 .forEach(port -> {
574 updateSecurityGroupRule(
575 instancePortService.instancePort(port.getId()),
576 port, sgRule, false);
577 log.debug("Removed security group rule {} from port {}",
578 sgRule.getId(), port.getId());
579 });
580 }
581
sangho2e97be02017-07-03 18:18:27 +0900582 private int binLower(String binStr, int bits) {
583 String outBin = binStr.substring(0, 16 - bits);
584 for (int i = 0; i < bits; i++) {
585 outBin += "0";
586 }
587
588 return Integer.parseInt(outBin, 2);
589 }
590
591 private int binHigher(String binStr, int bits) {
592 String outBin = binStr.substring(0, 16 - bits);
593 for (int i = 0; i < bits; i++) {
594 outBin += "1";
595 }
596
597 return Integer.parseInt(outBin, 2);
598 }
599
600 private int testMasks(String binStr, int start, int end) {
601 int mask = 0;
602 for (; mask <= 16; mask++) {
603 int maskStart = binLower(binStr, mask);
604 int maskEnd = binHigher(binStr, mask);
605 if (maskStart < start || maskEnd > end) {
606 return mask - 1;
607 }
608 }
609
610 return mask;
611 }
612
613 private String getMask(int bits) {
614 switch (bits) {
615 case 0: return "ffff";
616 case 1: return "fffe";
617 case 2: return "fffc";
618 case 3: return "fff8";
619 case 4: return "fff0";
620 case 5: return "ffe0";
621 case 6: return "ffc0";
622 case 7: return "ff80";
623 case 8: return "ff00";
624 case 9: return "fe00";
625 case 10: return "fc00";
626 case 11: return "f800";
627 case 12: return "f000";
628 case 13: return "e000";
629 case 14: return "c000";
630 case 15: return "8000";
631 case 16: return "0000";
632 default: return null;
633 }
634 }
635
636 private Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
637
638 boolean processing = true;
639 int start = portMin;
640 Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
641 while (processing) {
642 String minStr = Integer.toBinaryString(start);
643 String binStrMinPadded = "0000000000000000".substring(minStr.length()) + minStr;
644
645 int mask = testMasks(binStrMinPadded, start, portMax);
646 int maskStart = binLower(binStrMinPadded, mask);
647 int maskEnd = binHigher(binStrMinPadded, mask);
648
649 log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
Jian Libcc42282018-09-13 20:59:34 +0900650 portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(
651 Integer.parseInt(Objects.requireNonNull(getMask(mask)), 16)));
sangho2e97be02017-07-03 18:18:27 +0900652
653 start = maskEnd + 1;
654 if (start > portMax) {
655 processing = false;
656 }
657 }
658
659 return portMaskMap;
660 }
661
sangho6a9ff0d2017-03-27 11:23:37 +0900662 private class InternalInstancePortListener implements InstancePortListener {
663
664 @Override
665 public boolean isRelevant(InstancePortEvent event) {
666 InstancePort instPort = event.subject();
sangho0248ca22017-05-31 13:22:47 +0900667 if (!useSecurityGroup) {
668 return false;
669 }
sangho6a9ff0d2017-03-27 11:23:37 +0900670 return mastershipService.isLocalMaster(instPort.deviceId());
671 }
672
673 @Override
674 public void event(InstancePortEvent event) {
675 InstancePort instPort = event.subject();
676 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900677 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900678 case OPENSTACK_INSTANCE_PORT_UPDATED:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900679 log.debug("Instance port detected MAC:{} IP:{}",
680 instPort.macAddress(),
681 instPort.ipAddress());
sangho6a9ff0d2017-03-27 11:23:37 +0900682 eventExecutor.execute(() -> {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900683 setSecurityGroupRules(instPort,
684 osNetService.port(event.subject().portId()),
685 true);
sangho6a9ff0d2017-03-27 11:23:37 +0900686 });
687 break;
sangho6a9ff0d2017-03-27 11:23:37 +0900688 default:
689 break;
690 }
691 }
sangho6a9ff0d2017-03-27 11:23:37 +0900692 }
693
694 private class InternalOpenstackPortListener implements OpenstackNetworkListener {
695
696 @Override
697 public boolean isRelevant(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +0800698 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
sangho6a9ff0d2017-03-27 11:23:37 +0900699 return false;
700 }
Jian Libcc42282018-09-13 20:59:34 +0900701
702 InstancePort instPort = instancePortService.instancePort(event.port().getId());
703
704 if (instPort == null) {
705 return false;
706 }
707
708 return useSecurityGroup && mastershipService.isLocalMaster(instPort.deviceId());
709 }
710
711 @Override
712 public void event(OpenstackNetworkEvent event) {
713 log.debug("openstack port event received {}", event);
714 Port osPort = event.port();
715 InstancePort instPort = instancePortService.instancePort(osPort.getId());
716
717 switch (event.type()) {
718 case OPENSTACK_PORT_PRE_REMOVE:
719 eventExecutor.execute(() -> {
720 setSecurityGroupRules(instPort, osPort, false);
721 });
722 break;
723 default:
724 // do nothing for the other events
725 break;
726 }
727 }
728 }
729
730 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
731
732 @Override
733 public boolean isRelevant(OpenstackNetworkEvent event) {
734 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
735 return false;
736 }
Hyunsun Moonae51e732017-04-25 17:46:21 +0900737 if (event.securityGroupId() == null ||
738 securityGroupService.securityGroup(event.securityGroupId()) == null) {
739 return false;
740 }
741 if (instancePortService.instancePort(event.port().getId()) == null) {
742 return false;
743 }
Jian Libcc42282018-09-13 20:59:34 +0900744 return useSecurityGroup;
sangho6a9ff0d2017-03-27 11:23:37 +0900745 }
746
747 @Override
748 public void event(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +0800749 log.debug("security group event received {}", event);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900750 Port osPort = event.port();
751 InstancePort instPort = instancePortService.instancePort(osPort.getId());
752 SecurityGroup osSg = securityGroupService.securityGroup(event.securityGroupId());
753
sangho6a9ff0d2017-03-27 11:23:37 +0900754 switch (event.type()) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900755 case OPENSTACK_PORT_SECURITY_GROUP_ADDED:
756 eventExecutor.execute(() -> {
757 osSg.getRules().forEach(sgRule -> {
758 updateSecurityGroupRule(instPort, osPort, sgRule, true);
759 });
760 log.info("Added security group {} to port {}",
761 event.securityGroupId(), event.port().getId());
762 });
sangho6a9ff0d2017-03-27 11:23:37 +0900763 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +0900764 case OPENSTACK_PORT_SECURITY_GROUP_REMOVED:
765 eventExecutor.execute(() -> {
766 osSg.getRules().forEach(sgRule -> {
767 updateSecurityGroupRule(instPort, osPort, sgRule, false);
768 });
769 log.info("Removed security group {} from port {}",
770 event.securityGroupId(), event.port().getId());
771 });
sangho6a9ff0d2017-03-27 11:23:37 +0900772 break;
773 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900774 // do nothing for the other events
sangho6a9ff0d2017-03-27 11:23:37 +0900775 break;
776 }
777 }
sangho6a9ff0d2017-03-27 11:23:37 +0900778 }
779
780 private class InternalSecurityGroupListener implements OpenstackSecurityGroupListener {
781
782 @Override
sangho0248ca22017-05-31 13:22:47 +0900783 public boolean isRelevant(OpenstackSecurityGroupEvent event) {
Jian Libcc42282018-09-13 20:59:34 +0900784 return useSecurityGroup;
sangho0248ca22017-05-31 13:22:47 +0900785 }
786
787 @Override
sangho6a9ff0d2017-03-27 11:23:37 +0900788 public void event(OpenstackSecurityGroupEvent event) {
789 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900790 case OPENSTACK_SECURITY_GROUP_RULE_CREATED:
791 SecurityGroupRule securityGroupRuleToAdd = event.securityGroupRule();
792 eventExecutor.execute(() -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900793 securityGroupRuleAdded(securityGroupRuleToAdd);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900794 log.info("Applied new security group rule {} to ports",
795 securityGroupRuleToAdd.getId());
sangho6a9ff0d2017-03-27 11:23:37 +0900796 });
797 break;
798
799 case OPENSTACK_SECURITY_GROUP_RULE_REMOVED:
800 SecurityGroupRule securityGroupRuleToRemove = event.securityGroupRule();
801 eventExecutor.execute(() -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900802 securityGroupRuleRemoved(securityGroupRuleToRemove);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900803 log.info("Removed security group rule {} from ports",
804 securityGroupRuleToRemove.getId());
sangho6a9ff0d2017-03-27 11:23:37 +0900805 });
806 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +0900807 case OPENSTACK_SECURITY_GROUP_CREATED:
808 case OPENSTACK_SECURITY_GROUP_REMOVED:
sangho6a9ff0d2017-03-27 11:23:37 +0900809 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900810 // do nothing
811 break;
sangho6a9ff0d2017-03-27 11:23:37 +0900812 }
813 }
sangho6a9ff0d2017-03-27 11:23:37 +0900814 }
sangho1aaa7882017-05-31 13:22:47 +0900815
816 private class InternalNodeListener implements OpenstackNodeListener {
817
818 @Override
819 public boolean isRelevant(OpenstackNodeEvent event) {
820 // do not allow to proceed without leadership
821 NodeId leader = leadershipService.getLeader(appId.name());
822 if (!Objects.equals(localNodeId, leader)) {
823 return false;
824 }
825 return event.subject().type() == COMPUTE;
826 }
827
828 @Override
829 public void event(OpenstackNodeEvent event) {
830 OpenstackNode osNode = event.subject();
831
832 switch (event.type()) {
833 case OPENSTACK_NODE_COMPLETE:
834 eventExecutor.execute(() -> {
835 try {
836 if (useSecurityGroup) {
837 initializeConnTrackTable(osNode.intgBridge(), true);
Jian Li0488c732018-09-14 20:53:07 +0900838 log.info("SG table initialization : {} is done",
Jian Libcc42282018-09-13 20:59:34 +0900839 osNode.intgBridge());
sangho1aaa7882017-05-31 13:22:47 +0900840 }
841 } catch (IllegalArgumentException e) {
Jian Libcc42282018-09-13 20:59:34 +0900842 log.error("ACL table initialization error : {}",
843 e.getMessage());
sangho1aaa7882017-05-31 13:22:47 +0900844 }
845 });
846 break;
847 case OPENSTACK_NODE_CREATED:
848 case OPENSTACK_NODE_REMOVED:
849 case OPENSTACK_NODE_UPDATED:
850 case OPENSTACK_NODE_INCOMPLETE:
851 default:
852 break;
853 }
854 }
855 }
Jian Libcc42282018-09-13 20:59:34 +0900856}