blob: d3bc4d4b74ff04670f05164d48ea5314541ff4cb [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;
Jian Li9d35bd62018-10-13 01:43:24 +090051import org.onosproject.openstacknetworking.api.InstancePortAdminService;
sangho6a9ff0d2017-03-27 11:23:37 +090052import org.onosproject.openstacknetworking.api.InstancePortEvent;
53import org.onosproject.openstacknetworking.api.InstancePortListener;
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 Li9d35bd62018-10-13 01:43:24 +090061import org.onosproject.openstacknetworking.api.PreCommitPortService;
Jian Li26949762018-03-30 15:46:37 +090062import org.onosproject.openstacknetworking.util.RulePopulatorUtil;
sangho3dd2a8b2017-07-19 15:54:31 +090063import org.onosproject.openstacknode.api.OpenstackNode;
sangho1aaa7882017-05-31 13:22:47 +090064import org.onosproject.openstacknode.api.OpenstackNodeEvent;
65import org.onosproject.openstacknode.api.OpenstackNodeListener;
sangho3dd2a8b2017-07-19 15:54:31 +090066import org.onosproject.openstacknode.api.OpenstackNodeService;
sangho6a9ff0d2017-03-27 11:23:37 +090067import org.openstack4j.model.network.Port;
68import org.openstack4j.model.network.SecurityGroup;
69import org.openstack4j.model.network.SecurityGroupRule;
70import org.openstack4j.openstack.networking.domain.NeutronSecurityGroupRule;
sangho0248ca22017-05-31 13:22:47 +090071import org.osgi.service.component.ComponentContext;
sangho6a9ff0d2017-03-27 11:23:37 +090072import org.slf4j.Logger;
73
sangho6a9ff0d2017-03-27 11:23:37 +090074import java.util.Collections;
sangho0248ca22017-05-31 13:22:47 +090075import java.util.Dictionary;
sangho2e97be02017-07-03 18:18:27 +090076import java.util.Map;
sangho6a9ff0d2017-03-27 11:23:37 +090077import java.util.Objects;
78import java.util.Set;
79import java.util.concurrent.ExecutorService;
80import java.util.stream.Collectors;
81
82import static java.util.concurrent.Executors.newSingleThreadExecutor;
83import static org.onlab.util.Tools.groupedThreads;
sanghodc375372017-06-08 10:41:30 +090084import static org.onosproject.openstacknetworking.api.Constants.ACL_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;
Jian Li9d35bd62018-10-13 01:43:24 +090087import static org.onosproject.openstacknetworking.api.Constants.JUMP_TABLE;
sangho6a9ff0d2017-03-27 11:23:37 +090088import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
89import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ACL_RULE;
sangho1aaa7882017-05-31 13:22:47 +090090import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_DROP_RULE;
91import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_HOOK_RULE;
92import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_RULE;
Jian Li9d35bd62018-10-13 01:43:24 +090093import static org.onosproject.openstacknetworking.api.InstancePort.State.REMOVE_PENDING;
94import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_PRE_REMOVE;
Jian Libcc42282018-09-13 20:59:34 +090095import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtMaskFlag;
96import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtStateFlag;
97import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.niciraConnTrackTreatmentBuilder;
sangho1aaa7882017-05-31 13:22:47 +090098import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
sangho6a9ff0d2017-03-27 11:23:37 +090099import static org.slf4j.LoggerFactory.getLogger;
100
101/**
102 * Populates flow rules to handle OpenStack SecurityGroups.
103 */
104@Component(immediate = true)
105public class OpenstackSecurityGroupHandler {
106
107 private final Logger log = getLogger(getClass());
108
sangho0248ca22017-05-31 13:22:47 +0900109 private static final boolean USE_SECURITY_GROUP = false;
110
111 @Property(name = "useSecurityGroup", boolValue = USE_SECURITY_GROUP,
112 label = "Apply OpenStack security group rule for VM traffic")
113 private boolean useSecurityGroup = USE_SECURITY_GROUP;
114
sangho6a9ff0d2017-03-27 11:23:37 +0900115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected CoreService coreService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d35bd62018-10-13 01:43:24 +0900119 protected InstancePortAdminService instancePortService;
sangho6a9ff0d2017-03-27 11:23:37 +0900120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected MastershipService mastershipService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moonae51e732017-04-25 17:46:21 +0900125 protected OpenstackNetworkService osNetService;
sangho6a9ff0d2017-03-27 11:23:37 +0900126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected OpenstackSecurityGroupService securityGroupService;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sanghodc375372017-06-08 10:41:30 +0900131 protected OpenstackFlowRuleService osFlowRuleService;
sangho6a9ff0d2017-03-27 11:23:37 +0900132
sangho0248ca22017-05-31 13:22:47 +0900133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected ComponentConfigService configService;
135
sangho3dd2a8b2017-07-19 15:54:31 +0900136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 protected OpenstackNodeService osNodeService;
138
sanghoe6457a32017-08-24 14:31:19 +0900139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho1aaa7882017-05-31 13:22:47 +0900140 protected DriverService driverService;
141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
143 protected LeadershipService leadershipService;
144
145 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
146 protected ClusterService clusterService;
147
Jian Li9d35bd62018-10-13 01:43:24 +0900148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
149 protected PreCommitPortService preCommitPortService;
150
Jian Libcc42282018-09-13 20:59:34 +0900151 private final InstancePortListener instancePortListener =
152 new InternalInstancePortListener();
153 private final OpenstackNetworkListener osNetworkListener =
154 new InternalOpenstackNetworkListener();
155 private final OpenstackNetworkListener osPortListener =
156 new InternalOpenstackPortListener();
157 private final OpenstackSecurityGroupListener securityGroupListener =
158 new InternalSecurityGroupListener();
sangho1aaa7882017-05-31 13:22:47 +0900159 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
sangho6a9ff0d2017-03-27 11:23:37 +0900160 private ApplicationId appId;
sangho1aaa7882017-05-31 13:22:47 +0900161 private NodeId localNodeId;
sangho6a9ff0d2017-03-27 11:23:37 +0900162
163 private final ExecutorService eventExecutor = newSingleThreadExecutor(
164 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
165
166 private static final String PROTO_ICMP = "ICMP";
167 private static final String PROTO_TCP = "TCP";
168 private static final String PROTO_UDP = "UDP";
169 private static final String ETHTYPE_IPV4 = "IPV4";
170 private static final String EGRESS = "EGRESS";
171 private static final String INGRESS = "INGRESS";
172 private static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
173
sangho1aaa7882017-05-31 13:22:47 +0900174 // We expose pipeline structure to SONA application considering removing pipeline soon.
sanghoshinbbeb31a2018-09-11 17:01:01 +0800175 private static final int GOTO_CONNTRACK_TABLE = CT_TABLE;
176 private static final int GOTO_JUMP_TABLE = JUMP_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900177
178 private static final int CT_COMMIT = 0;
179 private static final int CT_NO_COMMIT = 1;
180 private static final short CT_NO_RECIRC = -1;
181
182 private static final int ACTION_NONE = 0;
183 private static final int ACTION_DROP = -1;
184
sangho6a9ff0d2017-03-27 11:23:37 +0900185 @Activate
186 protected void activate() {
187 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
sangho1aaa7882017-05-31 13:22:47 +0900188 localNodeId = clusterService.getLocalNode().id();
sangho6a9ff0d2017-03-27 11:23:37 +0900189 instancePortService.addListener(instancePortListener);
190 securityGroupService.addListener(securityGroupListener);
Jian Libcc42282018-09-13 20:59:34 +0900191 osNetService.addListener(osPortListener);
192 osNetService.addListener(osNetworkListener);
sangho0248ca22017-05-31 13:22:47 +0900193 configService.registerProperties(getClass());
sangho1aaa7882017-05-31 13:22:47 +0900194 osNodeService.addListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900195
196 log.info("Started");
197 }
198
199 @Deactivate
200 protected void deactivate() {
201 instancePortService.removeListener(instancePortListener);
202 securityGroupService.removeListener(securityGroupListener);
Jian Libcc42282018-09-13 20:59:34 +0900203 osNetService.removeListener(osNetworkListener);
204 osNetService.removeListener(osPortListener);
sangho0248ca22017-05-31 13:22:47 +0900205 configService.unregisterProperties(getClass(), false);
sangho1aaa7882017-05-31 13:22:47 +0900206 osNodeService.removeListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900207 eventExecutor.shutdown();
208
209 log.info("Stopped");
210 }
211
sangho0248ca22017-05-31 13:22:47 +0900212 @Modified
213 protected void modified(ComponentContext context) {
214 Dictionary<?, ?> properties = context.getProperties();
215 Boolean flag;
216
217 flag = Tools.isPropertyEnabled(properties, "useSecurityGroup");
218 if (flag == null) {
219 log.info("useSecurityGroup is not configured, " +
220 "using current value of {}", useSecurityGroup);
221 } else {
222 useSecurityGroup = flag;
223 log.info("Configured. useSecurityGroup is {}",
224 useSecurityGroup ? "enabled" : "disabled");
225 }
226
sanghoe6457a32017-08-24 14:31:19 +0900227 securityGroupService.setSecurityGroupEnabled(useSecurityGroup);
sangho0248ca22017-05-31 13:22:47 +0900228 resetSecurityGroupRules();
229 }
230
sangho1aaa7882017-05-31 13:22:47 +0900231 private void initializeConnTrackTable(DeviceId deviceId, boolean install) {
232
233 //table=1,ip,ct_state=-trk, actions=ct(table:2)
Jian Libcc42282018-09-13 20:59:34 +0900234 long ctState = computeCtStateFlag(false, false, false);
235 long ctMask = computeCtMaskFlag(true, false, false);
sangho1aaa7882017-05-31 13:22:47 +0900236 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, (short) GOTO_CONNTRACK_TABLE,
237 ACTION_NONE, PRIORITY_CT_HOOK_RULE, install);
238
239 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+est,action=goto_table:3
Jian Libcc42282018-09-13 20:59:34 +0900240 ctState = computeCtStateFlag(true, false, true);
241 ctMask = computeCtMaskFlag(true, false, true);
sangho1aaa7882017-05-31 13:22:47 +0900242 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
243 GOTO_JUMP_TABLE, PRIORITY_CT_RULE, install);
244
245 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+new,action=drop
Jian Libcc42282018-09-13 20:59:34 +0900246 ctState = computeCtStateFlag(true, true, false);
247 ctMask = computeCtMaskFlag(true, true, false);
sangho1aaa7882017-05-31 13:22:47 +0900248 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
249 ACTION_DROP, PRIORITY_CT_DROP_RULE, install);
250 }
251
Jian Libcc42282018-09-13 20:59:34 +0900252 private void setSecurityGroupRules(InstancePort instPort,
253 Port port, boolean install) {
sangho6a9ff0d2017-03-27 11:23:37 +0900254 port.getSecurityGroups().forEach(sgId -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900255 SecurityGroup sg = securityGroupService.securityGroup(sgId);
256 if (sg == null) {
257 log.error("Security Group Not Found : {}", sgId);
258 return;
259 }
Jian Libcc42282018-09-13 20:59:34 +0900260 sg.getRules().forEach(sgRule ->
261 updateSecurityGroupRule(instPort, port, sgRule, install));
Hyunsun Moonae51e732017-04-25 17:46:21 +0900262 final String action = install ? "Installed " : "Removed ";
263 log.debug(action + "security group rule ID : " + sgId);
sangho6a9ff0d2017-03-27 11:23:37 +0900264 });
Jian Li9d35bd62018-10-13 01:43:24 +0900265
266 if (install) {
267 preCommitPortService.subscribePreCommit(instPort.portId(),
268 OPENSTACK_PORT_PRE_REMOVE, this.getClass().getName());
269 log.info("Subscribed the port {} on listening pre-remove event", instPort.portId());
270 } else {
271 preCommitPortService.unsubscribePreCommit(instPort.portId(),
272 OPENSTACK_PORT_PRE_REMOVE, instancePortService, this.getClass().getName());
273 log.info("Unsubscribed the port {} on listening pre-remove event", instPort.portId());
274 }
sangho6a9ff0d2017-03-27 11:23:37 +0900275 }
276
Jian Libcc42282018-09-13 20:59:34 +0900277 private void updateSecurityGroupRule(InstancePort instPort, Port port,
278 SecurityGroupRule sgRule, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900279
Daniel Park3a140592018-06-28 18:33:10 +0900280 if (instPort == null || port == null || sgRule == null) {
281 return;
282 }
283
sangho6a9ff0d2017-03-27 11:23:37 +0900284 if (sgRule.getRemoteGroupId() != null && !sgRule.getRemoteGroupId().isEmpty()) {
285 getRemoteInstPorts(port.getTenantId(), sgRule.getRemoteGroupId())
Jian Libcc42282018-09-13 20:59:34 +0900286 .forEach(rInstPort -> {
287 populateSecurityGroupRule(sgRule, instPort,
288 rInstPort.ipAddress().toIpPrefix(), install);
289 populateSecurityGroupRule(sgRule, rInstPort,
290 instPort.ipAddress().toIpPrefix(), install);
sangho6a9ff0d2017-03-27 11:23:37 +0900291
Jian Libcc42282018-09-13 20:59:34 +0900292 SecurityGroupRule rSgRule =
293 new NeutronSecurityGroupRule
294 .SecurityGroupRuleConcreteBuilder()
295 .from(sgRule)
296 .direction(sgRule.getDirection().toUpperCase()
297 .equals(EGRESS) ? INGRESS : EGRESS)
298 .build();
299 populateSecurityGroupRule(rSgRule, instPort,
300 rInstPort.ipAddress().toIpPrefix(), install);
301 populateSecurityGroupRule(rSgRule, rInstPort,
302 instPort.ipAddress().toIpPrefix(), install);
303 });
sangho6a9ff0d2017-03-27 11:23:37 +0900304 } else {
Jian Libcc42282018-09-13 20:59:34 +0900305 populateSecurityGroupRule(sgRule, instPort,
306 sgRule.getRemoteIpPrefix() == null ? IP_PREFIX_ANY :
sangho6a9ff0d2017-03-27 11:23:37 +0900307 IpPrefix.valueOf(sgRule.getRemoteIpPrefix()), install);
308 }
309 }
310
Jian Libcc42282018-09-13 20:59:34 +0900311 private void populateSecurityGroupRule(SecurityGroupRule sgRule,
312 InstancePort instPort,
sangho6a9ff0d2017-03-27 11:23:37 +0900313 IpPrefix remoteIp, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900314 Set<TrafficSelector> selectors = buildSelectors(sgRule,
315 Ip4Address.valueOf(instPort.ipAddress().toInetAddress()), remoteIp);
316 if (selectors == null || selectors.isEmpty()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900317 return;
318 }
319
sangho2e97be02017-07-03 18:18:27 +0900320 selectors.forEach(selector -> {
321 osFlowRuleService.setRule(appId,
322 instPort.deviceId(),
323 selector,
sangho3dd2a8b2017-07-19 15:54:31 +0900324 DefaultTrafficTreatment.builder().transition(JUMP_TABLE).build(),
sangho2e97be02017-07-03 18:18:27 +0900325 PRIORITY_ACL_RULE,
326 ACL_TABLE,
327 install);
328 });
sangho6a9ff0d2017-03-27 11:23:37 +0900329 }
330
331 /**
sangho1aaa7882017-05-31 13:22:47 +0900332 * Sets connection tracking rule using OVS extension commands.
333 * 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 -0700334 * only here. The following is the usage of the function.
sangho1aaa7882017-05-31 13:22:47 +0900335 *
336 * @param deviceId Device ID
337 * @param ctState ctState: please use RulePopulatorUtil.computeCtStateFlag() to build the value
338 * @param ctMask crMask: please use RulePopulatorUtil.computeCtMaskFlag() to build the value
339 * @param commit CT_COMMIT for commit action, CT_NO_COMMIT otherwise
340 * @param recircTable table number for recirculation after CT actions. CT_NO_RECIRC with no recirculation
341 * @param action Additional actions. ACTION_DROP, ACTION_NONE, GOTO_XXX_TABLE are supported.
342 * @param priority priority value for the rule
343 * @param install true for insertion, false for removal
344 */
345 private void setConnTrackRule(DeviceId deviceId, long ctState, long ctMask,
346 int commit, short recircTable,
347 int action, int priority, boolean install) {
348
Jian Libcc42282018-09-13 20:59:34 +0900349 ExtensionSelector esCtSate = RulePopulatorUtil
350 .buildCtExtensionSelector(driverService, deviceId, ctState, ctMask);
sangho1aaa7882017-05-31 13:22:47 +0900351 TrafficSelector selector = DefaultTrafficSelector.builder()
352 .extension(esCtSate, deviceId)
353 .matchEthType(Ethernet.TYPE_IPV4)
354 .build();
355
356 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
357
358 if (commit == CT_COMMIT || recircTable > 0) {
359 RulePopulatorUtil.NiriraConnTrackTreatmentBuilder natTreatmentBuilder =
Jian Libcc42282018-09-13 20:59:34 +0900360 niciraConnTrackTreatmentBuilder(driverService, deviceId);
sangho1aaa7882017-05-31 13:22:47 +0900361 natTreatmentBuilder.natAction(false);
362 if (commit == CT_COMMIT) {
363 natTreatmentBuilder.commit(true);
364 } else {
365 natTreatmentBuilder.commit(false);
366 }
367 if (recircTable > 0) {
368 natTreatmentBuilder.table(recircTable);
369 }
370 tb.extension(natTreatmentBuilder.build(), deviceId);
371 } else if (action == ACTION_DROP) {
372 tb.drop();
373 }
374
sanghoshinbbeb31a2018-09-11 17:01:01 +0800375 if (action != ACTION_NONE && action != ACTION_DROP) {
sangho1aaa7882017-05-31 13:22:47 +0900376 tb.transition(action);
377 }
378
379 int tableType = ERROR_TABLE;
380 if (priority == PRIORITY_CT_RULE || priority == PRIORITY_CT_DROP_RULE) {
381 tableType = CT_TABLE;
382 } else if (priority == PRIORITY_CT_HOOK_RULE) {
383 tableType = ACL_TABLE;
384 } else {
385 log.error("Cannot an appropriate table for the conn track rule.");
386 }
387
388 osFlowRuleService.setRule(
389 appId,
390 deviceId,
391 selector,
392 tb.build(),
393 priority,
394 tableType,
395 install);
396 }
397
398 /**
sangho6a9ff0d2017-03-27 11:23:37 +0900399 * Returns a set of host IP addresses engaged with supplied security group ID.
400 * It only searches a VM in the same tenant boundary.
401 *
402 * @param tenantId tenant id
403 * @param sgId security group id
404 * @return set of ip addresses
405 */
406 private Set<InstancePort> getRemoteInstPorts(String tenantId, String sgId) {
407 Set<InstancePort> remoteInstPorts;
408
Hyunsun Moonae51e732017-04-25 17:46:21 +0900409 remoteInstPorts = osNetService.ports().stream()
sangho6a9ff0d2017-03-27 11:23:37 +0900410 .filter(port -> port.getTenantId().equals(tenantId))
411 .filter(port -> port.getSecurityGroups().contains(sgId))
412 .map(port -> instancePortService.instancePort(port.getId()))
413 .filter(instPort -> instPort != null && instPort.ipAddress() != null)
414 .collect(Collectors.toSet());
415
416 return Collections.unmodifiableSet(remoteInstPorts);
417 }
418
sangho2e97be02017-07-03 18:18:27 +0900419 private Set<TrafficSelector> buildSelectors(SecurityGroupRule sgRule,
420 Ip4Address vmIp,
421 IpPrefix remoteIp) {
422 if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, 32))) {
423 // do nothing if the remote IP is my IP
424 return null;
425 }
426
427 Set<TrafficSelector> selectorSet = Sets.newHashSet();
428
429 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
Jian Libcc42282018-09-13 20:59:34 +0900430 buildMatches(sBuilder, sgRule, vmIp, remoteIp);
sangho2e97be02017-07-03 18:18:27 +0900431
432 if (sgRule.getPortRangeMax() != null && sgRule.getPortRangeMin() != null &&
433 sgRule.getPortRangeMin() < sgRule.getPortRangeMax()) {
Jian Libcc42282018-09-13 20:59:34 +0900434 Map<TpPort, TpPort> portRangeMatchMap =
435 buildPortRangeMatches(sgRule.getPortRangeMin(),
sangho2e97be02017-07-03 18:18:27 +0900436 sgRule.getPortRangeMax());
437 portRangeMatchMap.entrySet().forEach(entry -> {
438
Jian Libcc42282018-09-13 20:59:34 +0900439 if (sgRule.getProtocol().toUpperCase().equals(PROTO_TCP)) {
440 if (sgRule.getDirection().toUpperCase().equals(EGRESS)) {
441 sBuilder.matchTcpSrcMasked(entry.getKey(), entry.getValue());
442 } else {
443 sBuilder.matchTcpDstMasked(entry.getKey(), entry.getValue());
444 }
445 } else if (sgRule.getProtocol().toUpperCase().equals(PROTO_UDP)) {
446 if (sgRule.getDirection().toUpperCase().equals(EGRESS)) {
447 sBuilder.matchUdpSrcMasked(entry.getKey(), entry.getValue());
448 } else {
449 sBuilder.matchUdpDstMasked(entry.getKey(), entry.getValue());
450 }
451 }
sangho2e97be02017-07-03 18:18:27 +0900452
Jian Libcc42282018-09-13 20:59:34 +0900453 selectorSet.add(sBuilder.build());
454 }
sangho2e97be02017-07-03 18:18:27 +0900455 );
456 } else {
457 selectorSet.add(sBuilder.build());
458 }
459
460 return selectorSet;
461 }
462
Jian Libcc42282018-09-13 20:59:34 +0900463 private void buildMatches(TrafficSelector.Builder sBuilder,
464 SecurityGroupRule sgRule,
465 Ip4Address vmIp, IpPrefix remoteIp) {
sangho6a9ff0d2017-03-27 11:23:37 +0900466 buildMatchEthType(sBuilder, sgRule.getEtherType());
467 buildMatchDirection(sBuilder, sgRule.getDirection(), vmIp);
468 buildMatchProto(sBuilder, sgRule.getProtocol());
469 buildMatchPort(sBuilder, sgRule.getProtocol(), sgRule.getDirection(),
sangho2e97be02017-07-03 18:18:27 +0900470 sgRule.getPortRangeMin() == null ? 0 : sgRule.getPortRangeMin(),
471 sgRule.getPortRangeMax() == null ? 0 : sgRule.getPortRangeMax());
sangho6a9ff0d2017-03-27 11:23:37 +0900472 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
473 if (sgRule.getRemoteGroupId() != null && sgRule.getRemoteGroupId().isEmpty()) {
474 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
475 }
476 }
477
478 private void buildMatchDirection(TrafficSelector.Builder sBuilder,
479 String direction,
480 Ip4Address vmIp) {
481 if (direction.toUpperCase().equals(EGRESS)) {
482 sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, 32));
483 } else {
484 sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, 32));
485 }
486 }
487
488 private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
489 // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
490 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
491 if (etherType != null && !Objects.equals(etherType, "null") &&
492 !etherType.toUpperCase().equals(ETHTYPE_IPV4)) {
493 log.debug("EthType {} is not supported yet in Security Group", etherType);
494 }
495 }
496
Jian Libcc42282018-09-13 20:59:34 +0900497 private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder,
498 IpPrefix remoteIpPrefix, String direction) {
499 if (remoteIpPrefix != null &&
500 !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
sangho6a9ff0d2017-03-27 11:23:37 +0900501 if (direction.toUpperCase().equals(EGRESS)) {
502 sBuilder.matchIPDst(remoteIpPrefix);
503 } else {
504 sBuilder.matchIPSrc(remoteIpPrefix);
505 }
506 }
507 }
508
509 private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
510 if (protocol != null) {
511 switch (protocol.toUpperCase()) {
512 case PROTO_ICMP:
513 sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
514 break;
515 case PROTO_TCP:
516 sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
517 break;
518 case PROTO_UDP:
519 sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
520 break;
521 default:
522 }
523 }
524 }
525
Jian Libcc42282018-09-13 20:59:34 +0900526 private void buildMatchPort(TrafficSelector.Builder sBuilder,
527 String protocol, String direction,
sangho6a9ff0d2017-03-27 11:23:37 +0900528 int portMin, int portMax) {
529 if (portMin > 0 && portMax > 0 && portMin == portMax) {
530 if (protocol.toUpperCase().equals(PROTO_TCP)) {
531 if (direction.toUpperCase().equals(EGRESS)) {
532 sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
533 } else {
534 sBuilder.matchTcpDst(TpPort.tpPort(portMax));
535 }
536 } else if (protocol.toUpperCase().equals(PROTO_UDP)) {
537 if (direction.toUpperCase().equals(EGRESS)) {
538 sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
539 } else {
540 sBuilder.matchUdpDst(TpPort.tpPort(portMax));
541 }
542 }
543 }
544 }
545
sangho0248ca22017-05-31 13:22:47 +0900546 private void resetSecurityGroupRules() {
547
548 if (useSecurityGroup) {
sangho3dd2a8b2017-07-19 15:54:31 +0900549 osNodeService.completeNodes(OpenstackNode.NodeType.COMPUTE)
Jian Libcc42282018-09-13 20:59:34 +0900550 .forEach(node -> osFlowRuleService
551 .setUpTableMissEntry(node.intgBridge(), ACL_TABLE));
sangho0248ca22017-05-31 13:22:47 +0900552 securityGroupService.securityGroups().forEach(securityGroup ->
553 securityGroup.getRules().forEach(this::securityGroupRuleAdded));
sangho1aaa7882017-05-31 13:22:47 +0900554 osNodeService.nodes().stream()
555 .filter(node -> node.type().equals(OpenstackNode.NodeType.COMPUTE))
556 .forEach(node -> initializeConnTrackTable(node .intgBridge(), true));
sangho0248ca22017-05-31 13:22:47 +0900557 } else {
sangho3dd2a8b2017-07-19 15:54:31 +0900558 osNodeService.completeNodes(OpenstackNode.NodeType.COMPUTE)
Jian Libcc42282018-09-13 20:59:34 +0900559 .forEach(node -> osFlowRuleService
560 .connectTables(node.intgBridge(), ACL_TABLE, JUMP_TABLE));
sangho0248ca22017-05-31 13:22:47 +0900561 securityGroupService.securityGroups().forEach(securityGroup ->
562 securityGroup.getRules().forEach(this::securityGroupRuleRemoved));
sangho1aaa7882017-05-31 13:22:47 +0900563 osNodeService.nodes().stream()
564 .filter(node -> node.type().equals(OpenstackNode.NodeType.COMPUTE))
565 .forEach(node -> initializeConnTrackTable(node.intgBridge(), false));
sangho0248ca22017-05-31 13:22:47 +0900566 }
567
Jian Libcc42282018-09-13 20:59:34 +0900568 log.info("Reset security group info " +
569 (useSecurityGroup ? " with " : " without") + " Security Group");
sangho0248ca22017-05-31 13:22:47 +0900570 }
571
572 private void securityGroupRuleAdded(SecurityGroupRule sgRule) {
573 osNetService.ports().stream()
Jian Libcc42282018-09-13 20:59:34 +0900574 .filter(port -> port.getSecurityGroups()
575 .contains(sgRule.getSecurityGroupId()))
sangho0248ca22017-05-31 13:22:47 +0900576 .forEach(port -> {
577 updateSecurityGroupRule(
578 instancePortService.instancePort(port.getId()),
579 port, sgRule, true);
580 log.debug("Applied security group rule {} to port {}",
581 sgRule.getId(), port.getId());
582 });
583 }
584
585 private void securityGroupRuleRemoved(SecurityGroupRule sgRule) {
586 osNetService.ports().stream()
Jian Libcc42282018-09-13 20:59:34 +0900587 .filter(port -> port.getSecurityGroups()
588 .contains(sgRule.getSecurityGroupId()))
sangho0248ca22017-05-31 13:22:47 +0900589 .forEach(port -> {
590 updateSecurityGroupRule(
591 instancePortService.instancePort(port.getId()),
592 port, sgRule, false);
593 log.debug("Removed security group rule {} from port {}",
594 sgRule.getId(), port.getId());
595 });
596 }
597
sangho2e97be02017-07-03 18:18:27 +0900598 private int binLower(String binStr, int bits) {
599 String outBin = binStr.substring(0, 16 - bits);
600 for (int i = 0; i < bits; i++) {
601 outBin += "0";
602 }
603
604 return Integer.parseInt(outBin, 2);
605 }
606
607 private int binHigher(String binStr, int bits) {
608 String outBin = binStr.substring(0, 16 - bits);
609 for (int i = 0; i < bits; i++) {
610 outBin += "1";
611 }
612
613 return Integer.parseInt(outBin, 2);
614 }
615
616 private int testMasks(String binStr, int start, int end) {
617 int mask = 0;
618 for (; mask <= 16; mask++) {
619 int maskStart = binLower(binStr, mask);
620 int maskEnd = binHigher(binStr, mask);
621 if (maskStart < start || maskEnd > end) {
622 return mask - 1;
623 }
624 }
625
626 return mask;
627 }
628
629 private String getMask(int bits) {
630 switch (bits) {
631 case 0: return "ffff";
632 case 1: return "fffe";
633 case 2: return "fffc";
634 case 3: return "fff8";
635 case 4: return "fff0";
636 case 5: return "ffe0";
637 case 6: return "ffc0";
638 case 7: return "ff80";
639 case 8: return "ff00";
640 case 9: return "fe00";
641 case 10: return "fc00";
642 case 11: return "f800";
643 case 12: return "f000";
644 case 13: return "e000";
645 case 14: return "c000";
646 case 15: return "8000";
647 case 16: return "0000";
648 default: return null;
649 }
650 }
651
652 private Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
653
654 boolean processing = true;
655 int start = portMin;
656 Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
657 while (processing) {
658 String minStr = Integer.toBinaryString(start);
659 String binStrMinPadded = "0000000000000000".substring(minStr.length()) + minStr;
660
661 int mask = testMasks(binStrMinPadded, start, portMax);
662 int maskStart = binLower(binStrMinPadded, mask);
663 int maskEnd = binHigher(binStrMinPadded, mask);
664
665 log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
Jian Libcc42282018-09-13 20:59:34 +0900666 portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(
667 Integer.parseInt(Objects.requireNonNull(getMask(mask)), 16)));
sangho2e97be02017-07-03 18:18:27 +0900668
669 start = maskEnd + 1;
670 if (start > portMax) {
671 processing = false;
672 }
673 }
674
675 return portMaskMap;
676 }
677
sangho6a9ff0d2017-03-27 11:23:37 +0900678 private class InternalInstancePortListener implements InstancePortListener {
679
680 @Override
681 public boolean isRelevant(InstancePortEvent event) {
682 InstancePort instPort = event.subject();
sangho0248ca22017-05-31 13:22:47 +0900683 if (!useSecurityGroup) {
684 return false;
685 }
sangho6a9ff0d2017-03-27 11:23:37 +0900686 return mastershipService.isLocalMaster(instPort.deviceId());
687 }
688
689 @Override
690 public void event(InstancePortEvent event) {
691 InstancePort instPort = event.subject();
692 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900693 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900694 case OPENSTACK_INSTANCE_PORT_UPDATED:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900695 log.debug("Instance port detected MAC:{} IP:{}",
696 instPort.macAddress(),
697 instPort.ipAddress());
sangho6a9ff0d2017-03-27 11:23:37 +0900698 eventExecutor.execute(() -> {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900699 setSecurityGroupRules(instPort,
700 osNetService.port(event.subject().portId()),
701 true);
sangho6a9ff0d2017-03-27 11:23:37 +0900702 });
703 break;
sangho6a9ff0d2017-03-27 11:23:37 +0900704 default:
705 break;
706 }
707 }
sangho6a9ff0d2017-03-27 11:23:37 +0900708 }
709
710 private class InternalOpenstackPortListener implements OpenstackNetworkListener {
711
712 @Override
713 public boolean isRelevant(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +0800714 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
sangho6a9ff0d2017-03-27 11:23:37 +0900715 return false;
716 }
Jian Libcc42282018-09-13 20:59:34 +0900717
718 InstancePort instPort = instancePortService.instancePort(event.port().getId());
719
720 if (instPort == null) {
721 return false;
722 }
723
724 return useSecurityGroup && mastershipService.isLocalMaster(instPort.deviceId());
725 }
726
727 @Override
728 public void event(OpenstackNetworkEvent event) {
729 log.debug("openstack port event received {}", event);
730 Port osPort = event.port();
731 InstancePort instPort = instancePortService.instancePort(osPort.getId());
732
733 switch (event.type()) {
734 case OPENSTACK_PORT_PRE_REMOVE:
Jian Li9d35bd62018-10-13 01:43:24 +0900735 instancePortService.updateInstancePort(
736 instPort.updateState(REMOVE_PENDING));
737 eventExecutor.execute(() ->
738 setSecurityGroupRules(instPort, osPort, false)
739 );
Jian Libcc42282018-09-13 20:59:34 +0900740 break;
741 default:
742 // do nothing for the other events
743 break;
744 }
745 }
746 }
747
748 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
749
750 @Override
751 public boolean isRelevant(OpenstackNetworkEvent event) {
752 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
753 return false;
754 }
Hyunsun Moonae51e732017-04-25 17:46:21 +0900755 if (event.securityGroupId() == null ||
756 securityGroupService.securityGroup(event.securityGroupId()) == null) {
757 return false;
758 }
759 if (instancePortService.instancePort(event.port().getId()) == null) {
760 return false;
761 }
Jian Libcc42282018-09-13 20:59:34 +0900762 return useSecurityGroup;
sangho6a9ff0d2017-03-27 11:23:37 +0900763 }
764
765 @Override
766 public void event(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +0800767 log.debug("security group event received {}", event);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900768 Port osPort = event.port();
769 InstancePort instPort = instancePortService.instancePort(osPort.getId());
770 SecurityGroup osSg = securityGroupService.securityGroup(event.securityGroupId());
771
sangho6a9ff0d2017-03-27 11:23:37 +0900772 switch (event.type()) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900773 case OPENSTACK_PORT_SECURITY_GROUP_ADDED:
774 eventExecutor.execute(() -> {
775 osSg.getRules().forEach(sgRule -> {
776 updateSecurityGroupRule(instPort, osPort, sgRule, true);
777 });
778 log.info("Added security group {} to port {}",
779 event.securityGroupId(), event.port().getId());
780 });
sangho6a9ff0d2017-03-27 11:23:37 +0900781 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +0900782 case OPENSTACK_PORT_SECURITY_GROUP_REMOVED:
783 eventExecutor.execute(() -> {
784 osSg.getRules().forEach(sgRule -> {
785 updateSecurityGroupRule(instPort, osPort, sgRule, false);
786 });
787 log.info("Removed security group {} from port {}",
788 event.securityGroupId(), event.port().getId());
789 });
sangho6a9ff0d2017-03-27 11:23:37 +0900790 break;
791 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900792 // do nothing for the other events
sangho6a9ff0d2017-03-27 11:23:37 +0900793 break;
794 }
795 }
sangho6a9ff0d2017-03-27 11:23:37 +0900796 }
797
798 private class InternalSecurityGroupListener implements OpenstackSecurityGroupListener {
799
800 @Override
sangho0248ca22017-05-31 13:22:47 +0900801 public boolean isRelevant(OpenstackSecurityGroupEvent event) {
Jian Libcc42282018-09-13 20:59:34 +0900802 return useSecurityGroup;
sangho0248ca22017-05-31 13:22:47 +0900803 }
804
805 @Override
sangho6a9ff0d2017-03-27 11:23:37 +0900806 public void event(OpenstackSecurityGroupEvent event) {
807 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900808 case OPENSTACK_SECURITY_GROUP_RULE_CREATED:
809 SecurityGroupRule securityGroupRuleToAdd = event.securityGroupRule();
810 eventExecutor.execute(() -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900811 securityGroupRuleAdded(securityGroupRuleToAdd);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900812 log.info("Applied new security group rule {} to ports",
813 securityGroupRuleToAdd.getId());
sangho6a9ff0d2017-03-27 11:23:37 +0900814 });
815 break;
816
817 case OPENSTACK_SECURITY_GROUP_RULE_REMOVED:
818 SecurityGroupRule securityGroupRuleToRemove = event.securityGroupRule();
819 eventExecutor.execute(() -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900820 securityGroupRuleRemoved(securityGroupRuleToRemove);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900821 log.info("Removed security group rule {} from ports",
822 securityGroupRuleToRemove.getId());
sangho6a9ff0d2017-03-27 11:23:37 +0900823 });
824 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +0900825 case OPENSTACK_SECURITY_GROUP_REMOVED:
Jian Li9d35bd62018-10-13 01:43:24 +0900826 case OPENSTACK_SECURITY_GROUP_CREATED:
sangho6a9ff0d2017-03-27 11:23:37 +0900827 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900828 // do nothing
829 break;
sangho6a9ff0d2017-03-27 11:23:37 +0900830 }
831 }
sangho6a9ff0d2017-03-27 11:23:37 +0900832 }
sangho1aaa7882017-05-31 13:22:47 +0900833
834 private class InternalNodeListener implements OpenstackNodeListener {
835
836 @Override
837 public boolean isRelevant(OpenstackNodeEvent event) {
838 // do not allow to proceed without leadership
839 NodeId leader = leadershipService.getLeader(appId.name());
840 if (!Objects.equals(localNodeId, leader)) {
841 return false;
842 }
843 return event.subject().type() == COMPUTE;
844 }
845
846 @Override
847 public void event(OpenstackNodeEvent event) {
848 OpenstackNode osNode = event.subject();
849
850 switch (event.type()) {
851 case OPENSTACK_NODE_COMPLETE:
852 eventExecutor.execute(() -> {
853 try {
854 if (useSecurityGroup) {
855 initializeConnTrackTable(osNode.intgBridge(), true);
Jian Li0488c732018-09-14 20:53:07 +0900856 log.info("SG table initialization : {} is done",
Jian Libcc42282018-09-13 20:59:34 +0900857 osNode.intgBridge());
sangho1aaa7882017-05-31 13:22:47 +0900858 }
859 } catch (IllegalArgumentException e) {
Jian Libcc42282018-09-13 20:59:34 +0900860 log.error("ACL table initialization error : {}",
861 e.getMessage());
sangho1aaa7882017-05-31 13:22:47 +0900862 }
863 });
864 break;
865 case OPENSTACK_NODE_CREATED:
866 case OPENSTACK_NODE_REMOVED:
867 case OPENSTACK_NODE_UPDATED:
868 case OPENSTACK_NODE_INCOMPLETE:
869 default:
870 break;
871 }
872 }
873 }
Jian Libcc42282018-09-13 20:59:34 +0900874}