2 * Copyright (c) 2014 - 2016 Red Hat, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.netvirt.openstack.netvirt.providers.openflow13.services;
11 import com.google.common.collect.Lists;
13 import org.opendaylight.netvirt.openstack.netvirt.providers.openflow13.AbstractServiceInstance;
14 import org.opendaylight.netvirt.openstack.netvirt.api.Constants;
15 import org.opendaylight.netvirt.openstack.netvirt.api.EgressAclProvider;
16 import org.opendaylight.netvirt.openstack.netvirt.api.SecurityGroupCacheManger;
17 import org.opendaylight.netvirt.openstack.netvirt.api.SecurityServicesManager;
18 import org.opendaylight.netvirt.openstack.netvirt.providers.ConfigInterface;
19 import org.opendaylight.netvirt.openstack.netvirt.providers.openflow13.Service;
20 import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronSecurityGroup;
21 import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronSecurityRule;
22 import org.opendaylight.netvirt.openstack.netvirt.translator.Neutron_IPs;
23 import org.opendaylight.netvirt.utils.mdsal.openflow.ActionUtils;
24 import org.opendaylight.netvirt.utils.mdsal.openflow.FlowUtils;
25 import org.opendaylight.netvirt.utils.mdsal.openflow.InstructionUtils;
26 import org.opendaylight.netvirt.utils.mdsal.openflow.MatchUtils;
27 import org.opendaylight.netvirt.utils.servicehelper.ServiceHelper;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
39 import org.osgi.framework.BundleContext;
40 import org.osgi.framework.ServiceReference;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
44 import java.net.Inet4Address;
45 import java.net.Inet6Address;
46 import java.net.InetAddress;
47 import java.net.UnknownHostException;
48 import java.util.List;
51 public class EgressAclService extends AbstractServiceInstance implements EgressAclProvider, ConfigInterface {
53 private static final Logger LOG = LoggerFactory.getLogger(EgressAclService.class);
54 private volatile SecurityServicesManager securityServicesManager;
55 private volatile SecurityGroupCacheManger securityGroupCacheManger;
56 private static final int DHCP_SOURCE_PORT = 67;
57 private static final int DHCP_DESTINATION_PORT = 68;
58 private static final int DHCPV6_SOURCE_PORT = 547;
59 private static final int DHCPV6_DESTINATION_PORT = 546;
60 private static final String HOST_MASK = "/32";
61 private static final String V6_HOST_MASK = "/128";
62 private static final int PORT_RANGE_MIN = 1;
63 private static final int PORT_RANGE_MAX = 65535;
65 public EgressAclService() {
66 super(Service.EGRESS_ACL);
69 public EgressAclService(Service service) {
74 public void programPortSecurityGroup(Long dpid, String segmentationId, String attachedMac, long localPort,
75 NeutronSecurityGroup securityGroup, String portUuid, boolean write) {
77 LOG.trace("programPortSecurityGroup: neutronSecurityGroup: {} ", securityGroup);
78 if (securityGroup == null || securityGroup.getSecurityRules() == null) {
82 List<NeutronSecurityRule> portSecurityList = securityGroup.getSecurityRules();
83 /* Iterate over the Port Security Rules in the Port Security Group bound to the port*/
84 for (NeutronSecurityRule portSecurityRule : portSecurityList) {
87 * Neutron Port Security Acl "egress" and "IPv4"
88 * Check that the base conditions for flow based Port Security are true:
89 * Port Security Rule Direction ("egress") and Protocol ("IPv4")
90 * Neutron defines the direction "ingress" as the vSwitch to the VM as defined in:
91 * http://docs.openstack.org/api/openstack-network/2.0/content/security_groups.html
95 if (portSecurityRule == null
96 || portSecurityRule.getSecurityRuleEthertype() == null
97 || portSecurityRule.getSecurityRuleDirection() == null) {
101 if (NeutronSecurityRule.DIRECTION_EGRESS.equals(portSecurityRule.getSecurityRuleDirection())) {
102 LOG.debug("programPortSecurityGroup: Acl Rule matching IP and ingress is: {} ", portSecurityRule);
103 if (null != portSecurityRule.getSecurityRemoteGroupID()) {
104 //Remote Security group is selected
105 List<Neutron_IPs> remoteSrcAddressList = securityServicesManager
106 .getVmListForSecurityGroup(portUuid,portSecurityRule.getSecurityRemoteGroupID());
107 if (null != remoteSrcAddressList) {
108 for (Neutron_IPs vmIp :remoteSrcAddressList ) {
110 programPortSecurityRule(dpid, segmentationId, attachedMac,
111 localPort, portSecurityRule, vmIp, write);
114 securityGroupCacheManger.addToCache(portSecurityRule.getSecurityRemoteGroupID(), portUuid);
116 securityGroupCacheManger.removeFromCache(portSecurityRule.getSecurityRemoteGroupID(),
121 programPortSecurityRule(dpid, segmentationId, attachedMac, localPort,
122 portSecurityRule, null, write);
125 securityGroupCacheManger.portAdded(securityGroup.getSecurityGroupUUID(), portUuid);
127 securityGroupCacheManger.portRemoved(securityGroup.getSecurityGroupUUID(), portUuid);
134 public void programPortSecurityRule(Long dpid, String segmentationId, String attachedMac,
135 long localPort, NeutronSecurityRule portSecurityRule,
136 Neutron_IPs vmIp, boolean write) {
137 String securityRuleEtherType = portSecurityRule.getSecurityRuleEthertype();
138 boolean isIpv6 = NeutronSecurityRule.ETHERTYPE_IPV6.equals(securityRuleEtherType);
139 if (!isIpv6 && !NeutronSecurityRule.ETHERTYPE_IPV4.equals(securityRuleEtherType)) {
140 LOG.debug("programPortSecurityRule: SecurityRuleEthertype {} does not match IPv4/v6.",
141 securityRuleEtherType);
145 if (null == portSecurityRule.getSecurityRuleProtocol()) {
146 /* TODO Rework on the priority values */
147 egressAclIp(dpid, isIpv6, segmentationId, attachedMac,
148 write, Constants.PROTO_PORT_PREFIX_MATCH_PRIORITY);
150 String ipaddress = null;
152 ipaddress = vmIp.getIpAddress();
154 InetAddress address = InetAddress.getByName(ipaddress);
155 if ((isIpv6 && (address instanceof Inet4Address)) || (!isIpv6 && address instanceof Inet6Address)) {
156 LOG.debug("programPortSecurityRule: Remote vmIP {} does not match with "
157 + "SecurityRuleEthertype {}.", ipaddress, securityRuleEtherType);
160 } catch (UnknownHostException e) {
161 LOG.warn("Invalid IP address {}", ipaddress, e);
166 switch (portSecurityRule.getSecurityRuleProtocol()) {
168 LOG.debug("programPortSecurityRule: Rule matching TCP", portSecurityRule);
169 egressAclTcp(dpid, segmentationId, attachedMac,
170 portSecurityRule,ipaddress, write,
171 Constants.PROTO_PORT_PREFIX_MATCH_PRIORITY);
174 LOG.debug("programPortSecurityRule: Rule matching UDP", portSecurityRule);
175 egressAclUdp(dpid, segmentationId, attachedMac,
176 portSecurityRule, ipaddress, write,
177 Constants.PROTO_PORT_PREFIX_MATCH_PRIORITY);
179 case MatchUtils.ICMP:
180 case MatchUtils.ICMPV6:
181 LOG.debug("programPortSecurityRule: Rule matching ICMP", portSecurityRule);
182 egressAclIcmp(dpid, segmentationId, attachedMac,
183 portSecurityRule, ipaddress,write,
184 Constants.PROTO_PORT_PREFIX_MATCH_PRIORITY);
187 LOG.info("programPortSecurityAcl: Protocol is not TCP/UDP/ICMP but other "
188 + "protocol = ", portSecurityRule.getSecurityRuleProtocol());
189 egressOtherProtocolAclHandler(dpid, segmentationId, attachedMac,
190 portSecurityRule, ipaddress, write,
191 Constants.PROTO_PORT_PREFIX_MATCH_PRIORITY);
197 private void egressOtherProtocolAclHandler(Long dpidLong, String segmentationId, String srcMac,
198 NeutronSecurityRule portSecurityRule, String dstAddress,
199 boolean write, Integer priority) {
200 MatchBuilder matchBuilder = new MatchBuilder();
201 String flowId = "Egress_Other_" + segmentationId + "_" + srcMac + "_";
202 matchBuilder = MatchUtils.createV4EtherMatchWithType(matchBuilder,srcMac,null,MatchUtils.ETHERTYPE_IPV4);
206 Integer protocol = new Integer(portSecurityRule.getSecurityRuleProtocol());
207 proto = protocol.shortValue();
208 flowId = flowId + proto;
209 } catch (NumberFormatException e) {
210 LOG.error("Protocol vlaue conversion failure", e);
212 matchBuilder = MatchUtils.createIpProtocolMatch(matchBuilder, proto);
214 if (null != dstAddress) {
215 flowId = flowId + dstAddress;
216 matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder, null,
217 MatchUtils.iPv4PrefixFromIPv4Address(dstAddress));
219 } else if (null != portSecurityRule.getSecurityRuleRemoteIpPrefix()) {
220 flowId = flowId + portSecurityRule.getSecurityRuleRemoteIpPrefix();
221 matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder, null,
222 new Ipv4Prefix(portSecurityRule.getSecurityRuleRemoteIpPrefix()));
224 flowId = flowId + "_Permit";
225 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
226 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowId, priority, matchBuilder, getTable());
227 addInstructionWithConntrackCommit(flowBuilder, false);
228 syncFlow(flowBuilder ,nodeBuilder, write);
232 public void programFixedSecurityGroup(Long dpid, String segmentationId, String attachedMac,
233 long localPort, List<Neutron_IPs> srcAddressList,
234 boolean isLastPortinBridge, boolean isComputePort ,boolean write) {
235 // If it is the only port in the bridge add the rule to allow any DHCP client traffic
236 //if (isLastPortinBridge) {
237 egressAclDhcpAllowClientTrafficFromVm(dpid, write, Constants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY);
238 egressAclDhcpv6AllowClientTrafficFromVm(dpid, write, Constants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY);
241 programArpRule(dpid, segmentationId, localPort, attachedMac, write);
242 if (securityServicesManager.isConntrackEnabled()) {
243 programEgressAclFixedConntrackRule(dpid, segmentationId, localPort, attachedMac, write);
245 // add rule to drop the DHCP server traffic originating from the vm.
246 egressAclDhcpDropServerTrafficfromVm(dpid, localPort, write,
247 Constants.PROTO_DHCP_CLIENT_SPOOF_MATCH_PRIORITY_DROP);
248 egressAclDhcpv6DropServerTrafficfromVm(dpid, localPort, write,
249 Constants.PROTO_DHCP_CLIENT_SPOOF_MATCH_PRIORITY_DROP);
250 //Adds rule to check legitimate ip/mac pair for each packet from the vm
251 for (Neutron_IPs srcAddress : srcAddressList) {
253 InetAddress address = InetAddress.getByName(srcAddress.getIpAddress());
254 if (address instanceof Inet4Address) {
255 String addressWithPrefix = srcAddress.getIpAddress() + HOST_MASK;
256 egressAclAllowTrafficFromVmIpMacPair(dpid, localPort, attachedMac, addressWithPrefix,
257 Constants.PROTO_VM_IP_MAC_MATCH_PRIORITY,write);
258 } else if (address instanceof Inet6Address) {
259 String addressWithPrefix = srcAddress.getIpAddress() + V6_HOST_MASK;
260 egressAclAllowTrafficFromVmIpV6MacPair(dpid, localPort, attachedMac, addressWithPrefix,
261 Constants.PROTO_VM_IP_MAC_MATCH_PRIORITY,write);
263 } catch (UnknownHostException e) {
264 LOG.warn("Invalid IP address {}", srcAddress.getIpAddress(), e);
270 private void programArpRule(Long dpid, String segmentationId, long localPort, String attachedMac, boolean write) {
271 MatchBuilder matchBuilder = new MatchBuilder();
272 String flowId = "Egress_ARP_" + segmentationId + "_" + localPort + "_";
273 MatchUtils.createV4EtherMatchWithType(matchBuilder,null,null,MatchUtils.ETHERTYPE_ARP);
274 MatchUtils.addArpMacMatch(matchBuilder, attachedMac, null);
275 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowId, Constants.PROTO_MATCH_PRIORITY,
276 matchBuilder, getTable());
277 addPipelineInstruction(flowBuilder, null, false);
278 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpid);
279 syncFlow(flowBuilder ,nodeBuilder, write);
282 private void programEgressAclFixedConntrackRule(Long dpid,
283 String segmentationId, long localPort, String attachMac, boolean write) {
285 programConntrackUntrackRule(dpid, segmentationId, localPort,attachMac,
286 Constants.CT_STATE_UNTRACKED_PRIORITY, write );
287 programConntrackTrackedPlusEstRule(dpid, segmentationId, localPort,
288 Constants.CT_STATE_TRACKED_EXIST_PRIORITY, write );
289 programConntrackTrackedPlusRelRule(dpid, segmentationId, localPort,
290 Constants.CT_STATE_TRACKED_EXIST_PRIORITY, write );
291 programConntrackNewDropRule(dpid, segmentationId, localPort,
292 Constants.CT_STATE_NEW_PRIORITY_DROP, write );
293 programConntrackInvDropRule(dpid, segmentationId, localPort,
294 Constants.CT_STATE_NEW_PRIORITY_DROP, write );
295 LOG.info("programEgressAclFixedConntrackRule : default connection tracking rule are added.");
296 } catch (Exception e) {
297 LOG.error("Failed to add default conntrack rules : " , e);
301 private void programConntrackUntrackRule(Long dpidLong, String segmentationId,
302 long localPort, String attachMac, Integer priority, boolean write) {
303 MatchBuilder matchBuilder = new MatchBuilder();
304 String flowName = "Egress_Fixed_Conntrk_Untrk_" + segmentationId + "_" + localPort + "_";
305 matchBuilder = MatchUtils.createV4EtherMatchWithType(matchBuilder, attachMac, null,MatchUtils.ETHERTYPE_IPV4);
306 matchBuilder = MatchUtils.addCtState(matchBuilder,MatchUtils.UNTRACKED_CT_STATE,
307 MatchUtils.UNTRACKED_CT_STATE_MASK);
308 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowName, priority, matchBuilder, getTable());
309 addInstructionWithConntrackRecirc(flowBuilder);
310 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
311 syncFlow(flowBuilder ,nodeBuilder, write);
314 private void programConntrackTrackedPlusEstRule(Long dpidLong, String segmentationId,
315 long localPort,Integer priority, boolean write) {
316 MatchBuilder matchBuilder = new MatchBuilder();
317 String flowName = "Egress_Fixed_Conntrk_TrkEst_" + segmentationId + "_" + localPort + "_";
318 matchBuilder = MatchUtils.createInPortMatch(matchBuilder, dpidLong, localPort);
319 matchBuilder = MatchUtils.addCtState(matchBuilder,MatchUtils.TRACKED_EST_CT_STATE,
320 MatchUtils.TRACKED_CT_STATE_MASK);
321 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowName, priority, matchBuilder, getTable());
322 addPipelineInstruction(flowBuilder, null, false);
323 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
324 syncFlow(flowBuilder ,nodeBuilder, write);
327 private void programConntrackTrackedPlusRelRule(Long dpidLong, String segmentationId,
328 long localPort,Integer priority, boolean write) {
329 MatchBuilder matchBuilder = new MatchBuilder();
330 String flowName = "Egress_Fixed_Conntrk_TrkRel_" + segmentationId + "_" + localPort + "_";
331 matchBuilder = MatchUtils.createInPortMatch(matchBuilder, dpidLong, localPort);
332 matchBuilder = MatchUtils.addCtState(matchBuilder,MatchUtils.TRACKED_REL_CT_STATE,
333 MatchUtils.TRACKED_CT_STATE_MASK);
334 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowName, priority, matchBuilder, getTable());
335 addPipelineInstruction(flowBuilder, null, false);
336 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
337 syncFlow(flowBuilder ,nodeBuilder, write);
340 private void programConntrackNewDropRule(Long dpidLong, String segmentationId,
341 long localPort, Integer priority, boolean write) {
342 MatchBuilder matchBuilder = new MatchBuilder();
344 String flowName = "Egress_Fixed_Conntrk_NewDrop_" + segmentationId + "_" + localPort + "_";
345 matchBuilder = MatchUtils.createInPortMatch(matchBuilder, dpidLong, localPort);
346 matchBuilder = MatchUtils.addCtState(matchBuilder,MatchUtils.TRACKED_NEW_CT_STATE,
347 MatchUtils.TRACKED_NEW_CT_STATE_MASK);
348 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowName, priority, matchBuilder, getTable());
349 addPipelineInstruction(flowBuilder, null, true);
350 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
351 syncFlow(flowBuilder ,nodeBuilder, write);
354 private void programConntrackInvDropRule(Long dpidLong, String segmentationId,
355 long localPort, Integer priority, boolean write) {
356 MatchBuilder matchBuilder = new MatchBuilder();
357 String flowName = "Egress_Fixed_Conntrk_InvDrop_" + segmentationId + "_" + localPort + "_";
358 matchBuilder = MatchUtils.createInPortMatch(matchBuilder, dpidLong, localPort);
359 matchBuilder = MatchUtils.addCtState(matchBuilder,MatchUtils.TRACKED_INV_CT_STATE,
360 MatchUtils.TRACKED_INV_CT_STATE_MASK);
361 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowName, priority, matchBuilder, getTable());
362 addPipelineInstruction(flowBuilder, null, true);
363 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
364 syncFlow(flowBuilder ,nodeBuilder, write);
368 * Allows IPv4/v6 packet egress from the src mac address.
369 * @param dpidLong the dpid
370 * @param isIpv6 whether the rule is for ipv6
371 * @param segmentationId the segementation id
372 * @param srcMac the src mac address
373 * @param write add or remove
374 * @param protoPortMatchPriority the protocol match priority.
376 private void egressAclIp(Long dpidLong, boolean isIpv6, String segmentationId, String srcMac,
377 boolean write, Integer protoPortMatchPriority ) {
378 MatchBuilder matchBuilder = new MatchBuilder();
379 String flowId = "Egress_IP" + segmentationId + "_" + srcMac + "_Permit_";
381 matchBuilder = MatchUtils.createV6EtherMatchWithType(matchBuilder,srcMac,null);
383 matchBuilder = MatchUtils.createV4EtherMatchWithType(matchBuilder,srcMac,null,MatchUtils.ETHERTYPE_IPV4);
385 addConntrackMatch(matchBuilder, MatchUtils.TRACKED_NEW_CT_STATE,MatchUtils.TRACKED_NEW_CT_STATE_MASK);
386 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowId, protoPortMatchPriority, matchBuilder, getTable());
387 addInstructionWithConntrackCommit(flowBuilder, false);
388 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
389 syncFlow(flowBuilder ,nodeBuilder, write);
393 * Creates a egress match with src macaddress. If dest address is specified
394 * destination specific match will be created. Otherwise a match with a
395 * CIDR will be created.
396 * @param dpidLong the dpid
397 * @param segmentationId the segmentation id
398 * @param srcMac the source mac address.
399 * @param portSecurityRule the security rule in the SG
400 * @param dstAddress the destination IP address
401 * @param write add or delete
402 * @param protoPortMatchPriority the protocol match priroty
404 private void egressAclTcp(Long dpidLong, String segmentationId, String srcMac,
405 NeutronSecurityRule portSecurityRule, String dstAddress,
406 boolean write, Integer protoPortMatchPriority) {
407 boolean portRange = false;
408 MatchBuilder matchBuilder = new MatchBuilder();
409 String flowId = "Egress_TCP_" + segmentationId + "_" + srcMac + "_";
410 boolean isIpv6 = NeutronSecurityRule.ETHERTYPE_IPV6.equals(portSecurityRule.getSecurityRuleEthertype());
412 matchBuilder = MatchUtils.createV6EtherMatchWithType(matchBuilder,srcMac,null);
414 matchBuilder = MatchUtils.createV4EtherMatchWithType(matchBuilder,srcMac,null,MatchUtils.ETHERTYPE_IPV4);
417 /* Custom TCP Match */
418 if (portSecurityRule.getSecurityRulePortMin().equals(portSecurityRule.getSecurityRulePortMax())) {
419 flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_";
420 matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.TCP_SHORT, 0,
421 portSecurityRule.getSecurityRulePortMin());
424 if (portSecurityRule.getSecurityRulePortMin().equals(PORT_RANGE_MIN)
425 && portSecurityRule.getSecurityRulePortMax().equals(PORT_RANGE_MAX)) {
426 flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_"
427 + portSecurityRule.getSecurityRulePortMax() + "_";
428 matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.TCP_SHORT, 0, 0);
433 if (null != dstAddress) {
434 flowId = flowId + dstAddress;
436 matchBuilder = MatchUtils.addRemoteIpv6Prefix(matchBuilder,null,
437 MatchUtils.iPv6PrefixFromIPv6Address(dstAddress));
439 matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder,null,
440 MatchUtils.iPv4PrefixFromIPv4Address(dstAddress));
442 } else if (null != portSecurityRule.getSecurityRuleRemoteIpPrefix()) {
443 flowId = flowId + portSecurityRule.getSecurityRuleRemoteIpPrefix();
445 matchBuilder = MatchUtils.addRemoteIpv6Prefix(matchBuilder,null,
446 new Ipv6Prefix(portSecurityRule.getSecurityRuleRemoteIpPrefix()));
448 matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder,null,
449 new Ipv4Prefix(portSecurityRule.getSecurityRuleRemoteIpPrefix()));
452 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
454 Map<Integer, Integer> portMaskMap = MatchUtils
455 .getLayer4MaskForRange(portSecurityRule.getSecurityRulePortMin(),
456 portSecurityRule.getSecurityRulePortMax());
457 for (Integer port: portMaskMap.keySet()) {
458 String rangeflowId = flowId + port + "_" + portMaskMap.get(port) + "_";
459 rangeflowId = rangeflowId + "_Permit";
460 MatchUtils.addLayer4MatchWithMask(matchBuilder, MatchUtils.TCP_SHORT,
461 0, port, portMaskMap.get(port));
462 addConntrackMatch(matchBuilder, MatchUtils.TRACKED_NEW_CT_STATE,MatchUtils.TRACKED_NEW_CT_STATE_MASK);
463 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(rangeflowId, protoPortMatchPriority,
464 matchBuilder, getTable());
465 addInstructionWithConntrackCommit(flowBuilder, false);
466 syncFlow(flowBuilder ,nodeBuilder, write);
469 flowId = flowId + "_Permit";
470 addConntrackMatch(matchBuilder, MatchUtils.TRACKED_NEW_CT_STATE,MatchUtils.TRACKED_NEW_CT_STATE_MASK);
471 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowId, protoPortMatchPriority,
472 matchBuilder, getTable());
473 addInstructionWithConntrackCommit(flowBuilder, false);
474 syncFlow(flowBuilder ,nodeBuilder, write);
478 private void egressAclIcmp(Long dpidLong, String segmentationId, String srcMac,
479 NeutronSecurityRule portSecurityRule, String dstAddress,
480 boolean write, Integer protoPortMatchPriority) {
482 boolean isIpv6 = NeutronSecurityRule.ETHERTYPE_IPV6.equals(portSecurityRule.getSecurityRuleEthertype());
484 egressAclIcmpV6(dpidLong, segmentationId, srcMac, portSecurityRule, dstAddress, write,
485 protoPortMatchPriority);
487 egressAclIcmpV4(dpidLong, segmentationId, srcMac, portSecurityRule, dstAddress, write,
488 protoPortMatchPriority);
493 * Creates a icmp egress match with src macaddress. If dest address is specified
494 * destination specific match will be created. Otherwise a match with a
495 * CIDR will be created.
496 * @param dpidLong the dpid
497 * @param segmentationId the segmentation id
498 * @param srcMac the source mac address.
499 * @param portSecurityRule the security rule in the SG
500 * @param dstAddress the source IP address
501 * @param write add or delete
502 * @param protoPortMatchPriority the protocol match priority
504 private void egressAclIcmpV4(Long dpidLong, String segmentationId, String srcMac,
505 NeutronSecurityRule portSecurityRule, String dstAddress,
506 boolean write, Integer protoPortMatchPriority) {
508 MatchBuilder matchBuilder = new MatchBuilder();
509 String flowId = "Egress_ICMP_" + segmentationId + "_" + srcMac + "_";
510 matchBuilder = MatchUtils.createV4EtherMatchWithType(matchBuilder,srcMac,null,MatchUtils.ETHERTYPE_IPV4);
511 /*Custom ICMP Match */
512 if (portSecurityRule.getSecurityRulePortMin() != null
513 && portSecurityRule.getSecurityRulePortMax() != null) {
514 flowId = flowId + portSecurityRule.getSecurityRulePortMin().shortValue() + "_"
515 + portSecurityRule.getSecurityRulePortMax().shortValue() + "_";
516 matchBuilder = MatchUtils.createICMPv4Match(matchBuilder,
517 portSecurityRule.getSecurityRulePortMin().shortValue(),
518 portSecurityRule.getSecurityRulePortMax().shortValue());
520 /* All ICMP Match */ // We are getting from neutron NULL for both min and max
521 flowId = flowId + "all" + "_" ;
522 matchBuilder = MatchUtils.createICMPv4Match(matchBuilder, MatchUtils.ALL_ICMP, MatchUtils.ALL_ICMP);
524 if (null != dstAddress) {
525 flowId = flowId + dstAddress;
526 matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder,null,
527 MatchUtils.iPv4PrefixFromIPv4Address(dstAddress));
528 } else if (null != portSecurityRule.getSecurityRuleRemoteIpPrefix()) {
529 flowId = flowId + portSecurityRule.getSecurityRuleRemoteIpPrefix();
530 if (!portSecurityRule.getSecurityRuleRemoteIpPrefix().contains("/0")) {
531 matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder,null,
532 new Ipv4Prefix(portSecurityRule.getSecurityRuleRemoteIpPrefix()));
535 flowId = flowId + "_Permit";
536 addConntrackMatch(matchBuilder, MatchUtils.TRACKED_NEW_CT_STATE,MatchUtils.TRACKED_NEW_CT_STATE_MASK);
537 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
538 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowId, protoPortMatchPriority, matchBuilder, getTable());
539 addInstructionWithConntrackCommit(flowBuilder, false);
540 syncFlow(flowBuilder ,nodeBuilder, write);
544 * Creates a icmpv6 egress match with src macaddress. If dest address is specified
545 * destination specific match will be created. Otherwise a match with a
546 * CIDR will be created.
547 * @param dpidLong the dpid
548 * @param segmentationId the segmentation id
549 * @param srcMac the source mac address.
550 * @param portSecurityRule the security rule in the SG
551 * @param dstAddress the source IP address
552 * @param write add or delete
553 * @param protoPortMatchPriority the protocol match priority
555 private void egressAclIcmpV6(Long dpidLong, String segmentationId, String srcMac,
556 NeutronSecurityRule portSecurityRule, String dstAddress,
557 boolean write, Integer protoPortMatchPriority) {
559 MatchBuilder matchBuilder = new MatchBuilder();
560 String flowId = "Egress_ICMP_" + segmentationId + "_" + srcMac + "_";
561 matchBuilder = MatchUtils.createV6EtherMatchWithType(matchBuilder,srcMac,null);
563 /*Custom ICMP Match */
564 if (portSecurityRule.getSecurityRulePortMin() != null
565 && portSecurityRule.getSecurityRulePortMax() != null) {
566 flowId = flowId + portSecurityRule.getSecurityRulePortMin().shortValue() + "_"
567 + portSecurityRule.getSecurityRulePortMax().shortValue() + "_";
568 matchBuilder = MatchUtils.createICMPv6Match(matchBuilder,
569 portSecurityRule.getSecurityRulePortMin().shortValue(),
570 portSecurityRule.getSecurityRulePortMax().shortValue());
572 /* All ICMP Match */ // We are getting from neutron NULL for both min and max
573 flowId = flowId + "all" + "_" ;
574 matchBuilder = MatchUtils.createICMPv6Match(matchBuilder, MatchUtils.ALL_ICMP, MatchUtils.ALL_ICMP);
576 if (null != dstAddress) {
577 flowId = flowId + dstAddress;
578 matchBuilder = MatchUtils.addRemoteIpv6Prefix(matchBuilder,null,
579 MatchUtils.iPv6PrefixFromIPv6Address(dstAddress));
580 } else if (null != portSecurityRule.getSecurityRuleRemoteIpPrefix()) {
581 flowId = flowId + portSecurityRule.getSecurityRuleRemoteIpPrefix();
582 matchBuilder = MatchUtils.addRemoteIpv6Prefix(matchBuilder,null,
583 new Ipv6Prefix(portSecurityRule.getSecurityRuleRemoteIpPrefix()));
585 flowId = flowId + "_Permit";
586 addConntrackMatch(matchBuilder, MatchUtils.TRACKED_NEW_CT_STATE,MatchUtils.TRACKED_NEW_CT_STATE_MASK);
587 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
588 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowId, protoPortMatchPriority, matchBuilder, getTable());
589 addInstructionWithConntrackCommit(flowBuilder, false);
590 syncFlow(flowBuilder ,nodeBuilder, write);
594 * Creates a egress match with src macaddress. If dest address is specified
595 * destination specific match will be created. Otherwise a match with a
596 * CIDR will be created.
597 * @param dpidLong the dpid
598 * @param segmentationId the segmentation id
599 * @param srcMac the source mac address.
600 * @param portSecurityRule the security rule in the SG
601 * @param dstAddress the source IP address
602 * @param write add or delete
603 * @param protoPortMatchPriority the protocol match priroty
605 private void egressAclUdp(Long dpidLong, String segmentationId, String srcMac,
606 NeutronSecurityRule portSecurityRule, String dstAddress,
607 boolean write, Integer protoPortMatchPriority) {
608 boolean portRange = false;
609 MatchBuilder matchBuilder = new MatchBuilder();
610 String flowId = "Egress_UDP_" + segmentationId + "_" + srcMac + "_";
611 boolean isIpv6 = NeutronSecurityRule.ETHERTYPE_IPV6.equals(portSecurityRule.getSecurityRuleEthertype());
613 matchBuilder = MatchUtils.createV6EtherMatchWithType(matchBuilder,srcMac,null);
615 matchBuilder = MatchUtils.createV4EtherMatchWithType(matchBuilder,srcMac,null,MatchUtils.ETHERTYPE_IPV4);
618 /* Custom UDP Match */
619 if (portSecurityRule.getSecurityRulePortMin().equals(portSecurityRule.getSecurityRulePortMax())) {
620 flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_";
621 matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.UDP_SHORT, 0,
622 portSecurityRule.getSecurityRulePortMin());
625 if (portSecurityRule.getSecurityRulePortMin().equals(PORT_RANGE_MIN)
626 && portSecurityRule.getSecurityRulePortMax().equals(PORT_RANGE_MAX)) {
627 flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_"
628 + portSecurityRule.getSecurityRulePortMax() + "_";
629 matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.UDP_SHORT, 0, 0);
634 if (null != dstAddress) {
635 flowId = flowId + dstAddress;
637 matchBuilder = MatchUtils.addRemoteIpv6Prefix(matchBuilder,null,
638 MatchUtils.iPv6PrefixFromIPv6Address(dstAddress));
640 matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder,null,
641 MatchUtils.iPv4PrefixFromIPv4Address(dstAddress));
643 } else if (null != portSecurityRule.getSecurityRuleRemoteIpPrefix()) {
644 flowId = flowId + portSecurityRule.getSecurityRuleRemoteIpPrefix();
646 matchBuilder = MatchUtils.addRemoteIpv6Prefix(matchBuilder, null,
647 new Ipv6Prefix(portSecurityRule
648 .getSecurityRuleRemoteIpPrefix()));
650 matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder, null,
651 new Ipv4Prefix(portSecurityRule
652 .getSecurityRuleRemoteIpPrefix()));
655 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
657 Map<Integer, Integer> portMaskMap = MatchUtils
658 .getLayer4MaskForRange(portSecurityRule.getSecurityRulePortMin(),
659 portSecurityRule.getSecurityRulePortMax());
660 for (Integer port: portMaskMap.keySet()) {
661 String rangeflowId = flowId + port + "_" + portMaskMap.get(port) + "_";
662 rangeflowId = rangeflowId + "_Permit";
663 MatchUtils.addLayer4MatchWithMask(matchBuilder, MatchUtils.UDP_SHORT,
664 0, port, portMaskMap.get(port));
665 addConntrackMatch(matchBuilder, MatchUtils.TRACKED_NEW_CT_STATE,MatchUtils.TRACKED_NEW_CT_STATE_MASK);
666 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(rangeflowId, protoPortMatchPriority,
667 matchBuilder, getTable());
668 addInstructionWithConntrackCommit(flowBuilder, false);
669 syncFlow(flowBuilder ,nodeBuilder, write);
672 flowId = flowId + "_Permit";
673 addConntrackMatch(matchBuilder, MatchUtils.TRACKED_NEW_CT_STATE,MatchUtils.TRACKED_NEW_CT_STATE_MASK);
674 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowId, protoPortMatchPriority,
675 matchBuilder, getTable());
676 addInstructionWithConntrackCommit(flowBuilder, false);
677 syncFlow(flowBuilder ,nodeBuilder, write);
682 * Adds flow to allow any DHCP client traffic.
684 * @param dpidLong the dpid
685 * @param write whether to write or delete the flow
686 * @param priority the priority
688 private void egressAclDhcpAllowClientTrafficFromVm(Long dpidLong,
689 boolean write, Integer priority) {
690 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
691 String flowName = "Egress_DHCP_Client" + "_Permit_";
692 MatchBuilder matchBuilder = new MatchBuilder();
693 MatchUtils.createDhcpMatch(matchBuilder, DHCP_DESTINATION_PORT, DHCP_SOURCE_PORT);
694 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowName, priority, matchBuilder, getTable());
695 addPipelineInstruction(flowBuilder, null, false);
696 syncFlow(flowBuilder ,nodeBuilder, write);
700 * Adds flow to allow any DHCP IPv6 client traffic.
702 * @param dpidLong the dpid
703 * @param write whether to write or delete the flow
704 * @param priority the priority
706 private void egressAclDhcpv6AllowClientTrafficFromVm(Long dpidLong,
707 boolean write, Integer priority) {
708 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
709 String flowName = "Egress_DHCPv6_Client" + "_Permit_";
710 MatchBuilder matchBuilder = new MatchBuilder();
711 MatchUtils.createDhcpv6Match(matchBuilder, DHCPV6_DESTINATION_PORT, DHCPV6_SOURCE_PORT);
712 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowName, priority, matchBuilder, getTable());
713 addPipelineInstruction(flowBuilder, null, false);
714 syncFlow(flowBuilder ,nodeBuilder, write);
718 * Adds rule to prevent DHCP spoofing by the vm attached to the port.
720 * @param dpidLong the dpid
721 * @param localPort the local port
722 * @param write is write or delete
723 * @param priority the priority
725 private void egressAclDhcpDropServerTrafficfromVm(Long dpidLong, long localPort,
726 boolean write, Integer priority) {
727 String flowName = "Egress_DHCP_Server" + "_" + localPort + "_DROP_";
728 MatchBuilder matchBuilder = new MatchBuilder();
729 MatchUtils.createInPortMatch(matchBuilder, dpidLong, localPort);
730 MatchUtils.createDhcpMatch(matchBuilder, DHCP_SOURCE_PORT, DHCP_DESTINATION_PORT);
731 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowName, priority, matchBuilder, getTable());
732 addPipelineInstruction(flowBuilder, null, true);
733 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
734 syncFlow(flowBuilder ,nodeBuilder, write);
738 * Adds rule to prevent DHCPv6 spoofing by the vm attached to the port.
740 * @param dpidLong the dpid
741 * @param localPort the local port
742 * @param write is write or delete
743 * @param priority the priority
745 private void egressAclDhcpv6DropServerTrafficfromVm(Long dpidLong, long localPort,
746 boolean write, Integer priority) {
748 String flowName = "Egress_DHCPv6_Server" + "_" + localPort + "_DROP_";
749 MatchBuilder matchBuilder = new MatchBuilder();
750 MatchUtils.createInPortMatch(matchBuilder, dpidLong, localPort);
751 MatchUtils.createDhcpv6Match(matchBuilder, DHCPV6_SOURCE_PORT, DHCPV6_DESTINATION_PORT);
752 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowName, priority, matchBuilder, getTable());
753 addPipelineInstruction(flowBuilder, null, true);
754 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
755 syncFlow(flowBuilder ,nodeBuilder, write);
759 * Adds rule to check legitimate ip/mac pair for each packet from the vm.
761 * @param dpidLong the dpid
762 * @param localPort the local port
763 * @param srcIp the vm ip address
764 * @param attachedMac the vm mac address
765 * @param priority the priority
766 * @param write is write or delete
768 private void egressAclAllowTrafficFromVmIpMacPair(Long dpidLong, long localPort,
769 String attachedMac, String srcIp,
770 Integer priority, boolean write) {
771 MatchBuilder matchBuilder = new MatchBuilder();
772 MatchUtils.createSrcL3Ipv4MatchWithMac(matchBuilder, new Ipv4Prefix(srcIp),new MacAddress(attachedMac));
773 MatchUtils.createInPortMatch(matchBuilder, dpidLong, localPort);
774 LOG.debug("egressAclAllowTrafficFromVmIpMacPair: MatchBuilder contains: {}", matchBuilder);
775 String flowName = "Egress_Allow_VM_IP_MAC" + "_" + localPort + attachedMac + "_Permit_";
776 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowName, priority, matchBuilder, getTable());
777 addPipelineInstruction(flowBuilder, null, false);
778 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
779 syncFlow(flowBuilder ,nodeBuilder, write);
783 * Adds rule to check legitimate ip/mac pair for each packet from the vm.
785 * @param dpidLong the dpid
786 * @param localPort the local port
787 * @param srcIp the vm ip address
788 * @param attachedMac the vm mac address
789 * @param priority the priority
790 * @param write is write or delete
792 private void egressAclAllowTrafficFromVmIpV6MacPair(Long dpidLong, long localPort,
793 String attachedMac, String srcIp,
794 Integer priority, boolean write) {
795 MatchBuilder matchBuilder = new MatchBuilder();
796 MatchUtils.createSrcL3Ipv6MatchWithMac(matchBuilder, new Ipv6Prefix(srcIp),new MacAddress(attachedMac));
797 MatchUtils.createInPortMatch(matchBuilder, dpidLong, localPort);
798 LOG.debug("egressAclAllowTrafficFromVmIpMacPair: MatchBuilder contains: {}", matchBuilder);
799 String flowName = "Egress_Allow_VM_IPv6_MAC" + "_" + localPort + attachedMac + "_Permit_";
800 FlowBuilder flowBuilder = FlowUtils.createFlowBuilder(flowName, priority, matchBuilder, getTable());
801 addPipelineInstruction(flowBuilder, null, false);
802 NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
803 syncFlow(flowBuilder ,nodeBuilder, write);
806 private void addConntrackMatch(MatchBuilder matchBuilder, int state, int mask) {
807 if (securityServicesManager.isConntrackEnabled()) {
808 MatchUtils.addCtState(matchBuilder, state, mask );
813 private FlowBuilder addInstructionWithConntrackCommit( FlowBuilder flowBuilder , boolean isDrop) {
814 InstructionBuilder instructionBuilder = null;
815 if (securityServicesManager.isConntrackEnabled()) {
816 Action conntrackAction = ActionUtils.nxConntrackAction(1, 0L, 0, (short)0xff);
817 instructionBuilder = InstructionUtils
818 .createInstructionBuilder(ActionUtils.conntrackActionBuilder(conntrackAction), 1, false);
820 return addPipelineInstruction(flowBuilder,instructionBuilder, isDrop);
823 private FlowBuilder addInstructionWithConntrackRecirc( FlowBuilder flowBuilder) {
824 InstructionBuilder instructionBuilder = null;
825 if (securityServicesManager.isConntrackEnabled()) {
826 Action conntrackAction = ActionUtils.nxConntrackAction(0, 0L, 0, (short)0x0);
828 instructionBuilder = InstructionUtils
829 .createInstructionBuilder(ActionUtils.conntrackActionBuilder(conntrackAction), 1, false);
830 List<Instruction> instructionsList = Lists.newArrayList();
831 instructionsList.add(instructionBuilder.build());
832 InstructionsBuilder isb = new InstructionsBuilder();
833 isb.setInstruction(instructionsList);
834 flowBuilder.setInstructions(isb.build());
839 private FlowBuilder addPipelineInstruction( FlowBuilder flowBuilder ,
840 InstructionBuilder instructionBuilder,boolean isDrop) {
841 InstructionBuilder pipeLineIndstructionBuilder = createPipleLineInstructionBuilder(isDrop);
842 List<Instruction> instructionsList = Lists.newArrayList();
843 instructionsList.add(pipeLineIndstructionBuilder.build());
844 if (null != instructionBuilder) {
845 instructionsList.add(instructionBuilder.build());
847 InstructionsBuilder isb = new InstructionsBuilder();
848 isb.setInstruction(instructionsList);
849 flowBuilder.setInstructions(isb.build());
853 private InstructionBuilder createPipleLineInstructionBuilder(boolean drop) {
854 InstructionBuilder ib = this.getMutablePipelineInstructionBuilder();
856 InstructionUtils.createDropInstructions(ib);
859 List<Instruction> instructionsList = Lists.newArrayList();
860 ib.setKey(new InstructionKey(0));
861 instructionsList.add(ib.build());
865 * Add or remove flow to the node.
866 * @param flowBuilder the flow builder
867 * @param nodeBuilder the node builder
868 * @param write whether it is a write
870 private void syncFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder,
873 writeFlow(flowBuilder, nodeBuilder);
875 removeFlow(flowBuilder, nodeBuilder);
881 public void setDependencies(BundleContext bundleContext, ServiceReference serviceReference) {
882 super.setDependencies(bundleContext.getServiceReference(EgressAclProvider.class.getName()), this);
883 securityServicesManager =
884 (SecurityServicesManager) ServiceHelper.getGlobalInstance(SecurityServicesManager.class, this);
885 securityGroupCacheManger =
886 (SecurityGroupCacheManger) ServiceHelper.getGlobalInstance(SecurityGroupCacheManger.class, this);
890 public void setDependencies(Object impl) {}