blob: 23beaf927d388ef553c21c0e0fbbb94bd676912e [file] [log] [blame]
sangho6a9ff0d2017-03-27 11:23:37 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002* Copyright 2017-present Open Networking Foundation
sangho6a9ff0d2017-03-27 11:23:37 +09003*
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*/
16
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;
sangho3dd2a8b2017-07-19 15:54:31 +090061import org.onosproject.openstacknode.api.OpenstackNode;
sangho1aaa7882017-05-31 13:22:47 +090062import org.onosproject.openstacknode.api.OpenstackNodeEvent;
63import org.onosproject.openstacknode.api.OpenstackNodeListener;
sangho3dd2a8b2017-07-19 15:54:31 +090064import org.onosproject.openstacknode.api.OpenstackNodeService;
sangho6a9ff0d2017-03-27 11:23:37 +090065import org.openstack4j.model.network.Port;
66import org.openstack4j.model.network.SecurityGroup;
67import org.openstack4j.model.network.SecurityGroupRule;
68import org.openstack4j.openstack.networking.domain.NeutronSecurityGroupRule;
sangho0248ca22017-05-31 13:22:47 +090069import org.osgi.service.component.ComponentContext;
sangho6a9ff0d2017-03-27 11:23:37 +090070import org.slf4j.Logger;
71
sangho6a9ff0d2017-03-27 11:23:37 +090072import java.util.Collections;
sangho0248ca22017-05-31 13:22:47 +090073import java.util.Dictionary;
sangho2e97be02017-07-03 18:18:27 +090074import java.util.Map;
sangho6a9ff0d2017-03-27 11:23:37 +090075import java.util.Objects;
76import java.util.Set;
77import java.util.concurrent.ExecutorService;
78import java.util.stream.Collectors;
79
80import static java.util.concurrent.Executors.newSingleThreadExecutor;
81import static org.onlab.util.Tools.groupedThreads;
sanghodc375372017-06-08 10:41:30 +090082import static org.onosproject.openstacknetworking.api.Constants.ACL_TABLE;
sangho3dd2a8b2017-07-19 15:54:31 +090083import static org.onosproject.openstacknetworking.api.Constants.JUMP_TABLE;
sangho1aaa7882017-05-31 13:22:47 +090084import static org.onosproject.openstacknetworking.api.Constants.CT_TABLE;
85import static org.onosproject.openstacknetworking.api.Constants.ERROR_TABLE;
sangho6a9ff0d2017-03-27 11:23:37 +090086import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
87import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ACL_RULE;
sangho1aaa7882017-05-31 13:22:47 +090088import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_DROP_RULE;
89import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_HOOK_RULE;
90import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CT_RULE;
91import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
sangho6a9ff0d2017-03-27 11:23:37 +090092import static org.slf4j.LoggerFactory.getLogger;
93
94/**
95 * Populates flow rules to handle OpenStack SecurityGroups.
96 */
97@Component(immediate = true)
98public class OpenstackSecurityGroupHandler {
99
100 private final Logger log = getLogger(getClass());
101
sangho0248ca22017-05-31 13:22:47 +0900102 private static final boolean USE_SECURITY_GROUP = false;
103
104 @Property(name = "useSecurityGroup", boolValue = USE_SECURITY_GROUP,
105 label = "Apply OpenStack security group rule for VM traffic")
106 private boolean useSecurityGroup = USE_SECURITY_GROUP;
107
sangho6a9ff0d2017-03-27 11:23:37 +0900108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected CoreService coreService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected InstancePortService instancePortService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected MastershipService mastershipService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moonae51e732017-04-25 17:46:21 +0900118 protected OpenstackNetworkService osNetService;
sangho6a9ff0d2017-03-27 11:23:37 +0900119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected OpenstackSecurityGroupService securityGroupService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sanghodc375372017-06-08 10:41:30 +0900124 protected OpenstackFlowRuleService osFlowRuleService;
sangho6a9ff0d2017-03-27 11:23:37 +0900125
sangho0248ca22017-05-31 13:22:47 +0900126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected ComponentConfigService configService;
128
sangho3dd2a8b2017-07-19 15:54:31 +0900129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected OpenstackNodeService osNodeService;
131
sanghoe6457a32017-08-24 14:31:19 +0900132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho1aaa7882017-05-31 13:22:47 +0900133 protected DriverService driverService;
134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected LeadershipService leadershipService;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
139 protected ClusterService clusterService;
140
sangho6a9ff0d2017-03-27 11:23:37 +0900141 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
142 private final OpenstackNetworkListener portListener = new InternalOpenstackPortListener();
143 private final OpenstackSecurityGroupListener securityGroupListener = new InternalSecurityGroupListener();
sangho1aaa7882017-05-31 13:22:47 +0900144 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
sangho6a9ff0d2017-03-27 11:23:37 +0900145 private ApplicationId appId;
sangho1aaa7882017-05-31 13:22:47 +0900146 private NodeId localNodeId;
sangho6a9ff0d2017-03-27 11:23:37 +0900147
148 private final ExecutorService eventExecutor = newSingleThreadExecutor(
149 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
150
151 private static final String PROTO_ICMP = "ICMP";
152 private static final String PROTO_TCP = "TCP";
153 private static final String PROTO_UDP = "UDP";
154 private static final String ETHTYPE_IPV4 = "IPV4";
155 private static final String EGRESS = "EGRESS";
156 private static final String INGRESS = "INGRESS";
157 private static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
158
sangho1aaa7882017-05-31 13:22:47 +0900159 // We expose pipeline structure to SONA application considering removing pipeline soon.
160 private static final int GOTO_CONNTRACK_TABLE = 2;
161 private static final int GOTO_JUMP_TABLE = 3;
162
163 private static final int CT_COMMIT = 0;
164 private static final int CT_NO_COMMIT = 1;
165 private static final short CT_NO_RECIRC = -1;
166
167 private static final int ACTION_NONE = 0;
168 private static final int ACTION_DROP = -1;
169
sangho6a9ff0d2017-03-27 11:23:37 +0900170 @Activate
171 protected void activate() {
172 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
sangho1aaa7882017-05-31 13:22:47 +0900173 localNodeId = clusterService.getLocalNode().id();
sangho6a9ff0d2017-03-27 11:23:37 +0900174 instancePortService.addListener(instancePortListener);
175 securityGroupService.addListener(securityGroupListener);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900176 osNetService.addListener(portListener);
sangho0248ca22017-05-31 13:22:47 +0900177 configService.registerProperties(getClass());
sangho1aaa7882017-05-31 13:22:47 +0900178 osNodeService.addListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900179
180 log.info("Started");
181 }
182
183 @Deactivate
184 protected void deactivate() {
185 instancePortService.removeListener(instancePortListener);
186 securityGroupService.removeListener(securityGroupListener);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900187 osNetService.removeListener(portListener);
sangho0248ca22017-05-31 13:22:47 +0900188 configService.unregisterProperties(getClass(), false);
sangho1aaa7882017-05-31 13:22:47 +0900189 osNodeService.removeListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900190 eventExecutor.shutdown();
191
192 log.info("Stopped");
193 }
194
sangho0248ca22017-05-31 13:22:47 +0900195 @Modified
196 protected void modified(ComponentContext context) {
197 Dictionary<?, ?> properties = context.getProperties();
198 Boolean flag;
199
200 flag = Tools.isPropertyEnabled(properties, "useSecurityGroup");
201 if (flag == null) {
202 log.info("useSecurityGroup is not configured, " +
203 "using current value of {}", useSecurityGroup);
204 } else {
205 useSecurityGroup = flag;
206 log.info("Configured. useSecurityGroup is {}",
207 useSecurityGroup ? "enabled" : "disabled");
208 }
209
sanghoe6457a32017-08-24 14:31:19 +0900210 securityGroupService.setSecurityGroupEnabled(useSecurityGroup);
sangho0248ca22017-05-31 13:22:47 +0900211 resetSecurityGroupRules();
212 }
213
sangho1aaa7882017-05-31 13:22:47 +0900214 private void initializeConnTrackTable(DeviceId deviceId, boolean install) {
215
216 //table=1,ip,ct_state=-trk, actions=ct(table:2)
217 long ctState = RulePopulatorUtil.computeCtStateFlag(false, false, false);
218 long ctMask = RulePopulatorUtil.computeCtMaskFlag(true, false, false);
219 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, (short) GOTO_CONNTRACK_TABLE,
220 ACTION_NONE, PRIORITY_CT_HOOK_RULE, install);
221
222 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+est,action=goto_table:3
223 ctState = RulePopulatorUtil.computeCtStateFlag(true, false, true);
224 ctMask = RulePopulatorUtil.computeCtMaskFlag(true, false, true);
225 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
226 GOTO_JUMP_TABLE, PRIORITY_CT_RULE, install);
227
228 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+new,action=drop
229 ctState = RulePopulatorUtil.computeCtStateFlag(true, true, false);
230 ctMask = RulePopulatorUtil.computeCtMaskFlag(true, true, false);
231 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
232 ACTION_DROP, PRIORITY_CT_DROP_RULE, install);
233 }
234
sangho6a9ff0d2017-03-27 11:23:37 +0900235 private void setSecurityGroupRules(InstancePort instPort, Port port, boolean install) {
236 port.getSecurityGroups().forEach(sgId -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900237 SecurityGroup sg = securityGroupService.securityGroup(sgId);
238 if (sg == null) {
239 log.error("Security Group Not Found : {}", sgId);
240 return;
241 }
242 sg.getRules().forEach(sgRule -> updateSecurityGroupRule(instPort, port, sgRule, install));
Hyunsun Moonae51e732017-04-25 17:46:21 +0900243 final String action = install ? "Installed " : "Removed ";
244 log.debug(action + "security group rule ID : " + sgId);
sangho6a9ff0d2017-03-27 11:23:37 +0900245 });
246 }
247
248 private void updateSecurityGroupRule(InstancePort instPort, Port port, SecurityGroupRule sgRule, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900249
sangho6a9ff0d2017-03-27 11:23:37 +0900250 if (sgRule.getRemoteGroupId() != null && !sgRule.getRemoteGroupId().isEmpty()) {
251 getRemoteInstPorts(port.getTenantId(), sgRule.getRemoteGroupId())
252 .forEach(rInstPort -> {
253 populateSecurityGroupRule(sgRule, instPort, rInstPort.ipAddress().toIpPrefix(), install);
254 populateSecurityGroupRule(sgRule, rInstPort, instPort.ipAddress().toIpPrefix(), install);
255
256 SecurityGroupRule rSgRule = new NeutronSecurityGroupRule.SecurityGroupRuleConcreteBuilder()
257 .from(sgRule)
258 .direction(sgRule.getDirection().toUpperCase().equals(EGRESS) ? INGRESS : EGRESS).build();
259 populateSecurityGroupRule(rSgRule, instPort, rInstPort.ipAddress().toIpPrefix(), install);
260 populateSecurityGroupRule(rSgRule, rInstPort, instPort.ipAddress().toIpPrefix(), install);
261 });
262 } else {
263 populateSecurityGroupRule(sgRule, instPort, sgRule.getRemoteIpPrefix() == null ? IP_PREFIX_ANY :
264 IpPrefix.valueOf(sgRule.getRemoteIpPrefix()), install);
265 }
266 }
267
268 private void populateSecurityGroupRule(SecurityGroupRule sgRule, InstancePort instPort,
269 IpPrefix remoteIp, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900270 Set<TrafficSelector> selectors = buildSelectors(sgRule,
271 Ip4Address.valueOf(instPort.ipAddress().toInetAddress()), remoteIp);
272 if (selectors == null || selectors.isEmpty()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900273 return;
274 }
275
sangho2e97be02017-07-03 18:18:27 +0900276 selectors.forEach(selector -> {
277 osFlowRuleService.setRule(appId,
278 instPort.deviceId(),
279 selector,
sangho3dd2a8b2017-07-19 15:54:31 +0900280 DefaultTrafficTreatment.builder().transition(JUMP_TABLE).build(),
sangho2e97be02017-07-03 18:18:27 +0900281 PRIORITY_ACL_RULE,
282 ACL_TABLE,
283 install);
284 });
sangho6a9ff0d2017-03-27 11:23:37 +0900285 }
286
287 /**
sangho1aaa7882017-05-31 13:22:47 +0900288 * Sets connection tracking rule using OVS extension commands.
289 * 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 -0700290 * only here. The following is the usage of the function.
sangho1aaa7882017-05-31 13:22:47 +0900291 *
292 * @param deviceId Device ID
293 * @param ctState ctState: please use RulePopulatorUtil.computeCtStateFlag() to build the value
294 * @param ctMask crMask: please use RulePopulatorUtil.computeCtMaskFlag() to build the value
295 * @param commit CT_COMMIT for commit action, CT_NO_COMMIT otherwise
296 * @param recircTable table number for recirculation after CT actions. CT_NO_RECIRC with no recirculation
297 * @param action Additional actions. ACTION_DROP, ACTION_NONE, GOTO_XXX_TABLE are supported.
298 * @param priority priority value for the rule
299 * @param install true for insertion, false for removal
300 */
301 private void setConnTrackRule(DeviceId deviceId, long ctState, long ctMask,
302 int commit, short recircTable,
303 int action, int priority, boolean install) {
304
305 ExtensionSelector esCtSate = RulePopulatorUtil.buildCtExtensionSelector(driverService, deviceId,
306 ctState, ctMask);
307 TrafficSelector selector = DefaultTrafficSelector.builder()
308 .extension(esCtSate, deviceId)
309 .matchEthType(Ethernet.TYPE_IPV4)
310 .build();
311
312 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
313
314 if (commit == CT_COMMIT || recircTable > 0) {
315 RulePopulatorUtil.NiriraConnTrackTreatmentBuilder natTreatmentBuilder =
316 RulePopulatorUtil.niciraConnTrackTreatmentBuilder(driverService, deviceId);
317 natTreatmentBuilder.natAction(false);
318 if (commit == CT_COMMIT) {
319 natTreatmentBuilder.commit(true);
320 } else {
321 natTreatmentBuilder.commit(false);
322 }
323 if (recircTable > 0) {
324 natTreatmentBuilder.table(recircTable);
325 }
326 tb.extension(natTreatmentBuilder.build(), deviceId);
327 } else if (action == ACTION_DROP) {
328 tb.drop();
329 }
330
331 if (action != ACTION_NONE) {
332 tb.transition(action);
333 }
334
335 int tableType = ERROR_TABLE;
336 if (priority == PRIORITY_CT_RULE || priority == PRIORITY_CT_DROP_RULE) {
337 tableType = CT_TABLE;
338 } else if (priority == PRIORITY_CT_HOOK_RULE) {
339 tableType = ACL_TABLE;
340 } else {
341 log.error("Cannot an appropriate table for the conn track rule.");
342 }
343
344 osFlowRuleService.setRule(
345 appId,
346 deviceId,
347 selector,
348 tb.build(),
349 priority,
350 tableType,
351 install);
352 }
353
354 /**
sangho6a9ff0d2017-03-27 11:23:37 +0900355 * Returns a set of host IP addresses engaged with supplied security group ID.
356 * It only searches a VM in the same tenant boundary.
357 *
358 * @param tenantId tenant id
359 * @param sgId security group id
360 * @return set of ip addresses
361 */
362 private Set<InstancePort> getRemoteInstPorts(String tenantId, String sgId) {
363 Set<InstancePort> remoteInstPorts;
364
Hyunsun Moonae51e732017-04-25 17:46:21 +0900365 remoteInstPorts = osNetService.ports().stream()
sangho6a9ff0d2017-03-27 11:23:37 +0900366 .filter(port -> port.getTenantId().equals(tenantId))
367 .filter(port -> port.getSecurityGroups().contains(sgId))
368 .map(port -> instancePortService.instancePort(port.getId()))
369 .filter(instPort -> instPort != null && instPort.ipAddress() != null)
370 .collect(Collectors.toSet());
371
372 return Collections.unmodifiableSet(remoteInstPorts);
373 }
374
sangho2e97be02017-07-03 18:18:27 +0900375 private Set<TrafficSelector> buildSelectors(SecurityGroupRule sgRule,
376 Ip4Address vmIp,
377 IpPrefix remoteIp) {
378 if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, 32))) {
379 // do nothing if the remote IP is my IP
380 return null;
381 }
382
383 Set<TrafficSelector> selectorSet = Sets.newHashSet();
384
385 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
386 buildMatchs(sBuilder, sgRule, vmIp, remoteIp);
387
388 if (sgRule.getPortRangeMax() != null && sgRule.getPortRangeMin() != null &&
389 sgRule.getPortRangeMin() < sgRule.getPortRangeMax()) {
390 Map<TpPort, TpPort> portRangeMatchMap = buildPortRangeMatches(sgRule.getPortRangeMin(),
391 sgRule.getPortRangeMax());
392 portRangeMatchMap.entrySet().forEach(entry -> {
393
394 if (sgRule.getProtocol().toUpperCase().equals(PROTO_TCP)) {
395 if (sgRule.getDirection().toUpperCase().equals(EGRESS)) {
396 sBuilder.matchTcpSrcMasked(entry.getKey(), entry.getValue());
397 } else {
398 sBuilder.matchTcpDstMasked(entry.getKey(), entry.getValue());
399 }
400 } else if (sgRule.getProtocol().toUpperCase().equals(PROTO_UDP)) {
401 if (sgRule.getDirection().toUpperCase().equals(EGRESS)) {
402 sBuilder.matchUdpSrcMasked(entry.getKey(), entry.getValue());
403 } else {
404 sBuilder.matchUdpDstMasked(entry.getKey(), entry.getValue());
405 }
406 }
407
408 selectorSet.add(sBuilder.build());
409 }
410 );
411 } else {
412 selectorSet.add(sBuilder.build());
413 }
414
415 return selectorSet;
416 }
417
sangho6a9ff0d2017-03-27 11:23:37 +0900418 private void buildMatchs(TrafficSelector.Builder sBuilder, SecurityGroupRule sgRule,
419 Ip4Address vmIp, IpPrefix remoteIp) {
420 buildMatchEthType(sBuilder, sgRule.getEtherType());
421 buildMatchDirection(sBuilder, sgRule.getDirection(), vmIp);
422 buildMatchProto(sBuilder, sgRule.getProtocol());
423 buildMatchPort(sBuilder, sgRule.getProtocol(), sgRule.getDirection(),
sangho2e97be02017-07-03 18:18:27 +0900424 sgRule.getPortRangeMin() == null ? 0 : sgRule.getPortRangeMin(),
425 sgRule.getPortRangeMax() == null ? 0 : sgRule.getPortRangeMax());
sangho6a9ff0d2017-03-27 11:23:37 +0900426 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
427 if (sgRule.getRemoteGroupId() != null && sgRule.getRemoteGroupId().isEmpty()) {
428 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
429 }
430 }
431
432 private void buildMatchDirection(TrafficSelector.Builder sBuilder,
433 String direction,
434 Ip4Address vmIp) {
435 if (direction.toUpperCase().equals(EGRESS)) {
436 sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, 32));
437 } else {
438 sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, 32));
439 }
440 }
441
442 private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
443 // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
444 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
445 if (etherType != null && !Objects.equals(etherType, "null") &&
446 !etherType.toUpperCase().equals(ETHTYPE_IPV4)) {
447 log.debug("EthType {} is not supported yet in Security Group", etherType);
448 }
449 }
450
451 private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder, IpPrefix remoteIpPrefix, String direction) {
452 if (remoteIpPrefix != null && !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
453 if (direction.toUpperCase().equals(EGRESS)) {
454 sBuilder.matchIPDst(remoteIpPrefix);
455 } else {
456 sBuilder.matchIPSrc(remoteIpPrefix);
457 }
458 }
459 }
460
461 private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
462 if (protocol != null) {
463 switch (protocol.toUpperCase()) {
464 case PROTO_ICMP:
465 sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
466 break;
467 case PROTO_TCP:
468 sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
469 break;
470 case PROTO_UDP:
471 sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
472 break;
473 default:
474 }
475 }
476 }
477
478 private void buildMatchPort(TrafficSelector.Builder sBuilder, String protocol, String direction,
479 int portMin, int portMax) {
480 if (portMin > 0 && portMax > 0 && portMin == portMax) {
481 if (protocol.toUpperCase().equals(PROTO_TCP)) {
482 if (direction.toUpperCase().equals(EGRESS)) {
483 sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
484 } else {
485 sBuilder.matchTcpDst(TpPort.tpPort(portMax));
486 }
487 } else if (protocol.toUpperCase().equals(PROTO_UDP)) {
488 if (direction.toUpperCase().equals(EGRESS)) {
489 sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
490 } else {
491 sBuilder.matchUdpDst(TpPort.tpPort(portMax));
492 }
493 }
494 }
495 }
496
sangho0248ca22017-05-31 13:22:47 +0900497 private void resetSecurityGroupRules() {
498
499 if (useSecurityGroup) {
sangho3dd2a8b2017-07-19 15:54:31 +0900500 osNodeService.completeNodes(OpenstackNode.NodeType.COMPUTE)
501 .forEach(node -> osFlowRuleService.setUpTableMissEntry(node.intgBridge(), ACL_TABLE));
sangho0248ca22017-05-31 13:22:47 +0900502 securityGroupService.securityGroups().forEach(securityGroup ->
503 securityGroup.getRules().forEach(this::securityGroupRuleAdded));
sangho1aaa7882017-05-31 13:22:47 +0900504 osNodeService.nodes().stream()
505 .filter(node -> node.type().equals(OpenstackNode.NodeType.COMPUTE))
506 .forEach(node -> initializeConnTrackTable(node .intgBridge(), true));
sangho0248ca22017-05-31 13:22:47 +0900507 } else {
sangho3dd2a8b2017-07-19 15:54:31 +0900508 osNodeService.completeNodes(OpenstackNode.NodeType.COMPUTE)
509 .forEach(node -> osFlowRuleService.connectTables(node.intgBridge(), ACL_TABLE, JUMP_TABLE));
sangho0248ca22017-05-31 13:22:47 +0900510 securityGroupService.securityGroups().forEach(securityGroup ->
511 securityGroup.getRules().forEach(this::securityGroupRuleRemoved));
sangho1aaa7882017-05-31 13:22:47 +0900512 osNodeService.nodes().stream()
513 .filter(node -> node.type().equals(OpenstackNode.NodeType.COMPUTE))
514 .forEach(node -> initializeConnTrackTable(node.intgBridge(), false));
sangho0248ca22017-05-31 13:22:47 +0900515 }
516
517 log.info("Reset security group info " + (useSecurityGroup ? " with " : " without") + " Security Group");
518 }
519
520 private void securityGroupRuleAdded(SecurityGroupRule sgRule) {
521 osNetService.ports().stream()
522 .filter(port -> port.getSecurityGroups().contains(sgRule.getSecurityGroupId()))
523 .forEach(port -> {
524 updateSecurityGroupRule(
525 instancePortService.instancePort(port.getId()),
526 port, sgRule, true);
527 log.debug("Applied security group rule {} to port {}",
528 sgRule.getId(), port.getId());
529 });
530 }
531
532 private void securityGroupRuleRemoved(SecurityGroupRule sgRule) {
533 osNetService.ports().stream()
534 .filter(port -> port.getSecurityGroups().contains(sgRule.getSecurityGroupId()))
535 .forEach(port -> {
536 updateSecurityGroupRule(
537 instancePortService.instancePort(port.getId()),
538 port, sgRule, false);
539 log.debug("Removed security group rule {} from port {}",
540 sgRule.getId(), port.getId());
541 });
542 }
543
sangho2e97be02017-07-03 18:18:27 +0900544 private int binLower(String binStr, int bits) {
545 String outBin = binStr.substring(0, 16 - bits);
546 for (int i = 0; i < bits; i++) {
547 outBin += "0";
548 }
549
550 return Integer.parseInt(outBin, 2);
551 }
552
553 private int binHigher(String binStr, int bits) {
554 String outBin = binStr.substring(0, 16 - bits);
555 for (int i = 0; i < bits; i++) {
556 outBin += "1";
557 }
558
559 return Integer.parseInt(outBin, 2);
560 }
561
562 private int testMasks(String binStr, int start, int end) {
563 int mask = 0;
564 for (; mask <= 16; mask++) {
565 int maskStart = binLower(binStr, mask);
566 int maskEnd = binHigher(binStr, mask);
567 if (maskStart < start || maskEnd > end) {
568 return mask - 1;
569 }
570 }
571
572 return mask;
573 }
574
575 private String getMask(int bits) {
576 switch (bits) {
577 case 0: return "ffff";
578 case 1: return "fffe";
579 case 2: return "fffc";
580 case 3: return "fff8";
581 case 4: return "fff0";
582 case 5: return "ffe0";
583 case 6: return "ffc0";
584 case 7: return "ff80";
585 case 8: return "ff00";
586 case 9: return "fe00";
587 case 10: return "fc00";
588 case 11: return "f800";
589 case 12: return "f000";
590 case 13: return "e000";
591 case 14: return "c000";
592 case 15: return "8000";
593 case 16: return "0000";
594 default: return null;
595 }
596 }
597
598 private Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
599
600 boolean processing = true;
601 int start = portMin;
602 Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
603 while (processing) {
604 String minStr = Integer.toBinaryString(start);
605 String binStrMinPadded = "0000000000000000".substring(minStr.length()) + minStr;
606
607 int mask = testMasks(binStrMinPadded, start, portMax);
608 int maskStart = binLower(binStrMinPadded, mask);
609 int maskEnd = binHigher(binStrMinPadded, mask);
610
611 log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
612 portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(Integer.parseInt(getMask(mask), 16)));
613
614 start = maskEnd + 1;
615 if (start > portMax) {
616 processing = false;
617 }
618 }
619
620 return portMaskMap;
621 }
622
sangho6a9ff0d2017-03-27 11:23:37 +0900623 private class InternalInstancePortListener implements InstancePortListener {
624
625 @Override
626 public boolean isRelevant(InstancePortEvent event) {
627 InstancePort instPort = event.subject();
sangho0248ca22017-05-31 13:22:47 +0900628 if (!useSecurityGroup) {
629 return false;
630 }
sangho6a9ff0d2017-03-27 11:23:37 +0900631 return mastershipService.isLocalMaster(instPort.deviceId());
632 }
633
634 @Override
635 public void event(InstancePortEvent event) {
636 InstancePort instPort = event.subject();
637 switch (event.type()) {
638 case OPENSTACK_INSTANCE_PORT_UPDATED:
639 case OPENSTACK_INSTANCE_PORT_DETECTED:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900640 log.debug("Instance port detected MAC:{} IP:{}",
641 instPort.macAddress(),
642 instPort.ipAddress());
sangho6a9ff0d2017-03-27 11:23:37 +0900643 eventExecutor.execute(() -> {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900644 setSecurityGroupRules(instPort,
645 osNetService.port(event.subject().portId()),
646 true);
sangho6a9ff0d2017-03-27 11:23:37 +0900647 });
648 break;
649 case OPENSTACK_INSTANCE_PORT_VANISHED:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900650 log.debug("Instance port vanished MAC:{} IP:{}",
651 instPort.macAddress(),
652 instPort.ipAddress());
sangho6a9ff0d2017-03-27 11:23:37 +0900653 eventExecutor.execute(() -> {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900654 setSecurityGroupRules(instPort,
655 osNetService.port(event.subject().portId()),
656 false);
sangho6a9ff0d2017-03-27 11:23:37 +0900657 });
658 break;
659 default:
660 break;
661 }
662 }
sangho6a9ff0d2017-03-27 11:23:37 +0900663 }
664
665 private class InternalOpenstackPortListener implements OpenstackNetworkListener {
666
667 @Override
668 public boolean isRelevant(OpenstackNetworkEvent event) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900669 if (event.port() == null || !Strings.isNullOrEmpty(event.port().getId())) {
sangho6a9ff0d2017-03-27 11:23:37 +0900670 return false;
671 }
Hyunsun Moonae51e732017-04-25 17:46:21 +0900672 if (event.securityGroupId() == null ||
673 securityGroupService.securityGroup(event.securityGroupId()) == null) {
674 return false;
675 }
676 if (instancePortService.instancePort(event.port().getId()) == null) {
677 return false;
678 }
sangho0248ca22017-05-31 13:22:47 +0900679 if (!useSecurityGroup) {
680 return false;
681 }
Hyunsun Moonae51e732017-04-25 17:46:21 +0900682 return true;
sangho6a9ff0d2017-03-27 11:23:37 +0900683 }
684
685 @Override
686 public void event(OpenstackNetworkEvent event) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900687 Port osPort = event.port();
688 InstancePort instPort = instancePortService.instancePort(osPort.getId());
689 SecurityGroup osSg = securityGroupService.securityGroup(event.securityGroupId());
690
sangho6a9ff0d2017-03-27 11:23:37 +0900691 switch (event.type()) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900692 case OPENSTACK_PORT_SECURITY_GROUP_ADDED:
693 eventExecutor.execute(() -> {
694 osSg.getRules().forEach(sgRule -> {
695 updateSecurityGroupRule(instPort, osPort, sgRule, true);
696 });
697 log.info("Added security group {} to port {}",
698 event.securityGroupId(), event.port().getId());
699 });
sangho6a9ff0d2017-03-27 11:23:37 +0900700 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +0900701 case OPENSTACK_PORT_SECURITY_GROUP_REMOVED:
702 eventExecutor.execute(() -> {
703 osSg.getRules().forEach(sgRule -> {
704 updateSecurityGroupRule(instPort, osPort, sgRule, false);
705 });
706 log.info("Removed security group {} from port {}",
707 event.securityGroupId(), event.port().getId());
708 });
sangho6a9ff0d2017-03-27 11:23:37 +0900709 break;
710 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900711 // do nothing for the other events
sangho6a9ff0d2017-03-27 11:23:37 +0900712 break;
713 }
714 }
sangho6a9ff0d2017-03-27 11:23:37 +0900715 }
716
717 private class InternalSecurityGroupListener implements OpenstackSecurityGroupListener {
718
719 @Override
sangho0248ca22017-05-31 13:22:47 +0900720 public boolean isRelevant(OpenstackSecurityGroupEvent event) {
721 if (!useSecurityGroup) {
722 return false;
723 }
724 return true;
725 }
726
727 @Override
sangho6a9ff0d2017-03-27 11:23:37 +0900728 public void event(OpenstackSecurityGroupEvent event) {
729 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900730 case OPENSTACK_SECURITY_GROUP_RULE_CREATED:
731 SecurityGroupRule securityGroupRuleToAdd = event.securityGroupRule();
732 eventExecutor.execute(() -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900733 securityGroupRuleAdded(securityGroupRuleToAdd);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900734 log.info("Applied new security group rule {} to ports",
735 securityGroupRuleToAdd.getId());
sangho6a9ff0d2017-03-27 11:23:37 +0900736 });
737 break;
738
739 case OPENSTACK_SECURITY_GROUP_RULE_REMOVED:
740 SecurityGroupRule securityGroupRuleToRemove = event.securityGroupRule();
741 eventExecutor.execute(() -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900742 securityGroupRuleRemoved(securityGroupRuleToRemove);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900743 log.info("Removed security group rule {} from ports",
744 securityGroupRuleToRemove.getId());
sangho6a9ff0d2017-03-27 11:23:37 +0900745 });
746 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +0900747 case OPENSTACK_SECURITY_GROUP_CREATED:
748 case OPENSTACK_SECURITY_GROUP_REMOVED:
sangho6a9ff0d2017-03-27 11:23:37 +0900749 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900750 // do nothing
751 break;
sangho6a9ff0d2017-03-27 11:23:37 +0900752 }
753 }
sangho6a9ff0d2017-03-27 11:23:37 +0900754 }
sangho1aaa7882017-05-31 13:22:47 +0900755
756 private class InternalNodeListener implements OpenstackNodeListener {
757
758 @Override
759 public boolean isRelevant(OpenstackNodeEvent event) {
760 // do not allow to proceed without leadership
761 NodeId leader = leadershipService.getLeader(appId.name());
762 if (!Objects.equals(localNodeId, leader)) {
763 return false;
764 }
765 return event.subject().type() == COMPUTE;
766 }
767
768 @Override
769 public void event(OpenstackNodeEvent event) {
770 OpenstackNode osNode = event.subject();
771
772 switch (event.type()) {
773 case OPENSTACK_NODE_COMPLETE:
774 eventExecutor.execute(() -> {
775 try {
776 if (useSecurityGroup) {
777 initializeConnTrackTable(osNode.intgBridge(), true);
778 log.warn("SG table initialization : {} is done", osNode.intgBridge());
779 }
780 } catch (IllegalArgumentException e) {
781 log.error("ACL table initialization error : {}", e.getMessage());
782 }
783 });
784 break;
785 case OPENSTACK_NODE_CREATED:
786 case OPENSTACK_NODE_REMOVED:
787 case OPENSTACK_NODE_UPDATED:
788 case OPENSTACK_NODE_INCOMPLETE:
789 default:
790 break;
791 }
792 }
793 }
sangho6a9ff0d2017-03-27 11:23:37 +0900794}