blob: d0241d668dd79a3275bc26d6347e327f5445e4fe [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;
92import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
sangho6a9ff0d2017-03-27 11:23:37 +090093import static org.slf4j.LoggerFactory.getLogger;
94
95/**
96 * Populates flow rules to handle OpenStack SecurityGroups.
97 */
98@Component(immediate = true)
99public class OpenstackSecurityGroupHandler {
100
101 private final Logger log = getLogger(getClass());
102
sangho0248ca22017-05-31 13:22:47 +0900103 private static final boolean USE_SECURITY_GROUP = false;
104
105 @Property(name = "useSecurityGroup", boolValue = USE_SECURITY_GROUP,
106 label = "Apply OpenStack security group rule for VM traffic")
107 private boolean useSecurityGroup = USE_SECURITY_GROUP;
108
sangho6a9ff0d2017-03-27 11:23:37 +0900109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected CoreService coreService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected InstancePortService instancePortService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected MastershipService mastershipService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moonae51e732017-04-25 17:46:21 +0900119 protected OpenstackNetworkService osNetService;
sangho6a9ff0d2017-03-27 11:23:37 +0900120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected OpenstackSecurityGroupService securityGroupService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sanghodc375372017-06-08 10:41:30 +0900125 protected OpenstackFlowRuleService osFlowRuleService;
sangho6a9ff0d2017-03-27 11:23:37 +0900126
sangho0248ca22017-05-31 13:22:47 +0900127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected ComponentConfigService configService;
129
sangho3dd2a8b2017-07-19 15:54:31 +0900130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected OpenstackNodeService osNodeService;
132
sanghoe6457a32017-08-24 14:31:19 +0900133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho1aaa7882017-05-31 13:22:47 +0900134 protected DriverService driverService;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 protected LeadershipService leadershipService;
138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected ClusterService clusterService;
141
sangho6a9ff0d2017-03-27 11:23:37 +0900142 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
143 private final OpenstackNetworkListener portListener = new InternalOpenstackPortListener();
144 private final OpenstackSecurityGroupListener securityGroupListener = new InternalSecurityGroupListener();
sangho1aaa7882017-05-31 13:22:47 +0900145 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
sangho6a9ff0d2017-03-27 11:23:37 +0900146 private ApplicationId appId;
sangho1aaa7882017-05-31 13:22:47 +0900147 private NodeId localNodeId;
sangho6a9ff0d2017-03-27 11:23:37 +0900148
149 private final ExecutorService eventExecutor = newSingleThreadExecutor(
150 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
151
152 private static final String PROTO_ICMP = "ICMP";
153 private static final String PROTO_TCP = "TCP";
154 private static final String PROTO_UDP = "UDP";
155 private static final String ETHTYPE_IPV4 = "IPV4";
156 private static final String EGRESS = "EGRESS";
157 private static final String INGRESS = "INGRESS";
158 private static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
159
sangho1aaa7882017-05-31 13:22:47 +0900160 // We expose pipeline structure to SONA application considering removing pipeline soon.
sanghoshinbbeb31a2018-09-11 17:01:01 +0800161 private static final int GOTO_CONNTRACK_TABLE = CT_TABLE;
162 private static final int GOTO_JUMP_TABLE = JUMP_TABLE;
sangho1aaa7882017-05-31 13:22:47 +0900163
164 private static final int CT_COMMIT = 0;
165 private static final int CT_NO_COMMIT = 1;
166 private static final short CT_NO_RECIRC = -1;
167
168 private static final int ACTION_NONE = 0;
169 private static final int ACTION_DROP = -1;
170
sangho6a9ff0d2017-03-27 11:23:37 +0900171 @Activate
172 protected void activate() {
173 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
sangho1aaa7882017-05-31 13:22:47 +0900174 localNodeId = clusterService.getLocalNode().id();
sangho6a9ff0d2017-03-27 11:23:37 +0900175 instancePortService.addListener(instancePortListener);
176 securityGroupService.addListener(securityGroupListener);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900177 osNetService.addListener(portListener);
sangho0248ca22017-05-31 13:22:47 +0900178 configService.registerProperties(getClass());
sangho1aaa7882017-05-31 13:22:47 +0900179 osNodeService.addListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900180
181 log.info("Started");
182 }
183
184 @Deactivate
185 protected void deactivate() {
186 instancePortService.removeListener(instancePortListener);
187 securityGroupService.removeListener(securityGroupListener);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900188 osNetService.removeListener(portListener);
sangho0248ca22017-05-31 13:22:47 +0900189 configService.unregisterProperties(getClass(), false);
sangho1aaa7882017-05-31 13:22:47 +0900190 osNodeService.removeListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900191 eventExecutor.shutdown();
192
193 log.info("Stopped");
194 }
195
sangho0248ca22017-05-31 13:22:47 +0900196 @Modified
197 protected void modified(ComponentContext context) {
198 Dictionary<?, ?> properties = context.getProperties();
199 Boolean flag;
200
201 flag = Tools.isPropertyEnabled(properties, "useSecurityGroup");
202 if (flag == null) {
203 log.info("useSecurityGroup is not configured, " +
204 "using current value of {}", useSecurityGroup);
205 } else {
206 useSecurityGroup = flag;
207 log.info("Configured. useSecurityGroup is {}",
208 useSecurityGroup ? "enabled" : "disabled");
209 }
210
sanghoe6457a32017-08-24 14:31:19 +0900211 securityGroupService.setSecurityGroupEnabled(useSecurityGroup);
sangho0248ca22017-05-31 13:22:47 +0900212 resetSecurityGroupRules();
213 }
214
sangho1aaa7882017-05-31 13:22:47 +0900215 private void initializeConnTrackTable(DeviceId deviceId, boolean install) {
216
217 //table=1,ip,ct_state=-trk, actions=ct(table:2)
218 long ctState = RulePopulatorUtil.computeCtStateFlag(false, false, false);
219 long ctMask = RulePopulatorUtil.computeCtMaskFlag(true, false, false);
220 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, (short) GOTO_CONNTRACK_TABLE,
221 ACTION_NONE, PRIORITY_CT_HOOK_RULE, install);
222
223 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+est,action=goto_table:3
224 ctState = RulePopulatorUtil.computeCtStateFlag(true, false, true);
225 ctMask = RulePopulatorUtil.computeCtMaskFlag(true, false, true);
226 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
227 GOTO_JUMP_TABLE, PRIORITY_CT_RULE, install);
228
229 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+new,action=drop
230 ctState = RulePopulatorUtil.computeCtStateFlag(true, true, false);
231 ctMask = RulePopulatorUtil.computeCtMaskFlag(true, true, false);
232 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
233 ACTION_DROP, PRIORITY_CT_DROP_RULE, install);
234 }
235
sangho6a9ff0d2017-03-27 11:23:37 +0900236 private void setSecurityGroupRules(InstancePort instPort, Port port, boolean install) {
237 port.getSecurityGroups().forEach(sgId -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900238 SecurityGroup sg = securityGroupService.securityGroup(sgId);
239 if (sg == null) {
240 log.error("Security Group Not Found : {}", sgId);
241 return;
242 }
243 sg.getRules().forEach(sgRule -> updateSecurityGroupRule(instPort, port, sgRule, install));
Hyunsun Moonae51e732017-04-25 17:46:21 +0900244 final String action = install ? "Installed " : "Removed ";
245 log.debug(action + "security group rule ID : " + sgId);
sangho6a9ff0d2017-03-27 11:23:37 +0900246 });
247 }
248
249 private void updateSecurityGroupRule(InstancePort instPort, Port port, SecurityGroupRule sgRule, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900250
Daniel Park3a140592018-06-28 18:33:10 +0900251 if (instPort == null || port == null || sgRule == null) {
252 return;
253 }
254
sangho6a9ff0d2017-03-27 11:23:37 +0900255 if (sgRule.getRemoteGroupId() != null && !sgRule.getRemoteGroupId().isEmpty()) {
256 getRemoteInstPorts(port.getTenantId(), sgRule.getRemoteGroupId())
257 .forEach(rInstPort -> {
258 populateSecurityGroupRule(sgRule, instPort, rInstPort.ipAddress().toIpPrefix(), install);
259 populateSecurityGroupRule(sgRule, rInstPort, instPort.ipAddress().toIpPrefix(), install);
260
261 SecurityGroupRule rSgRule = new NeutronSecurityGroupRule.SecurityGroupRuleConcreteBuilder()
262 .from(sgRule)
263 .direction(sgRule.getDirection().toUpperCase().equals(EGRESS) ? INGRESS : EGRESS).build();
264 populateSecurityGroupRule(rSgRule, instPort, rInstPort.ipAddress().toIpPrefix(), install);
265 populateSecurityGroupRule(rSgRule, rInstPort, instPort.ipAddress().toIpPrefix(), install);
266 });
267 } else {
268 populateSecurityGroupRule(sgRule, instPort, sgRule.getRemoteIpPrefix() == null ? IP_PREFIX_ANY :
269 IpPrefix.valueOf(sgRule.getRemoteIpPrefix()), install);
270 }
271 }
272
273 private void populateSecurityGroupRule(SecurityGroupRule sgRule, InstancePort instPort,
274 IpPrefix remoteIp, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900275 Set<TrafficSelector> selectors = buildSelectors(sgRule,
276 Ip4Address.valueOf(instPort.ipAddress().toInetAddress()), remoteIp);
277 if (selectors == null || selectors.isEmpty()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900278 return;
279 }
280
sangho2e97be02017-07-03 18:18:27 +0900281 selectors.forEach(selector -> {
282 osFlowRuleService.setRule(appId,
283 instPort.deviceId(),
284 selector,
sangho3dd2a8b2017-07-19 15:54:31 +0900285 DefaultTrafficTreatment.builder().transition(JUMP_TABLE).build(),
sangho2e97be02017-07-03 18:18:27 +0900286 PRIORITY_ACL_RULE,
287 ACL_TABLE,
288 install);
289 });
sangho6a9ff0d2017-03-27 11:23:37 +0900290 }
291
292 /**
sangho1aaa7882017-05-31 13:22:47 +0900293 * Sets connection tracking rule using OVS extension commands.
294 * 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 -0700295 * only here. The following is the usage of the function.
sangho1aaa7882017-05-31 13:22:47 +0900296 *
297 * @param deviceId Device ID
298 * @param ctState ctState: please use RulePopulatorUtil.computeCtStateFlag() to build the value
299 * @param ctMask crMask: please use RulePopulatorUtil.computeCtMaskFlag() to build the value
300 * @param commit CT_COMMIT for commit action, CT_NO_COMMIT otherwise
301 * @param recircTable table number for recirculation after CT actions. CT_NO_RECIRC with no recirculation
302 * @param action Additional actions. ACTION_DROP, ACTION_NONE, GOTO_XXX_TABLE are supported.
303 * @param priority priority value for the rule
304 * @param install true for insertion, false for removal
305 */
306 private void setConnTrackRule(DeviceId deviceId, long ctState, long ctMask,
307 int commit, short recircTable,
308 int action, int priority, boolean install) {
309
310 ExtensionSelector esCtSate = RulePopulatorUtil.buildCtExtensionSelector(driverService, deviceId,
311 ctState, ctMask);
312 TrafficSelector selector = DefaultTrafficSelector.builder()
313 .extension(esCtSate, deviceId)
314 .matchEthType(Ethernet.TYPE_IPV4)
315 .build();
316
317 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
318
319 if (commit == CT_COMMIT || recircTable > 0) {
320 RulePopulatorUtil.NiriraConnTrackTreatmentBuilder natTreatmentBuilder =
321 RulePopulatorUtil.niciraConnTrackTreatmentBuilder(driverService, deviceId);
322 natTreatmentBuilder.natAction(false);
323 if (commit == CT_COMMIT) {
324 natTreatmentBuilder.commit(true);
325 } else {
326 natTreatmentBuilder.commit(false);
327 }
328 if (recircTable > 0) {
329 natTreatmentBuilder.table(recircTable);
330 }
331 tb.extension(natTreatmentBuilder.build(), deviceId);
332 } else if (action == ACTION_DROP) {
333 tb.drop();
334 }
335
sanghoshinbbeb31a2018-09-11 17:01:01 +0800336 if (action != ACTION_NONE && action != ACTION_DROP) {
sangho1aaa7882017-05-31 13:22:47 +0900337 tb.transition(action);
338 }
339
340 int tableType = ERROR_TABLE;
341 if (priority == PRIORITY_CT_RULE || priority == PRIORITY_CT_DROP_RULE) {
342 tableType = CT_TABLE;
343 } else if (priority == PRIORITY_CT_HOOK_RULE) {
344 tableType = ACL_TABLE;
345 } else {
346 log.error("Cannot an appropriate table for the conn track rule.");
347 }
348
349 osFlowRuleService.setRule(
350 appId,
351 deviceId,
352 selector,
353 tb.build(),
354 priority,
355 tableType,
356 install);
357 }
358
359 /**
sangho6a9ff0d2017-03-27 11:23:37 +0900360 * Returns a set of host IP addresses engaged with supplied security group ID.
361 * It only searches a VM in the same tenant boundary.
362 *
363 * @param tenantId tenant id
364 * @param sgId security group id
365 * @return set of ip addresses
366 */
367 private Set<InstancePort> getRemoteInstPorts(String tenantId, String sgId) {
368 Set<InstancePort> remoteInstPorts;
369
Hyunsun Moonae51e732017-04-25 17:46:21 +0900370 remoteInstPorts = osNetService.ports().stream()
sangho6a9ff0d2017-03-27 11:23:37 +0900371 .filter(port -> port.getTenantId().equals(tenantId))
372 .filter(port -> port.getSecurityGroups().contains(sgId))
373 .map(port -> instancePortService.instancePort(port.getId()))
374 .filter(instPort -> instPort != null && instPort.ipAddress() != null)
375 .collect(Collectors.toSet());
376
377 return Collections.unmodifiableSet(remoteInstPorts);
378 }
379
sangho2e97be02017-07-03 18:18:27 +0900380 private Set<TrafficSelector> buildSelectors(SecurityGroupRule sgRule,
381 Ip4Address vmIp,
382 IpPrefix remoteIp) {
383 if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, 32))) {
384 // do nothing if the remote IP is my IP
385 return null;
386 }
387
388 Set<TrafficSelector> selectorSet = Sets.newHashSet();
389
390 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
391 buildMatchs(sBuilder, sgRule, vmIp, remoteIp);
392
393 if (sgRule.getPortRangeMax() != null && sgRule.getPortRangeMin() != null &&
394 sgRule.getPortRangeMin() < sgRule.getPortRangeMax()) {
395 Map<TpPort, TpPort> portRangeMatchMap = buildPortRangeMatches(sgRule.getPortRangeMin(),
396 sgRule.getPortRangeMax());
397 portRangeMatchMap.entrySet().forEach(entry -> {
398
399 if (sgRule.getProtocol().toUpperCase().equals(PROTO_TCP)) {
400 if (sgRule.getDirection().toUpperCase().equals(EGRESS)) {
401 sBuilder.matchTcpSrcMasked(entry.getKey(), entry.getValue());
402 } else {
403 sBuilder.matchTcpDstMasked(entry.getKey(), entry.getValue());
404 }
405 } else if (sgRule.getProtocol().toUpperCase().equals(PROTO_UDP)) {
406 if (sgRule.getDirection().toUpperCase().equals(EGRESS)) {
407 sBuilder.matchUdpSrcMasked(entry.getKey(), entry.getValue());
408 } else {
409 sBuilder.matchUdpDstMasked(entry.getKey(), entry.getValue());
410 }
411 }
412
413 selectorSet.add(sBuilder.build());
414 }
415 );
416 } else {
417 selectorSet.add(sBuilder.build());
418 }
419
420 return selectorSet;
421 }
422
sangho6a9ff0d2017-03-27 11:23:37 +0900423 private void buildMatchs(TrafficSelector.Builder sBuilder, SecurityGroupRule sgRule,
424 Ip4Address vmIp, IpPrefix remoteIp) {
425 buildMatchEthType(sBuilder, sgRule.getEtherType());
426 buildMatchDirection(sBuilder, sgRule.getDirection(), vmIp);
427 buildMatchProto(sBuilder, sgRule.getProtocol());
428 buildMatchPort(sBuilder, sgRule.getProtocol(), sgRule.getDirection(),
sangho2e97be02017-07-03 18:18:27 +0900429 sgRule.getPortRangeMin() == null ? 0 : sgRule.getPortRangeMin(),
430 sgRule.getPortRangeMax() == null ? 0 : sgRule.getPortRangeMax());
sangho6a9ff0d2017-03-27 11:23:37 +0900431 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
432 if (sgRule.getRemoteGroupId() != null && sgRule.getRemoteGroupId().isEmpty()) {
433 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
434 }
435 }
436
437 private void buildMatchDirection(TrafficSelector.Builder sBuilder,
438 String direction,
439 Ip4Address vmIp) {
440 if (direction.toUpperCase().equals(EGRESS)) {
441 sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, 32));
442 } else {
443 sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, 32));
444 }
445 }
446
447 private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
448 // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
449 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
450 if (etherType != null && !Objects.equals(etherType, "null") &&
451 !etherType.toUpperCase().equals(ETHTYPE_IPV4)) {
452 log.debug("EthType {} is not supported yet in Security Group", etherType);
453 }
454 }
455
456 private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder, IpPrefix remoteIpPrefix, String direction) {
457 if (remoteIpPrefix != null && !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
458 if (direction.toUpperCase().equals(EGRESS)) {
459 sBuilder.matchIPDst(remoteIpPrefix);
460 } else {
461 sBuilder.matchIPSrc(remoteIpPrefix);
462 }
463 }
464 }
465
466 private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
467 if (protocol != null) {
468 switch (protocol.toUpperCase()) {
469 case PROTO_ICMP:
470 sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
471 break;
472 case PROTO_TCP:
473 sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
474 break;
475 case PROTO_UDP:
476 sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
477 break;
478 default:
479 }
480 }
481 }
482
483 private void buildMatchPort(TrafficSelector.Builder sBuilder, String protocol, String direction,
484 int portMin, int portMax) {
485 if (portMin > 0 && portMax > 0 && portMin == portMax) {
486 if (protocol.toUpperCase().equals(PROTO_TCP)) {
487 if (direction.toUpperCase().equals(EGRESS)) {
488 sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
489 } else {
490 sBuilder.matchTcpDst(TpPort.tpPort(portMax));
491 }
492 } else if (protocol.toUpperCase().equals(PROTO_UDP)) {
493 if (direction.toUpperCase().equals(EGRESS)) {
494 sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
495 } else {
496 sBuilder.matchUdpDst(TpPort.tpPort(portMax));
497 }
498 }
499 }
500 }
501
sangho0248ca22017-05-31 13:22:47 +0900502 private void resetSecurityGroupRules() {
503
504 if (useSecurityGroup) {
sangho3dd2a8b2017-07-19 15:54:31 +0900505 osNodeService.completeNodes(OpenstackNode.NodeType.COMPUTE)
506 .forEach(node -> osFlowRuleService.setUpTableMissEntry(node.intgBridge(), ACL_TABLE));
sangho0248ca22017-05-31 13:22:47 +0900507 securityGroupService.securityGroups().forEach(securityGroup ->
508 securityGroup.getRules().forEach(this::securityGroupRuleAdded));
sangho1aaa7882017-05-31 13:22:47 +0900509 osNodeService.nodes().stream()
510 .filter(node -> node.type().equals(OpenstackNode.NodeType.COMPUTE))
511 .forEach(node -> initializeConnTrackTable(node .intgBridge(), true));
sangho0248ca22017-05-31 13:22:47 +0900512 } else {
sangho3dd2a8b2017-07-19 15:54:31 +0900513 osNodeService.completeNodes(OpenstackNode.NodeType.COMPUTE)
514 .forEach(node -> osFlowRuleService.connectTables(node.intgBridge(), ACL_TABLE, JUMP_TABLE));
sangho0248ca22017-05-31 13:22:47 +0900515 securityGroupService.securityGroups().forEach(securityGroup ->
516 securityGroup.getRules().forEach(this::securityGroupRuleRemoved));
sangho1aaa7882017-05-31 13:22:47 +0900517 osNodeService.nodes().stream()
518 .filter(node -> node.type().equals(OpenstackNode.NodeType.COMPUTE))
519 .forEach(node -> initializeConnTrackTable(node.intgBridge(), false));
sangho0248ca22017-05-31 13:22:47 +0900520 }
521
522 log.info("Reset security group info " + (useSecurityGroup ? " with " : " without") + " Security Group");
523 }
524
525 private void securityGroupRuleAdded(SecurityGroupRule sgRule) {
526 osNetService.ports().stream()
527 .filter(port -> port.getSecurityGroups().contains(sgRule.getSecurityGroupId()))
528 .forEach(port -> {
529 updateSecurityGroupRule(
530 instancePortService.instancePort(port.getId()),
531 port, sgRule, true);
532 log.debug("Applied security group rule {} to port {}",
533 sgRule.getId(), port.getId());
534 });
535 }
536
537 private void securityGroupRuleRemoved(SecurityGroupRule sgRule) {
538 osNetService.ports().stream()
539 .filter(port -> port.getSecurityGroups().contains(sgRule.getSecurityGroupId()))
540 .forEach(port -> {
541 updateSecurityGroupRule(
542 instancePortService.instancePort(port.getId()),
543 port, sgRule, false);
544 log.debug("Removed security group rule {} from port {}",
545 sgRule.getId(), port.getId());
546 });
547 }
548
sangho2e97be02017-07-03 18:18:27 +0900549 private int binLower(String binStr, int bits) {
550 String outBin = binStr.substring(0, 16 - bits);
551 for (int i = 0; i < bits; i++) {
552 outBin += "0";
553 }
554
555 return Integer.parseInt(outBin, 2);
556 }
557
558 private int binHigher(String binStr, int bits) {
559 String outBin = binStr.substring(0, 16 - bits);
560 for (int i = 0; i < bits; i++) {
561 outBin += "1";
562 }
563
564 return Integer.parseInt(outBin, 2);
565 }
566
567 private int testMasks(String binStr, int start, int end) {
568 int mask = 0;
569 for (; mask <= 16; mask++) {
570 int maskStart = binLower(binStr, mask);
571 int maskEnd = binHigher(binStr, mask);
572 if (maskStart < start || maskEnd > end) {
573 return mask - 1;
574 }
575 }
576
577 return mask;
578 }
579
580 private String getMask(int bits) {
581 switch (bits) {
582 case 0: return "ffff";
583 case 1: return "fffe";
584 case 2: return "fffc";
585 case 3: return "fff8";
586 case 4: return "fff0";
587 case 5: return "ffe0";
588 case 6: return "ffc0";
589 case 7: return "ff80";
590 case 8: return "ff00";
591 case 9: return "fe00";
592 case 10: return "fc00";
593 case 11: return "f800";
594 case 12: return "f000";
595 case 13: return "e000";
596 case 14: return "c000";
597 case 15: return "8000";
598 case 16: return "0000";
599 default: return null;
600 }
601 }
602
603 private Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
604
605 boolean processing = true;
606 int start = portMin;
607 Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
608 while (processing) {
609 String minStr = Integer.toBinaryString(start);
610 String binStrMinPadded = "0000000000000000".substring(minStr.length()) + minStr;
611
612 int mask = testMasks(binStrMinPadded, start, portMax);
613 int maskStart = binLower(binStrMinPadded, mask);
614 int maskEnd = binHigher(binStrMinPadded, mask);
615
616 log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
617 portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(Integer.parseInt(getMask(mask), 16)));
618
619 start = maskEnd + 1;
620 if (start > portMax) {
621 processing = false;
622 }
623 }
624
625 return portMaskMap;
626 }
627
sangho6a9ff0d2017-03-27 11:23:37 +0900628 private class InternalInstancePortListener implements InstancePortListener {
629
630 @Override
631 public boolean isRelevant(InstancePortEvent event) {
632 InstancePort instPort = event.subject();
sangho0248ca22017-05-31 13:22:47 +0900633 if (!useSecurityGroup) {
634 return false;
635 }
sangho6a9ff0d2017-03-27 11:23:37 +0900636 return mastershipService.isLocalMaster(instPort.deviceId());
637 }
638
639 @Override
640 public void event(InstancePortEvent event) {
641 InstancePort instPort = event.subject();
642 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900643 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900644 case OPENSTACK_INSTANCE_PORT_UPDATED:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900645 log.debug("Instance port detected MAC:{} IP:{}",
646 instPort.macAddress(),
647 instPort.ipAddress());
sangho6a9ff0d2017-03-27 11:23:37 +0900648 eventExecutor.execute(() -> {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900649 setSecurityGroupRules(instPort,
650 osNetService.port(event.subject().portId()),
651 true);
sangho6a9ff0d2017-03-27 11:23:37 +0900652 });
653 break;
654 case OPENSTACK_INSTANCE_PORT_VANISHED:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900655 log.debug("Instance port vanished MAC:{} IP:{}",
656 instPort.macAddress(),
657 instPort.ipAddress());
sangho6a9ff0d2017-03-27 11:23:37 +0900658 eventExecutor.execute(() -> {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900659 setSecurityGroupRules(instPort,
660 osNetService.port(event.subject().portId()),
661 false);
sangho6a9ff0d2017-03-27 11:23:37 +0900662 });
663 break;
664 default:
665 break;
666 }
667 }
sangho6a9ff0d2017-03-27 11:23:37 +0900668 }
669
670 private class InternalOpenstackPortListener implements OpenstackNetworkListener {
671
672 @Override
673 public boolean isRelevant(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +0800674 if (event.port() == null || Strings.isNullOrEmpty(event.port().getId())) {
sangho6a9ff0d2017-03-27 11:23:37 +0900675 return false;
676 }
Hyunsun Moonae51e732017-04-25 17:46:21 +0900677 if (event.securityGroupId() == null ||
678 securityGroupService.securityGroup(event.securityGroupId()) == null) {
679 return false;
680 }
681 if (instancePortService.instancePort(event.port().getId()) == null) {
682 return false;
683 }
sangho0248ca22017-05-31 13:22:47 +0900684 if (!useSecurityGroup) {
685 return false;
686 }
Hyunsun Moonae51e732017-04-25 17:46:21 +0900687 return true;
sangho6a9ff0d2017-03-27 11:23:37 +0900688 }
689
690 @Override
691 public void event(OpenstackNetworkEvent event) {
sanghoshinbbeb31a2018-09-11 17:01:01 +0800692 log.debug("security group event received {}", event);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900693 Port osPort = event.port();
694 InstancePort instPort = instancePortService.instancePort(osPort.getId());
695 SecurityGroup osSg = securityGroupService.securityGroup(event.securityGroupId());
696
sangho6a9ff0d2017-03-27 11:23:37 +0900697 switch (event.type()) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900698 case OPENSTACK_PORT_SECURITY_GROUP_ADDED:
699 eventExecutor.execute(() -> {
700 osSg.getRules().forEach(sgRule -> {
701 updateSecurityGroupRule(instPort, osPort, sgRule, true);
702 });
703 log.info("Added security group {} to port {}",
704 event.securityGroupId(), event.port().getId());
705 });
sangho6a9ff0d2017-03-27 11:23:37 +0900706 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +0900707 case OPENSTACK_PORT_SECURITY_GROUP_REMOVED:
708 eventExecutor.execute(() -> {
709 osSg.getRules().forEach(sgRule -> {
710 updateSecurityGroupRule(instPort, osPort, sgRule, false);
711 });
712 log.info("Removed security group {} from port {}",
713 event.securityGroupId(), event.port().getId());
714 });
sangho6a9ff0d2017-03-27 11:23:37 +0900715 break;
716 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900717 // do nothing for the other events
sangho6a9ff0d2017-03-27 11:23:37 +0900718 break;
719 }
720 }
sangho6a9ff0d2017-03-27 11:23:37 +0900721 }
722
723 private class InternalSecurityGroupListener implements OpenstackSecurityGroupListener {
724
725 @Override
sangho0248ca22017-05-31 13:22:47 +0900726 public boolean isRelevant(OpenstackSecurityGroupEvent event) {
727 if (!useSecurityGroup) {
728 return false;
729 }
730 return true;
731 }
732
733 @Override
sangho6a9ff0d2017-03-27 11:23:37 +0900734 public void event(OpenstackSecurityGroupEvent event) {
735 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900736 case OPENSTACK_SECURITY_GROUP_RULE_CREATED:
737 SecurityGroupRule securityGroupRuleToAdd = event.securityGroupRule();
738 eventExecutor.execute(() -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900739 securityGroupRuleAdded(securityGroupRuleToAdd);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900740 log.info("Applied new security group rule {} to ports",
741 securityGroupRuleToAdd.getId());
sangho6a9ff0d2017-03-27 11:23:37 +0900742 });
743 break;
744
745 case OPENSTACK_SECURITY_GROUP_RULE_REMOVED:
746 SecurityGroupRule securityGroupRuleToRemove = event.securityGroupRule();
747 eventExecutor.execute(() -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900748 securityGroupRuleRemoved(securityGroupRuleToRemove);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900749 log.info("Removed security group rule {} from ports",
750 securityGroupRuleToRemove.getId());
sangho6a9ff0d2017-03-27 11:23:37 +0900751 });
752 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +0900753 case OPENSTACK_SECURITY_GROUP_CREATED:
754 case OPENSTACK_SECURITY_GROUP_REMOVED:
sangho6a9ff0d2017-03-27 11:23:37 +0900755 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900756 // do nothing
757 break;
sangho6a9ff0d2017-03-27 11:23:37 +0900758 }
759 }
sangho6a9ff0d2017-03-27 11:23:37 +0900760 }
sangho1aaa7882017-05-31 13:22:47 +0900761
762 private class InternalNodeListener implements OpenstackNodeListener {
763
764 @Override
765 public boolean isRelevant(OpenstackNodeEvent event) {
766 // do not allow to proceed without leadership
767 NodeId leader = leadershipService.getLeader(appId.name());
768 if (!Objects.equals(localNodeId, leader)) {
769 return false;
770 }
771 return event.subject().type() == COMPUTE;
772 }
773
774 @Override
775 public void event(OpenstackNodeEvent event) {
776 OpenstackNode osNode = event.subject();
777
778 switch (event.type()) {
779 case OPENSTACK_NODE_COMPLETE:
780 eventExecutor.execute(() -> {
781 try {
782 if (useSecurityGroup) {
783 initializeConnTrackTable(osNode.intgBridge(), true);
784 log.warn("SG table initialization : {} is done", osNode.intgBridge());
785 }
786 } catch (IllegalArgumentException e) {
787 log.error("ACL table initialization error : {}", e.getMessage());
788 }
789 });
790 break;
791 case OPENSTACK_NODE_CREATED:
792 case OPENSTACK_NODE_REMOVED:
793 case OPENSTACK_NODE_UPDATED:
794 case OPENSTACK_NODE_INCOMPLETE:
795 default:
796 break;
797 }
798 }
799 }
sangho6a9ff0d2017-03-27 11:23:37 +0900800}