blob: f425f3b1eb83aa7f6b446c80e2c9146429c3ab22 [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
sangho1aaa7882017-05-31 13:22:47 +0900132 protected DriverService driverService;
133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135 protected LeadershipService leadershipService;
136
137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected ClusterService clusterService;
139
sangho6a9ff0d2017-03-27 11:23:37 +0900140 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
141 private final OpenstackNetworkListener portListener = new InternalOpenstackPortListener();
142 private final OpenstackSecurityGroupListener securityGroupListener = new InternalSecurityGroupListener();
sangho1aaa7882017-05-31 13:22:47 +0900143 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
sangho6a9ff0d2017-03-27 11:23:37 +0900144 private ApplicationId appId;
sangho1aaa7882017-05-31 13:22:47 +0900145 private NodeId localNodeId;
sangho6a9ff0d2017-03-27 11:23:37 +0900146
147 private final ExecutorService eventExecutor = newSingleThreadExecutor(
148 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
149
150 private static final String PROTO_ICMP = "ICMP";
151 private static final String PROTO_TCP = "TCP";
152 private static final String PROTO_UDP = "UDP";
153 private static final String ETHTYPE_IPV4 = "IPV4";
154 private static final String EGRESS = "EGRESS";
155 private static final String INGRESS = "INGRESS";
156 private static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
157
sangho1aaa7882017-05-31 13:22:47 +0900158 // We expose pipeline structure to SONA application considering removing pipeline soon.
159 private static final int GOTO_CONNTRACK_TABLE = 2;
160 private static final int GOTO_JUMP_TABLE = 3;
161
162 private static final int CT_COMMIT = 0;
163 private static final int CT_NO_COMMIT = 1;
164 private static final short CT_NO_RECIRC = -1;
165
166 private static final int ACTION_NONE = 0;
167 private static final int ACTION_DROP = -1;
168
sangho6a9ff0d2017-03-27 11:23:37 +0900169 @Activate
170 protected void activate() {
171 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
sangho1aaa7882017-05-31 13:22:47 +0900172 localNodeId = clusterService.getLocalNode().id();
sangho6a9ff0d2017-03-27 11:23:37 +0900173 instancePortService.addListener(instancePortListener);
174 securityGroupService.addListener(securityGroupListener);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900175 osNetService.addListener(portListener);
sangho0248ca22017-05-31 13:22:47 +0900176 configService.registerProperties(getClass());
sangho1aaa7882017-05-31 13:22:47 +0900177 osNodeService.addListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900178
179 log.info("Started");
180 }
181
182 @Deactivate
183 protected void deactivate() {
184 instancePortService.removeListener(instancePortListener);
185 securityGroupService.removeListener(securityGroupListener);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900186 osNetService.removeListener(portListener);
sangho0248ca22017-05-31 13:22:47 +0900187 configService.unregisterProperties(getClass(), false);
sangho1aaa7882017-05-31 13:22:47 +0900188 osNodeService.removeListener(osNodeListener);
sangho6a9ff0d2017-03-27 11:23:37 +0900189 eventExecutor.shutdown();
190
191 log.info("Stopped");
192 }
193
sangho0248ca22017-05-31 13:22:47 +0900194 @Modified
195 protected void modified(ComponentContext context) {
196 Dictionary<?, ?> properties = context.getProperties();
197 Boolean flag;
198
199 flag = Tools.isPropertyEnabled(properties, "useSecurityGroup");
200 if (flag == null) {
201 log.info("useSecurityGroup is not configured, " +
202 "using current value of {}", useSecurityGroup);
203 } else {
204 useSecurityGroup = flag;
205 log.info("Configured. useSecurityGroup is {}",
206 useSecurityGroup ? "enabled" : "disabled");
207 }
208
209 resetSecurityGroupRules();
210 }
211
sangho1aaa7882017-05-31 13:22:47 +0900212 private void initializeConnTrackTable(DeviceId deviceId, boolean install) {
213
214 //table=1,ip,ct_state=-trk, actions=ct(table:2)
215 long ctState = RulePopulatorUtil.computeCtStateFlag(false, false, false);
216 long ctMask = RulePopulatorUtil.computeCtMaskFlag(true, false, false);
217 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, (short) GOTO_CONNTRACK_TABLE,
218 ACTION_NONE, PRIORITY_CT_HOOK_RULE, install);
219
220 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+est,action=goto_table:3
221 ctState = RulePopulatorUtil.computeCtStateFlag(true, false, true);
222 ctMask = RulePopulatorUtil.computeCtMaskFlag(true, false, true);
223 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
224 GOTO_JUMP_TABLE, PRIORITY_CT_RULE, install);
225
226 //table=2,ip,nw_dst=10.10.0.2,ct_state=+trk+new,action=drop
227 ctState = RulePopulatorUtil.computeCtStateFlag(true, true, false);
228 ctMask = RulePopulatorUtil.computeCtMaskFlag(true, true, false);
229 setConnTrackRule(deviceId, ctState, ctMask, CT_NO_COMMIT, CT_NO_RECIRC,
230 ACTION_DROP, PRIORITY_CT_DROP_RULE, install);
231 }
232
sangho6a9ff0d2017-03-27 11:23:37 +0900233 private void setSecurityGroupRules(InstancePort instPort, Port port, boolean install) {
234 port.getSecurityGroups().forEach(sgId -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900235 SecurityGroup sg = securityGroupService.securityGroup(sgId);
236 if (sg == null) {
237 log.error("Security Group Not Found : {}", sgId);
238 return;
239 }
240 sg.getRules().forEach(sgRule -> updateSecurityGroupRule(instPort, port, sgRule, install));
Hyunsun Moonae51e732017-04-25 17:46:21 +0900241 final String action = install ? "Installed " : "Removed ";
242 log.debug(action + "security group rule ID : " + sgId);
sangho6a9ff0d2017-03-27 11:23:37 +0900243 });
244 }
245
246 private void updateSecurityGroupRule(InstancePort instPort, Port port, SecurityGroupRule sgRule, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900247
sangho6a9ff0d2017-03-27 11:23:37 +0900248 if (sgRule.getRemoteGroupId() != null && !sgRule.getRemoteGroupId().isEmpty()) {
249 getRemoteInstPorts(port.getTenantId(), sgRule.getRemoteGroupId())
250 .forEach(rInstPort -> {
251 populateSecurityGroupRule(sgRule, instPort, rInstPort.ipAddress().toIpPrefix(), install);
252 populateSecurityGroupRule(sgRule, rInstPort, instPort.ipAddress().toIpPrefix(), install);
253
254 SecurityGroupRule rSgRule = new NeutronSecurityGroupRule.SecurityGroupRuleConcreteBuilder()
255 .from(sgRule)
256 .direction(sgRule.getDirection().toUpperCase().equals(EGRESS) ? INGRESS : EGRESS).build();
257 populateSecurityGroupRule(rSgRule, instPort, rInstPort.ipAddress().toIpPrefix(), install);
258 populateSecurityGroupRule(rSgRule, rInstPort, instPort.ipAddress().toIpPrefix(), install);
259 });
260 } else {
261 populateSecurityGroupRule(sgRule, instPort, sgRule.getRemoteIpPrefix() == null ? IP_PREFIX_ANY :
262 IpPrefix.valueOf(sgRule.getRemoteIpPrefix()), install);
263 }
264 }
265
266 private void populateSecurityGroupRule(SecurityGroupRule sgRule, InstancePort instPort,
267 IpPrefix remoteIp, boolean install) {
sangho2e97be02017-07-03 18:18:27 +0900268 Set<TrafficSelector> selectors = buildSelectors(sgRule,
269 Ip4Address.valueOf(instPort.ipAddress().toInetAddress()), remoteIp);
270 if (selectors == null || selectors.isEmpty()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900271 return;
272 }
273
sangho2e97be02017-07-03 18:18:27 +0900274 selectors.forEach(selector -> {
275 osFlowRuleService.setRule(appId,
276 instPort.deviceId(),
277 selector,
sangho3dd2a8b2017-07-19 15:54:31 +0900278 DefaultTrafficTreatment.builder().transition(JUMP_TABLE).build(),
sangho2e97be02017-07-03 18:18:27 +0900279 PRIORITY_ACL_RULE,
280 ACL_TABLE,
281 install);
282 });
sangho6a9ff0d2017-03-27 11:23:37 +0900283 }
284
285 /**
sangho1aaa7882017-05-31 13:22:47 +0900286 * Sets connection tracking rule using OVS extension commands.
287 * It is not so graceful, but I don't want to make it more general because it is going to be used
288 * only here. The following is the usage of the fucntion.
289 *
290 * @param deviceId Device ID
291 * @param ctState ctState: please use RulePopulatorUtil.computeCtStateFlag() to build the value
292 * @param ctMask crMask: please use RulePopulatorUtil.computeCtMaskFlag() to build the value
293 * @param commit CT_COMMIT for commit action, CT_NO_COMMIT otherwise
294 * @param recircTable table number for recirculation after CT actions. CT_NO_RECIRC with no recirculation
295 * @param action Additional actions. ACTION_DROP, ACTION_NONE, GOTO_XXX_TABLE are supported.
296 * @param priority priority value for the rule
297 * @param install true for insertion, false for removal
298 */
299 private void setConnTrackRule(DeviceId deviceId, long ctState, long ctMask,
300 int commit, short recircTable,
301 int action, int priority, boolean install) {
302
303 ExtensionSelector esCtSate = RulePopulatorUtil.buildCtExtensionSelector(driverService, deviceId,
304 ctState, ctMask);
305 TrafficSelector selector = DefaultTrafficSelector.builder()
306 .extension(esCtSate, deviceId)
307 .matchEthType(Ethernet.TYPE_IPV4)
308 .build();
309
310 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
311
312 if (commit == CT_COMMIT || recircTable > 0) {
313 RulePopulatorUtil.NiriraConnTrackTreatmentBuilder natTreatmentBuilder =
314 RulePopulatorUtil.niciraConnTrackTreatmentBuilder(driverService, deviceId);
315 natTreatmentBuilder.natAction(false);
316 if (commit == CT_COMMIT) {
317 natTreatmentBuilder.commit(true);
318 } else {
319 natTreatmentBuilder.commit(false);
320 }
321 if (recircTable > 0) {
322 natTreatmentBuilder.table(recircTable);
323 }
324 tb.extension(natTreatmentBuilder.build(), deviceId);
325 } else if (action == ACTION_DROP) {
326 tb.drop();
327 }
328
329 if (action != ACTION_NONE) {
330 tb.transition(action);
331 }
332
333 int tableType = ERROR_TABLE;
334 if (priority == PRIORITY_CT_RULE || priority == PRIORITY_CT_DROP_RULE) {
335 tableType = CT_TABLE;
336 } else if (priority == PRIORITY_CT_HOOK_RULE) {
337 tableType = ACL_TABLE;
338 } else {
339 log.error("Cannot an appropriate table for the conn track rule.");
340 }
341
342 osFlowRuleService.setRule(
343 appId,
344 deviceId,
345 selector,
346 tb.build(),
347 priority,
348 tableType,
349 install);
350 }
351
352 /**
sangho6a9ff0d2017-03-27 11:23:37 +0900353 * Returns a set of host IP addresses engaged with supplied security group ID.
354 * It only searches a VM in the same tenant boundary.
355 *
356 * @param tenantId tenant id
357 * @param sgId security group id
358 * @return set of ip addresses
359 */
360 private Set<InstancePort> getRemoteInstPorts(String tenantId, String sgId) {
361 Set<InstancePort> remoteInstPorts;
362
Hyunsun Moonae51e732017-04-25 17:46:21 +0900363 remoteInstPorts = osNetService.ports().stream()
sangho6a9ff0d2017-03-27 11:23:37 +0900364 .filter(port -> port.getTenantId().equals(tenantId))
365 .filter(port -> port.getSecurityGroups().contains(sgId))
366 .map(port -> instancePortService.instancePort(port.getId()))
367 .filter(instPort -> instPort != null && instPort.ipAddress() != null)
368 .collect(Collectors.toSet());
369
370 return Collections.unmodifiableSet(remoteInstPorts);
371 }
372
sangho2e97be02017-07-03 18:18:27 +0900373 private Set<TrafficSelector> buildSelectors(SecurityGroupRule sgRule,
374 Ip4Address vmIp,
375 IpPrefix remoteIp) {
376 if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, 32))) {
377 // do nothing if the remote IP is my IP
378 return null;
379 }
380
381 Set<TrafficSelector> selectorSet = Sets.newHashSet();
382
383 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
384 buildMatchs(sBuilder, sgRule, vmIp, remoteIp);
385
386 if (sgRule.getPortRangeMax() != null && sgRule.getPortRangeMin() != null &&
387 sgRule.getPortRangeMin() < sgRule.getPortRangeMax()) {
388 Map<TpPort, TpPort> portRangeMatchMap = buildPortRangeMatches(sgRule.getPortRangeMin(),
389 sgRule.getPortRangeMax());
390 portRangeMatchMap.entrySet().forEach(entry -> {
391
392 if (sgRule.getProtocol().toUpperCase().equals(PROTO_TCP)) {
393 if (sgRule.getDirection().toUpperCase().equals(EGRESS)) {
394 sBuilder.matchTcpSrcMasked(entry.getKey(), entry.getValue());
395 } else {
396 sBuilder.matchTcpDstMasked(entry.getKey(), entry.getValue());
397 }
398 } else if (sgRule.getProtocol().toUpperCase().equals(PROTO_UDP)) {
399 if (sgRule.getDirection().toUpperCase().equals(EGRESS)) {
400 sBuilder.matchUdpSrcMasked(entry.getKey(), entry.getValue());
401 } else {
402 sBuilder.matchUdpDstMasked(entry.getKey(), entry.getValue());
403 }
404 }
405
406 selectorSet.add(sBuilder.build());
407 }
408 );
409 } else {
410 selectorSet.add(sBuilder.build());
411 }
412
413 return selectorSet;
414 }
415
sangho6a9ff0d2017-03-27 11:23:37 +0900416 private void buildMatchs(TrafficSelector.Builder sBuilder, SecurityGroupRule sgRule,
417 Ip4Address vmIp, IpPrefix remoteIp) {
418 buildMatchEthType(sBuilder, sgRule.getEtherType());
419 buildMatchDirection(sBuilder, sgRule.getDirection(), vmIp);
420 buildMatchProto(sBuilder, sgRule.getProtocol());
421 buildMatchPort(sBuilder, sgRule.getProtocol(), sgRule.getDirection(),
sangho2e97be02017-07-03 18:18:27 +0900422 sgRule.getPortRangeMin() == null ? 0 : sgRule.getPortRangeMin(),
423 sgRule.getPortRangeMax() == null ? 0 : sgRule.getPortRangeMax());
sangho6a9ff0d2017-03-27 11:23:37 +0900424 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
425 if (sgRule.getRemoteGroupId() != null && sgRule.getRemoteGroupId().isEmpty()) {
426 buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
427 }
428 }
429
430 private void buildMatchDirection(TrafficSelector.Builder sBuilder,
431 String direction,
432 Ip4Address vmIp) {
433 if (direction.toUpperCase().equals(EGRESS)) {
434 sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, 32));
435 } else {
436 sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, 32));
437 }
438 }
439
440 private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
441 // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
442 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
443 if (etherType != null && !Objects.equals(etherType, "null") &&
444 !etherType.toUpperCase().equals(ETHTYPE_IPV4)) {
445 log.debug("EthType {} is not supported yet in Security Group", etherType);
446 }
447 }
448
449 private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder, IpPrefix remoteIpPrefix, String direction) {
450 if (remoteIpPrefix != null && !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
451 if (direction.toUpperCase().equals(EGRESS)) {
452 sBuilder.matchIPDst(remoteIpPrefix);
453 } else {
454 sBuilder.matchIPSrc(remoteIpPrefix);
455 }
456 }
457 }
458
459 private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
460 if (protocol != null) {
461 switch (protocol.toUpperCase()) {
462 case PROTO_ICMP:
463 sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
464 break;
465 case PROTO_TCP:
466 sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
467 break;
468 case PROTO_UDP:
469 sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
470 break;
471 default:
472 }
473 }
474 }
475
476 private void buildMatchPort(TrafficSelector.Builder sBuilder, String protocol, String direction,
477 int portMin, int portMax) {
478 if (portMin > 0 && portMax > 0 && portMin == portMax) {
479 if (protocol.toUpperCase().equals(PROTO_TCP)) {
480 if (direction.toUpperCase().equals(EGRESS)) {
481 sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
482 } else {
483 sBuilder.matchTcpDst(TpPort.tpPort(portMax));
484 }
485 } else if (protocol.toUpperCase().equals(PROTO_UDP)) {
486 if (direction.toUpperCase().equals(EGRESS)) {
487 sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
488 } else {
489 sBuilder.matchUdpDst(TpPort.tpPort(portMax));
490 }
491 }
492 }
493 }
494
sangho0248ca22017-05-31 13:22:47 +0900495 private void resetSecurityGroupRules() {
496
497 if (useSecurityGroup) {
sangho3dd2a8b2017-07-19 15:54:31 +0900498 osNodeService.completeNodes(OpenstackNode.NodeType.COMPUTE)
499 .forEach(node -> osFlowRuleService.setUpTableMissEntry(node.intgBridge(), ACL_TABLE));
sangho0248ca22017-05-31 13:22:47 +0900500 securityGroupService.securityGroups().forEach(securityGroup ->
501 securityGroup.getRules().forEach(this::securityGroupRuleAdded));
sangho1aaa7882017-05-31 13:22:47 +0900502 osNodeService.nodes().stream()
503 .filter(node -> node.type().equals(OpenstackNode.NodeType.COMPUTE))
504 .forEach(node -> initializeConnTrackTable(node .intgBridge(), true));
sangho0248ca22017-05-31 13:22:47 +0900505 } else {
sangho3dd2a8b2017-07-19 15:54:31 +0900506 osNodeService.completeNodes(OpenstackNode.NodeType.COMPUTE)
507 .forEach(node -> osFlowRuleService.connectTables(node.intgBridge(), ACL_TABLE, JUMP_TABLE));
sangho0248ca22017-05-31 13:22:47 +0900508 securityGroupService.securityGroups().forEach(securityGroup ->
509 securityGroup.getRules().forEach(this::securityGroupRuleRemoved));
sangho1aaa7882017-05-31 13:22:47 +0900510 osNodeService.nodes().stream()
511 .filter(node -> node.type().equals(OpenstackNode.NodeType.COMPUTE))
512 .forEach(node -> initializeConnTrackTable(node.intgBridge(), false));
sangho0248ca22017-05-31 13:22:47 +0900513 }
514
515 log.info("Reset security group info " + (useSecurityGroup ? " with " : " without") + " Security Group");
516 }
517
518 private void securityGroupRuleAdded(SecurityGroupRule sgRule) {
519 osNetService.ports().stream()
520 .filter(port -> port.getSecurityGroups().contains(sgRule.getSecurityGroupId()))
521 .forEach(port -> {
522 updateSecurityGroupRule(
523 instancePortService.instancePort(port.getId()),
524 port, sgRule, true);
525 log.debug("Applied security group rule {} to port {}",
526 sgRule.getId(), port.getId());
527 });
528 }
529
530 private void securityGroupRuleRemoved(SecurityGroupRule sgRule) {
531 osNetService.ports().stream()
532 .filter(port -> port.getSecurityGroups().contains(sgRule.getSecurityGroupId()))
533 .forEach(port -> {
534 updateSecurityGroupRule(
535 instancePortService.instancePort(port.getId()),
536 port, sgRule, false);
537 log.debug("Removed security group rule {} from port {}",
538 sgRule.getId(), port.getId());
539 });
540 }
541
sangho2e97be02017-07-03 18:18:27 +0900542 private int binLower(String binStr, int bits) {
543 String outBin = binStr.substring(0, 16 - bits);
544 for (int i = 0; i < bits; i++) {
545 outBin += "0";
546 }
547
548 return Integer.parseInt(outBin, 2);
549 }
550
551 private int binHigher(String binStr, int bits) {
552 String outBin = binStr.substring(0, 16 - bits);
553 for (int i = 0; i < bits; i++) {
554 outBin += "1";
555 }
556
557 return Integer.parseInt(outBin, 2);
558 }
559
560 private int testMasks(String binStr, int start, int end) {
561 int mask = 0;
562 for (; mask <= 16; mask++) {
563 int maskStart = binLower(binStr, mask);
564 int maskEnd = binHigher(binStr, mask);
565 if (maskStart < start || maskEnd > end) {
566 return mask - 1;
567 }
568 }
569
570 return mask;
571 }
572
573 private String getMask(int bits) {
574 switch (bits) {
575 case 0: return "ffff";
576 case 1: return "fffe";
577 case 2: return "fffc";
578 case 3: return "fff8";
579 case 4: return "fff0";
580 case 5: return "ffe0";
581 case 6: return "ffc0";
582 case 7: return "ff80";
583 case 8: return "ff00";
584 case 9: return "fe00";
585 case 10: return "fc00";
586 case 11: return "f800";
587 case 12: return "f000";
588 case 13: return "e000";
589 case 14: return "c000";
590 case 15: return "8000";
591 case 16: return "0000";
592 default: return null;
593 }
594 }
595
596 private Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
597
598 boolean processing = true;
599 int start = portMin;
600 Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
601 while (processing) {
602 String minStr = Integer.toBinaryString(start);
603 String binStrMinPadded = "0000000000000000".substring(minStr.length()) + minStr;
604
605 int mask = testMasks(binStrMinPadded, start, portMax);
606 int maskStart = binLower(binStrMinPadded, mask);
607 int maskEnd = binHigher(binStrMinPadded, mask);
608
609 log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
610 portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(Integer.parseInt(getMask(mask), 16)));
611
612 start = maskEnd + 1;
613 if (start > portMax) {
614 processing = false;
615 }
616 }
617
618 return portMaskMap;
619 }
620
sangho6a9ff0d2017-03-27 11:23:37 +0900621 private class InternalInstancePortListener implements InstancePortListener {
622
623 @Override
624 public boolean isRelevant(InstancePortEvent event) {
625 InstancePort instPort = event.subject();
sangho0248ca22017-05-31 13:22:47 +0900626 if (!useSecurityGroup) {
627 return false;
628 }
sangho6a9ff0d2017-03-27 11:23:37 +0900629 return mastershipService.isLocalMaster(instPort.deviceId());
630 }
631
632 @Override
633 public void event(InstancePortEvent event) {
634 InstancePort instPort = event.subject();
635 switch (event.type()) {
636 case OPENSTACK_INSTANCE_PORT_UPDATED:
637 case OPENSTACK_INSTANCE_PORT_DETECTED:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900638 log.debug("Instance port detected MAC:{} IP:{}",
639 instPort.macAddress(),
640 instPort.ipAddress());
sangho6a9ff0d2017-03-27 11:23:37 +0900641 eventExecutor.execute(() -> {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900642 setSecurityGroupRules(instPort,
643 osNetService.port(event.subject().portId()),
644 true);
sangho6a9ff0d2017-03-27 11:23:37 +0900645 });
646 break;
647 case OPENSTACK_INSTANCE_PORT_VANISHED:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900648 log.debug("Instance port vanished MAC:{} IP:{}",
649 instPort.macAddress(),
650 instPort.ipAddress());
sangho6a9ff0d2017-03-27 11:23:37 +0900651 eventExecutor.execute(() -> {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900652 setSecurityGroupRules(instPort,
653 osNetService.port(event.subject().portId()),
654 false);
sangho6a9ff0d2017-03-27 11:23:37 +0900655 });
656 break;
657 default:
658 break;
659 }
660 }
sangho6a9ff0d2017-03-27 11:23:37 +0900661 }
662
663 private class InternalOpenstackPortListener implements OpenstackNetworkListener {
664
665 @Override
666 public boolean isRelevant(OpenstackNetworkEvent event) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900667 if (event.port() == null || !Strings.isNullOrEmpty(event.port().getId())) {
sangho6a9ff0d2017-03-27 11:23:37 +0900668 return false;
669 }
Hyunsun Moonae51e732017-04-25 17:46:21 +0900670 if (event.securityGroupId() == null ||
671 securityGroupService.securityGroup(event.securityGroupId()) == null) {
672 return false;
673 }
674 if (instancePortService.instancePort(event.port().getId()) == null) {
675 return false;
676 }
sangho0248ca22017-05-31 13:22:47 +0900677 if (!useSecurityGroup) {
678 return false;
679 }
Hyunsun Moonae51e732017-04-25 17:46:21 +0900680 return true;
sangho6a9ff0d2017-03-27 11:23:37 +0900681 }
682
683 @Override
684 public void event(OpenstackNetworkEvent event) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900685 Port osPort = event.port();
686 InstancePort instPort = instancePortService.instancePort(osPort.getId());
687 SecurityGroup osSg = securityGroupService.securityGroup(event.securityGroupId());
688
sangho6a9ff0d2017-03-27 11:23:37 +0900689 switch (event.type()) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900690 case OPENSTACK_PORT_SECURITY_GROUP_ADDED:
691 eventExecutor.execute(() -> {
692 osSg.getRules().forEach(sgRule -> {
693 updateSecurityGroupRule(instPort, osPort, sgRule, true);
694 });
695 log.info("Added security group {} to port {}",
696 event.securityGroupId(), event.port().getId());
697 });
sangho6a9ff0d2017-03-27 11:23:37 +0900698 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +0900699 case OPENSTACK_PORT_SECURITY_GROUP_REMOVED:
700 eventExecutor.execute(() -> {
701 osSg.getRules().forEach(sgRule -> {
702 updateSecurityGroupRule(instPort, osPort, sgRule, false);
703 });
704 log.info("Removed security group {} from port {}",
705 event.securityGroupId(), event.port().getId());
706 });
sangho6a9ff0d2017-03-27 11:23:37 +0900707 break;
708 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900709 // do nothing for the other events
sangho6a9ff0d2017-03-27 11:23:37 +0900710 break;
711 }
712 }
sangho6a9ff0d2017-03-27 11:23:37 +0900713 }
714
715 private class InternalSecurityGroupListener implements OpenstackSecurityGroupListener {
716
717 @Override
sangho0248ca22017-05-31 13:22:47 +0900718 public boolean isRelevant(OpenstackSecurityGroupEvent event) {
719 if (!useSecurityGroup) {
720 return false;
721 }
722 return true;
723 }
724
725 @Override
sangho6a9ff0d2017-03-27 11:23:37 +0900726 public void event(OpenstackSecurityGroupEvent event) {
727 switch (event.type()) {
sangho6a9ff0d2017-03-27 11:23:37 +0900728 case OPENSTACK_SECURITY_GROUP_RULE_CREATED:
729 SecurityGroupRule securityGroupRuleToAdd = event.securityGroupRule();
730 eventExecutor.execute(() -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900731 securityGroupRuleAdded(securityGroupRuleToAdd);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900732 log.info("Applied new security group rule {} to ports",
733 securityGroupRuleToAdd.getId());
sangho6a9ff0d2017-03-27 11:23:37 +0900734 });
735 break;
736
737 case OPENSTACK_SECURITY_GROUP_RULE_REMOVED:
738 SecurityGroupRule securityGroupRuleToRemove = event.securityGroupRule();
739 eventExecutor.execute(() -> {
sangho6a9ff0d2017-03-27 11:23:37 +0900740 securityGroupRuleRemoved(securityGroupRuleToRemove);
Hyunsun Moonae51e732017-04-25 17:46:21 +0900741 log.info("Removed security group rule {} from ports",
742 securityGroupRuleToRemove.getId());
sangho6a9ff0d2017-03-27 11:23:37 +0900743 });
744 break;
Hyunsun Moonae51e732017-04-25 17:46:21 +0900745 case OPENSTACK_SECURITY_GROUP_CREATED:
746 case OPENSTACK_SECURITY_GROUP_REMOVED:
sangho6a9ff0d2017-03-27 11:23:37 +0900747 default:
Hyunsun Moonae51e732017-04-25 17:46:21 +0900748 // do nothing
749 break;
sangho6a9ff0d2017-03-27 11:23:37 +0900750 }
751 }
sangho6a9ff0d2017-03-27 11:23:37 +0900752 }
sangho1aaa7882017-05-31 13:22:47 +0900753
754 private class InternalNodeListener implements OpenstackNodeListener {
755
756 @Override
757 public boolean isRelevant(OpenstackNodeEvent event) {
758 // do not allow to proceed without leadership
759 NodeId leader = leadershipService.getLeader(appId.name());
760 if (!Objects.equals(localNodeId, leader)) {
761 return false;
762 }
763 return event.subject().type() == COMPUTE;
764 }
765
766 @Override
767 public void event(OpenstackNodeEvent event) {
768 OpenstackNode osNode = event.subject();
769
770 switch (event.type()) {
771 case OPENSTACK_NODE_COMPLETE:
772 eventExecutor.execute(() -> {
773 try {
774 if (useSecurityGroup) {
775 initializeConnTrackTable(osNode.intgBridge(), true);
776 log.warn("SG table initialization : {} is done", osNode.intgBridge());
777 }
778 } catch (IllegalArgumentException e) {
779 log.error("ACL table initialization error : {}", e.getMessage());
780 }
781 });
782 break;
783 case OPENSTACK_NODE_CREATED:
784 case OPENSTACK_NODE_REMOVED:
785 case OPENSTACK_NODE_UPDATED:
786 case OPENSTACK_NODE_INCOMPLETE:
787 default:
788 break;
789 }
790 }
791 }
sangho6a9ff0d2017-03-27 11:23:37 +0900792}