a3f1d0f5c0f6ea45a5970fbb2400873120ddc4fe
[netvirt.git] / openstack / net-virt-providers / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / providers / openflow13 / services / EgressAclService.java
1 /*
2  * Copyright (c) 2014, 2015 Red Hat, Inc. and others. All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services;
10
11 import com.google.common.collect.Lists;
12
13 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
14 import org.opendaylight.ovsdb.openstack.netvirt.api.EgressAclProvider;
15 import org.opendaylight.ovsdb.openstack.netvirt.api.SecurityGroupCacheManger;
16 import org.opendaylight.ovsdb.openstack.netvirt.api.SecurityServicesManager;
17 import org.opendaylight.ovsdb.openstack.netvirt.providers.ConfigInterface;
18 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.AbstractServiceInstance;
19 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service;
20 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityGroup;
21 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityRule;
22 import org.opendaylight.ovsdb.openstack.netvirt.translator.Neutron_IPs;
23 import org.opendaylight.ovsdb.utils.mdsal.openflow.ActionUtils;
24 import org.opendaylight.ovsdb.utils.mdsal.openflow.FlowUtils;
25 import org.opendaylight.ovsdb.utils.mdsal.openflow.InstructionUtils;
26 import org.opendaylight.ovsdb.utils.mdsal.openflow.MatchUtils;
27 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefixBuilder;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.arp.match.fields.ArpSourceHardwareAddressBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetTypeBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
52 import org.osgi.framework.BundleContext;
53 import org.osgi.framework.ServiceReference;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 import java.math.BigInteger;
58 import java.net.Inet4Address;
59 import java.net.Inet6Address;
60 import java.net.InetAddress;
61 import java.net.UnknownHostException;
62 import java.util.List;
63 import java.util.Map;
64
65 public class EgressAclService extends AbstractServiceInstance implements EgressAclProvider, ConfigInterface {
66
67     private static final Logger LOG = LoggerFactory.getLogger(EgressAclService.class);
68     private volatile SecurityServicesManager securityServicesManager;
69     private volatile SecurityGroupCacheManger securityGroupCacheManger;
70     private static final int DHCP_SOURCE_PORT = 67;
71     private static final int DHCP_DESTINATION_PORT = 68;
72     private static final String HOST_MASK = "/32";
73     private static final int PORT_RANGE_MIN = 1;
74     private static final int PORT_RANGE_MAX = 65535;
75
76     public EgressAclService() {
77         super(Service.EGRESS_ACL);
78     }
79
80     public EgressAclService(Service service) {
81         super(service);
82     }
83
84     @Override
85     public void programPortSecurityGroup(Long dpid, String segmentationId, String attachedMac, long localPort,
86                                        NeutronSecurityGroup securityGroup, String portUuid, boolean write) {
87
88         LOG.trace("programPortSecurityGroup: neutronSecurityGroup: {} ", securityGroup);
89         if (securityGroup == null || securityGroup.getSecurityRules() == null) {
90             return;
91         }
92
93         List<NeutronSecurityRule> portSecurityList = securityGroup.getSecurityRules();
94         /* Iterate over the Port Security Rules in the Port Security Group bound to the port*/
95         for (NeutronSecurityRule portSecurityRule : portSecurityList) {
96
97             /**
98              * Neutron Port Security Acl "egress" and "IPv4"
99              * Check that the base conditions for flow based Port Security are true:
100              * Port Security Rule Direction ("egress") and Protocol ("IPv4")
101              * Neutron defines the direction "ingress" as the vSwitch to the VM as defined in:
102              * http://docs.openstack.org/api/openstack-network/2.0/content/security_groups.html
103              *
104              */
105
106             if (portSecurityRule == null ||
107                     portSecurityRule.getSecurityRuleEthertype() == null ||
108                     portSecurityRule.getSecurityRuleDirection() == null) {
109                 continue;
110             }
111
112             if ("IPv4".equals(portSecurityRule.getSecurityRuleEthertype())
113                     && portSecurityRule.getSecurityRuleDirection().equals("egress")) {
114                 LOG.debug("programPortSecurityGroup: Acl Rule matching IPv4 and ingress is: {} ", portSecurityRule);
115                 if (null != portSecurityRule.getSecurityRemoteGroupID()) {
116                     //Remote Security group is selected
117                     List<Neutron_IPs> remoteSrcAddressList = securityServicesManager
118                             .getVmListForSecurityGroup(portUuid,portSecurityRule.getSecurityRemoteGroupID());
119                     if (null != remoteSrcAddressList) {
120                         for (Neutron_IPs vmIp :remoteSrcAddressList ) {
121
122                             programPortSecurityRule(dpid, segmentationId, attachedMac,
123                                                     localPort, portSecurityRule, vmIp, write);
124                         }
125                         if (write) {
126                             securityGroupCacheManger.addToCache(portSecurityRule.getSecurityRemoteGroupID(), portUuid);
127                         } else {
128                             securityGroupCacheManger.removeFromCache(portSecurityRule.getSecurityRemoteGroupID(),
129                                                                      portUuid);
130                         }
131                     }
132                 } else {
133                     programPortSecurityRule(dpid, segmentationId, attachedMac, localPort,
134                                             portSecurityRule, null, write);
135                 }
136                 if (write) {
137                     securityGroupCacheManger.portAdded(securityGroup.getSecurityGroupUUID(), portUuid);
138                 } else {
139                     securityGroupCacheManger.portRemoved(securityGroup.getSecurityGroupUUID(), portUuid);
140                 }
141             }
142         }
143     }
144
145     @Override
146     public void programPortSecurityRule(Long dpid, String segmentationId, String attachedMac,
147                                         long localPort, NeutronSecurityRule portSecurityRule,
148                                         Neutron_IPs vmIp, boolean write) {
149         if (null == portSecurityRule.getSecurityRuleProtocol()) {
150             /* TODO Rework on the priority values */
151             egressAclIPv4(dpid, segmentationId, attachedMac,
152                           write, Constants.PROTO_PORT_PREFIX_MATCH_PRIORITY);
153         } else {
154             String ipaddress = null;
155             if (null != vmIp) {
156                 ipaddress = vmIp.getIpAddress();
157                 try {
158                     InetAddress address = InetAddress.getByName(ipaddress);
159                     // TODO: remove this when ipv6 support is implemented
160                     if (address instanceof Inet6Address) {
161                         LOG.debug("Skipping ip address {}. IPv6 support is not yet implemented.", address);
162                         return;
163                     }
164                 } catch (UnknownHostException e) {
165                     LOG.warn("Invalid ip address {}", ipaddress, e);
166                     return;
167                 }
168             }
169
170             if (null != portSecurityRule.getSecurityRuleRemoteIpPrefix()) {
171                 String prefixStr = portSecurityRule.getSecurityRuleRemoteIpPrefix();
172                 try {
173                     IpPrefix ipPrefix = IpPrefixBuilder.getDefaultInstance(prefixStr);
174                     // TODO: remove this when ipv6 support is implemented
175                     if (ipPrefix.getIpv6Prefix() != null) {
176                         LOG.debug("Skipping ip prefix {}. IPv6 support is not yet implemented.", ipPrefix);
177                         return;
178                     }
179                 } catch (IllegalArgumentException e) {
180                     LOG.warn("Invalid ip prefix {}", prefixStr, e);
181                     return;
182                 }
183             }
184
185             switch (portSecurityRule.getSecurityRuleProtocol()) {
186               case MatchUtils.TCP:
187                   LOG.debug("programPortSecurityRule: Rule matching TCP", portSecurityRule);
188                   egressAclTcp(dpid, segmentationId, attachedMac,
189                                portSecurityRule,ipaddress, write,
190                                Constants.PROTO_PORT_PREFIX_MATCH_PRIORITY);
191                   break;
192               case MatchUtils.UDP:
193                   LOG.debug("programPortSecurityRule: Rule matching UDP", portSecurityRule);
194                   egressAclUdp(dpid, segmentationId, attachedMac,
195                                portSecurityRule, ipaddress, write,
196                                Constants.PROTO_PORT_PREFIX_MATCH_PRIORITY);
197                   break;
198               case MatchUtils.ICMP:
199                   LOG.debug("programPortSecurityRule: Rule matching ICMP", portSecurityRule);
200                   egressAclIcmp(dpid, segmentationId, attachedMac,
201                                 portSecurityRule, ipaddress,write,
202                                 Constants.PROTO_PORT_PREFIX_MATCH_PRIORITY);
203                   break;
204               default:
205                   LOG.info("programPortSecurityAcl: Protocol is not TCP/UDP/ICMP but other " +
206                           "protocol = ", portSecurityRule.getSecurityRuleProtocol());
207                   egressOtherProtocolAclHandler(dpid, segmentationId, attachedMac,
208                                       portSecurityRule, ipaddress, write,
209                                       Constants.PROTO_PORT_PREFIX_MATCH_PRIORITY);
210                   break;
211             }
212         }
213     }
214
215     private void egressOtherProtocolAclHandler(Long dpidLong, String segmentationId, String srcMac,
216                                                NeutronSecurityRule portSecurityRule, String dstAddress,
217                                                boolean write, Integer priority) {
218         MatchBuilder matchBuilder = new MatchBuilder();
219         String flowId = "Egress_Other_" + segmentationId + "_" + srcMac + "_";
220         matchBuilder = MatchUtils.createEtherMatchWithType(matchBuilder,srcMac,null);
221
222         short proto = 0;
223         try {
224             Integer protocol = new Integer(portSecurityRule.getSecurityRuleProtocol());
225             proto = protocol.shortValue();
226             flowId = flowId + proto;
227         } catch (NumberFormatException e) {
228             LOG.error("Protocol vlaue conversion failure", e);
229         }
230         matchBuilder = MatchUtils.createIpProtocolMatch(matchBuilder, proto);
231
232         if (null != dstAddress) {
233             flowId = flowId + dstAddress;
234             matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder, null,
235                                                          MatchUtils.iPv4PrefixFromIPv4Address(dstAddress));
236
237         } else if (null != portSecurityRule.getSecurityRuleRemoteIpPrefix()) {
238             flowId = flowId + portSecurityRule.getSecurityRuleRemoteIpPrefix();
239             matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder, null,
240                     new Ipv4Prefix(portSecurityRule.getSecurityRuleRemoteIpPrefix()));
241         }
242         flowId = flowId + "_Permit";
243         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
244         syncFlow(flowId, nodeBuilder, matchBuilder, priority, write, false, securityServicesManager.isConntrackEnabled());
245     }
246
247     @Override
248     public void programFixedSecurityGroup(Long dpid, String segmentationId, String attachedMac,
249                                         long localPort, List<Neutron_IPs> srcAddressList,
250                                         boolean isLastPortinBridge, boolean isComputePort ,boolean write) {
251         // If it is the only port in the bridge add the rule to allow any DHCP client traffic
252         //if (isLastPortinBridge) {
253             egressAclDhcpAllowClientTrafficFromVm(dpid, write, Constants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY);
254        // }
255         if (isComputePort) {
256             programArpRule(dpid, segmentationId, localPort, attachedMac, write);
257             if (securityServicesManager.isConntrackEnabled()) {
258                 programEgressAclFixedConntrackRule(dpid, segmentationId, localPort, attachedMac, write);
259             }
260             // add rule to drop the DHCP server traffic originating from the vm.
261             egressAclDhcpDropServerTrafficfromVm(dpid, localPort, write,
262                                                  Constants.PROTO_DHCP_CLIENT_SPOOF_MATCH_PRIORITY_DROP);
263             //Adds rule to check legitimate ip/mac pair for each packet from the vm
264             for (Neutron_IPs srcAddress : srcAddressList) {
265                 try {
266                     InetAddress address = InetAddress.getByName(srcAddress.getIpAddress());
267                     if (address instanceof Inet4Address) {
268                         String addressWithPrefix = srcAddress.getIpAddress() + HOST_MASK;
269                         egressAclAllowTrafficFromVmIpMacPair(dpid, localPort, attachedMac, addressWithPrefix,
270                                                              Constants.PROTO_VM_IP_MAC_MATCH_PRIORITY,write);
271                     } else {
272                         LOG.debug("Skipping IPv6 address {}. IPv6 support is not yet implemented.",
273                                   srcAddress.getIpAddress());
274                     }
275                 } catch(UnknownHostException e) {
276                     LOG.warn("Invalid IP address {}", srcAddress.getIpAddress());
277                 }
278             }
279         }
280     }
281
282     private void programArpRule(Long dpid, String segmentationId, long localPort, String attachedMac, boolean write) {
283         String nodeName = Constants.OPENFLOW_NODE_PREFIX + dpid;
284         MatchBuilder matchBuilder = new MatchBuilder();
285         NodeBuilder nodeBuilder = createNodeBuilder(nodeName);
286         String flowId = "Egress_ARP_" + segmentationId + "_" + localPort + "_";
287
288         EthernetMatchBuilder ethernetType = new EthernetMatchBuilder();
289         EthernetTypeBuilder ethTypeBuilder = new EthernetTypeBuilder();
290         ethTypeBuilder.setType(new EtherType(0x0806L));
291         ethernetType.setEthernetType(ethTypeBuilder.build());
292         matchBuilder.setEthernetMatch(ethernetType.build());
293
294         ArpMatchBuilder arpDstMatch = new ArpMatchBuilder();
295         ArpSourceHardwareAddressBuilder arpSrc = new ArpSourceHardwareAddressBuilder();
296         arpSrc.setAddress(new MacAddress(attachedMac));
297         arpDstMatch.setArpSourceHardwareAddress(arpSrc.build());
298         matchBuilder.setLayer3Match(arpDstMatch.build());
299
300         syncFlow(flowId, nodeBuilder, matchBuilder, Constants.PROTO_MATCH_PRIORITY, write, false, false);
301     }
302
303     private void programEgressAclFixedConntrackRule(Long dpid,
304             String segmentationId, long localPort, String attachMac, boolean write) {
305          try {
306              String nodeName = Constants.OPENFLOW_NODE_PREFIX + dpid;
307              programConntrackUntrackRule(nodeName, segmentationId, localPort,attachMac,
308                                          Constants.CT_STATE_UNTRACKED_PRIORITY, write );
309              programConntrackTrackedPlusEstRule(nodeName, dpid, segmentationId, localPort,
310                                          Constants.CT_STATE_TRACKED_EST_PRIORITY, write );
311              programConntrackNewDropRule(nodeName, dpid, segmentationId, localPort,
312                                               Constants.CT_STATE_NEW_PRIORITY_DROP, write );
313              LOG.info("programEgressAclFixedConntrackRule :  default connection tracking rule are added.");
314          } catch (Exception e) {
315              LOG.error("Failed to add default conntrack rules : " , e);
316          }
317      }
318
319      private void programConntrackUntrackRule(String nodeName, String segmentationId,
320                                               long localPort, String attachMac, Integer priority, boolean write) {
321          MatchBuilder matchBuilder = new MatchBuilder();
322          NodeBuilder nodeBuilder = createNodeBuilder(nodeName);
323          String flowName = "Egress_Fixed_Conntrk_Untrk_" + segmentationId + "_" + localPort + "_";
324          matchBuilder = MatchUtils.createEtherMatchWithType(matchBuilder, attachMac, null);
325          matchBuilder = MatchUtils.addCtState(matchBuilder,0x00,0X80);
326          FlowBuilder flowBuilder = new FlowBuilder();
327          flowBuilder.setMatch(matchBuilder.build());
328          FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(priority);
329          if (write) {
330              InstructionBuilder ib = new InstructionBuilder();
331              List<Instruction> instructionsList = Lists.newArrayList();
332              InstructionsBuilder isb = new InstructionsBuilder();
333              ActionBuilder ab = new ActionBuilder();
334              ab.setAction(ActionUtils.nxConntrackAction(0, 0L, 0, (short)0x0));
335              // 0xff means no table, 0x0 is table = 0
336              ab.setOrder(0);
337              ab.setKey(new ActionKey(0));
338              List<Action> actionList = Lists.newArrayList();
339              actionList.add(ab.build());
340              ApplyActionsBuilder aab = new ApplyActionsBuilder();
341              aab.setAction(actionList);
342              ib.setOrder(0);
343              ib.setKey(new InstructionKey(0));
344              ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
345              instructionsList.add(ib.build());
346              isb.setInstruction(instructionsList);
347              flowBuilder.setInstructions(isb.build());
348              writeFlow(flowBuilder, nodeBuilder);
349              LOG.info("EGRESS:default programConntrackUntrackRule() flows are written");
350          } else {
351              removeFlow(flowBuilder, nodeBuilder);
352          }
353      }
354
355      private void programConntrackTrackedPlusEstRule(String nodeName, Long dpid, String segmentationId,
356                                                    long localPort,Integer priority, boolean write) {
357          MatchBuilder matchBuilder = new MatchBuilder();
358          NodeBuilder nodeBuilder = createNodeBuilder(nodeName);
359          String flowName = "Egress_Fixed_Conntrk_TrkEst_" + segmentationId + "_" + localPort + "_";
360          matchBuilder = MatchUtils.createInPortMatch(matchBuilder, dpid, localPort);
361          matchBuilder = MatchUtils.addCtState(matchBuilder,0x82, 0x82);
362          FlowBuilder flowBuilder = new FlowBuilder();
363          flowBuilder.setMatch(matchBuilder.build());
364          FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(priority);
365          if (write) {
366              InstructionBuilder ib = new InstructionBuilder();
367              List<Instruction> instructionsList = Lists.newArrayList();
368              InstructionsBuilder isb = new InstructionsBuilder();
369              // got to next table instruction
370              ib = this.getMutablePipelineInstructionBuilder();
371              ib.setOrder(0);
372              ib.setKey(new InstructionKey(0));
373              instructionsList.add(ib.build());
374              isb.setInstruction(instructionsList);
375              flowBuilder.setInstructions(isb.build());
376              writeFlow(flowBuilder, nodeBuilder);
377              LOG.info("EGRESS:default programConntrackTrackedPlusEstRule() flows are written");
378          } else {
379              removeFlow(flowBuilder, nodeBuilder);
380          }
381      }
382
383      private void programConntrackNewDropRule(String nodeName, Long dpid, String segmentationId,
384                                               long localPort, Integer priority, boolean write) {
385          MatchBuilder matchBuilder = new MatchBuilder();
386          NodeBuilder nodeBuilder = createNodeBuilder(nodeName);
387          String flowName = "Egress_Fixed_Conntrk_NewDrop_" + segmentationId + "_" + localPort + "_";
388          matchBuilder = MatchUtils.createInPortMatch(matchBuilder, dpid, localPort);
389          matchBuilder = MatchUtils.addCtState(matchBuilder,0x01, 0x01);
390          FlowBuilder flowBuilder = new FlowBuilder();
391          flowBuilder.setMatch(matchBuilder.build());
392          FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(priority);
393          if (write) {
394              InstructionBuilder ib = new InstructionBuilder();
395              InstructionsBuilder isb = new InstructionsBuilder();
396              List<Instruction> instructions = Lists.newArrayList();
397              InstructionUtils.createDropInstructions(ib);
398              ib.setOrder(0);
399              ib.setKey(new InstructionKey(0));
400              instructions.add(ib.build());
401              isb.setInstruction(instructions);
402              LOG.debug("Instructions contain: {}", ib.getInstruction());
403              flowBuilder.setInstructions(isb.build());
404              writeFlow(flowBuilder, nodeBuilder);
405              LOG.info("EGRESS:default programConntrackNewDropRule() flows are written");
406          } else {
407              removeFlow(flowBuilder, nodeBuilder);
408          }
409      }
410
411     /**
412      * Allows IPv4 packet egress from the src mac address.
413      * @param dpidLong the dpid
414      * @param segmentationId the segementation id
415      * @param srcMac the src mac address
416      * @param write add or remove
417      * @param protoPortMatchPriority the protocol match priority.
418      */
419     private void egressAclIPv4(Long dpidLong, String segmentationId, String srcMac,
420                                boolean write, Integer protoPortMatchPriority ) {
421         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
422         MatchBuilder matchBuilder = new MatchBuilder();
423         String flowId = "Egress_IP" + segmentationId + "_" + srcMac + "_Permit_";
424         matchBuilder = MatchUtils.createEtherMatchWithType(matchBuilder,srcMac,null);
425         syncFlow(flowId, nodeBuilder, matchBuilder, protoPortMatchPriority, write, false, false);
426     }
427
428     /**
429      * Creates a egress match with src macaddress. If dest address is specified
430      * destination specific match will be created. Otherwise a match with a
431      * CIDR will be created.
432      * @param dpidLong the dpid
433      * @param segmentationId the segmentation id
434      * @param srcMac the source mac address.
435      * @param portSecurityRule the security rule in the SG
436      * @param dstAddress the destination IP address
437      * @param write add or delete
438      * @param protoPortMatchPriority the protocol match priroty
439      */
440     private void egressAclTcp(Long dpidLong, String segmentationId, String srcMac,
441                               NeutronSecurityRule portSecurityRule, String dstAddress,
442                               boolean write, Integer protoPortMatchPriority) {
443         boolean portRange = false;
444         MatchBuilder matchBuilder = new MatchBuilder();
445         String flowId = "Egress_TCP_" + segmentationId + "_" + srcMac + "_";
446         matchBuilder = MatchUtils.createEtherMatchWithType(matchBuilder,srcMac,null);
447
448         /* Custom TCP Match */
449         if (portSecurityRule.getSecurityRulePortMin().equals(portSecurityRule.getSecurityRulePortMax())) {
450             flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_";
451             matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.TCP_SHORT, 0,
452                                                      portSecurityRule.getSecurityRulePortMin());
453         } else {
454             /* All TCP Match */
455             if (portSecurityRule.getSecurityRulePortMin().equals(PORT_RANGE_MIN)
456                     && portSecurityRule.getSecurityRulePortMax().equals(PORT_RANGE_MAX)) {
457                 flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_"
458                             + portSecurityRule.getSecurityRulePortMax() + "_";
459                 matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.TCP_SHORT, 0, 0);
460             } else {
461                 portRange = true;
462             }
463         }
464         if (null != dstAddress) {
465             flowId = flowId + dstAddress;
466             matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder,null,
467                                       MatchUtils.iPv4PrefixFromIPv4Address(dstAddress));
468
469         } else if (null != portSecurityRule.getSecurityRuleRemoteIpPrefix()) {
470             flowId = flowId + portSecurityRule.getSecurityRuleRemoteIpPrefix();
471             matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder,null,
472                                       new Ipv4Prefix(portSecurityRule.getSecurityRuleRemoteIpPrefix()));
473         }
474         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
475         if (portRange) {
476             Map<Integer, Integer> portMaskMap = MatchUtils
477                     .getLayer4MaskForRange(portSecurityRule.getSecurityRulePortMin(),
478                                            portSecurityRule.getSecurityRulePortMax());
479             for (Integer port: portMaskMap.keySet()) {
480                 String rangeflowId = flowId + port + "_" + portMaskMap.get(port) + "_";
481                 rangeflowId = rangeflowId + "_Permit";
482                 MatchUtils.addLayer4MatchWithMask(matchBuilder, MatchUtils.TCP_SHORT,
483                                                   0, port, portMaskMap.get(port));
484                 syncFlow(rangeflowId, nodeBuilder, matchBuilder, protoPortMatchPriority, write, false, securityServicesManager.isConntrackEnabled());
485             }
486         } else {
487             flowId = flowId + "_Permit";
488             syncFlow(flowId, nodeBuilder, matchBuilder, protoPortMatchPriority, write, false, securityServicesManager.isConntrackEnabled());
489         }
490     }
491
492     /**
493      * Creates a 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
503      */
504     private void egressAclIcmp(Long dpidLong, String segmentationId, String srcMac,
505                                NeutronSecurityRule portSecurityRule, String dstAddress,
506                                boolean write, Integer protoPortMatchPriority) {
507
508         MatchBuilder matchBuilder = new MatchBuilder();
509         String flowId = "Egress_ICMP_" + segmentationId + "_" + srcMac + "_";
510         matchBuilder = MatchUtils.createEtherMatchWithType(matchBuilder,srcMac,null);
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());
519         } else {
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);
523         }
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()));
533             }
534         }
535         flowId = flowId + "_Permit";
536         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
537         syncFlow(flowId, nodeBuilder, matchBuilder, protoPortMatchPriority, write, false, securityServicesManager.isConntrackEnabled());
538     }
539
540     /**
541      * Creates a egress match with src macaddress. If dest address is specified
542      * destination specific match will be created. Otherwise a match with a
543      * CIDR will be created.
544      * @param dpidLong the dpid
545      * @param segmentationId the segmentation id
546      * @param srcMac the source mac address.
547      * @param portSecurityRule the security rule in the SG
548      * @param dstAddress the source IP address
549      * @param write add or delete
550      * @param protoPortMatchPriority the protocol match priroty
551      */
552     private void egressAclUdp(Long dpidLong, String segmentationId, String srcMac,
553                               NeutronSecurityRule portSecurityRule, String dstAddress,
554                               boolean write, Integer protoPortMatchPriority) {
555         boolean portRange = false;
556         MatchBuilder matchBuilder = new MatchBuilder();
557         String flowId = "Egress_UDP_" + segmentationId + "_" + srcMac + "_";
558         matchBuilder = MatchUtils.createEtherMatchWithType(matchBuilder,srcMac,null);
559
560         /* Custom UDP Match */
561         if (portSecurityRule.getSecurityRulePortMin().equals(portSecurityRule.getSecurityRulePortMax())) {
562             flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_";
563             matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.UDP_SHORT, 0,
564                                                      portSecurityRule.getSecurityRulePortMin());
565         } else {
566             /* All UDP Match */
567             if (portSecurityRule.getSecurityRulePortMin().equals(PORT_RANGE_MIN)
568                     && portSecurityRule.getSecurityRulePortMax().equals(PORT_RANGE_MAX)) {
569                 flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_"
570                     + portSecurityRule.getSecurityRulePortMax() + "_";
571                 matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.UDP_SHORT, 0, 0);
572             } else {
573                 portRange = true;
574             }
575         }
576         if (null != dstAddress) {
577             flowId = flowId + dstAddress;
578             matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder,null,
579                                                         MatchUtils.iPv4PrefixFromIPv4Address(dstAddress));
580         } else if (null != portSecurityRule.getSecurityRuleRemoteIpPrefix()) {
581             flowId = flowId + portSecurityRule.getSecurityRuleRemoteIpPrefix();
582             matchBuilder = MatchUtils.addRemoteIpPrefix(matchBuilder, null,
583                                                         new Ipv4Prefix(portSecurityRule
584                                                                        .getSecurityRuleRemoteIpPrefix()));
585         }
586         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
587         if (portRange) {
588             Map<Integer, Integer> portMaskMap = MatchUtils
589                     .getLayer4MaskForRange(portSecurityRule.getSecurityRulePortMin(),
590                                            portSecurityRule.getSecurityRulePortMax());
591             for (Integer port: portMaskMap.keySet()) {
592                 String rangeflowId = flowId + port + "_" + portMaskMap.get(port) + "_";
593                 rangeflowId = rangeflowId + "_Permit";
594                 MatchUtils.addLayer4MatchWithMask(matchBuilder, MatchUtils.UDP_SHORT,
595                                                   0, port, portMaskMap.get(port));
596                 syncFlow(rangeflowId, nodeBuilder, matchBuilder, protoPortMatchPriority, write, false, securityServicesManager.isConntrackEnabled());
597             }
598         } else {
599             flowId = flowId + "_Permit";
600             syncFlow(flowId, nodeBuilder, matchBuilder, protoPortMatchPriority, write, false, securityServicesManager.isConntrackEnabled());
601         }
602     }
603
604     public void egressACLDefaultTcpDrop(Long dpidLong, String segmentationId, String attachedMac,
605                                         int priority, boolean write) {
606         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
607         FlowBuilder flowBuilder = new FlowBuilder();
608         String flowName = "TCP_Syn_Egress_Default_Drop_" + segmentationId + "_" + attachedMac;
609         FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(priority);
610
611         MatchBuilder matchBuilder = new MatchBuilder();
612         MatchUtils.createSmacTcpPortWithFlagMatch(matchBuilder, attachedMac, Constants.TCP_SYN, segmentationId);
613         flowBuilder.setMatch(matchBuilder.build());
614
615         if (write) {
616             InstructionBuilder ib = new InstructionBuilder();
617             InstructionsBuilder isb = new InstructionsBuilder();
618             List<Instruction> instructions = Lists.newArrayList();
619
620             InstructionUtils.createDropInstructions(ib);
621             ib.setOrder(0);
622             ib.setKey(new InstructionKey(0));
623             instructions.add(ib.build());
624             isb.setInstruction(instructions);
625
626             flowBuilder.setInstructions(isb.build());
627             writeFlow(flowBuilder, nodeBuilder);
628         } else {
629             removeFlow(flowBuilder, nodeBuilder);
630         }
631     }
632
633     public void egressACLTcpPortWithPrefix(Long dpidLong, String segmentationId, String attachedMac, boolean write,
634                                            Integer securityRulePortMin, String securityRuleIpPrefix,
635                                            Integer priority) {
636         PortNumber tcpPort = new PortNumber(securityRulePortMin);
637         Ipv4Prefix srcIpPrefix = new Ipv4Prefix(securityRuleIpPrefix);
638
639         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
640         FlowBuilder flowBuilder = new FlowBuilder();
641         String flowName = "UcastEgress_" + segmentationId + "_" + attachedMac
642                 + securityRulePortMin + securityRuleIpPrefix;
643         FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(priority);
644
645         MatchBuilder matchBuilder = new MatchBuilder();
646         MatchUtils.createSmacTcpSynDstIpPrefixTcpPort(matchBuilder, new MacAddress(attachedMac),
647                         tcpPort, Constants.TCP_SYN, segmentationId, srcIpPrefix);
648         flowBuilder.setMatch(matchBuilder.build());
649
650         if (write) {
651             InstructionsBuilder isb = new InstructionsBuilder();
652             List<Instruction> instructionsList = Lists.newArrayList();
653
654             InstructionBuilder ib = this.getMutablePipelineInstructionBuilder();
655             ib.setOrder(0);
656             ib.setKey(new InstructionKey(0));
657             instructionsList.add(ib.build());
658             isb.setInstruction(instructionsList);
659
660             flowBuilder.setInstructions(isb.build());
661             writeFlow(flowBuilder, nodeBuilder);
662         } else {
663             removeFlow(flowBuilder, nodeBuilder);
664         }
665     }
666
667     public void egressAllowProto(Long dpidLong, String segmentationId, String attachedMac, boolean write,
668                                  String securityRuleProtcol, Integer priority) {
669         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
670         FlowBuilder flowBuilder = new FlowBuilder();
671         String flowName = "EgressAllProto_" + segmentationId + "_"
672                 + attachedMac + "_AllowEgressTCPSyn_" + securityRuleProtcol;
673         FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(priority);
674
675         MatchBuilder matchBuilder = new MatchBuilder();
676         MatchUtils.createDmacIpTcpSynMatch(matchBuilder, new MacAddress(attachedMac), null, null);
677         MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(segmentationId));
678         flowBuilder.setMatch(matchBuilder.build());
679
680         if (write) {
681             InstructionsBuilder isb = new InstructionsBuilder();
682             List<Instruction> instructionsList = Lists.newArrayList();
683
684             InstructionBuilder ib = this.getMutablePipelineInstructionBuilder();
685             ib.setOrder(0);
686             ib.setKey(new InstructionKey(0));
687             instructionsList.add(ib.build());
688             isb.setInstruction(instructionsList);
689
690             flowBuilder.setInstructions(isb.build());
691             writeFlow(flowBuilder, nodeBuilder);
692         } else {
693             removeFlow(flowBuilder, nodeBuilder);
694         }
695     }
696
697     public void egressACLPermitAllProto(Long dpidLong, String segmentationId, String attachedMac,
698                                         boolean write, String securityRuleIpPrefix, Integer priority) {
699         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
700         FlowBuilder flowBuilder = new FlowBuilder();
701         String flowName = "Egress_Proto_ACL" + segmentationId + "_" +
702                 attachedMac + "_Permit_" + securityRuleIpPrefix;
703         FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(priority);
704
705         MatchBuilder matchBuilder = new MatchBuilder();
706         MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(segmentationId));
707         if (securityRuleIpPrefix != null) {
708             Ipv4Prefix srcIpPrefix = new Ipv4Prefix(securityRuleIpPrefix);
709             MatchUtils.createSmacIpTcpSynMatch(matchBuilder, new MacAddress(attachedMac), null, srcIpPrefix);
710         } else {
711             MatchUtils.createSmacIpTcpSynMatch(matchBuilder, new MacAddress(attachedMac), null, null);
712         }
713         flowBuilder.setMatch(matchBuilder.build());
714
715         if (write) {
716             InstructionsBuilder isb = new InstructionsBuilder();
717             List<Instruction> instructionsList = Lists.newArrayList();
718
719             InstructionBuilder ib = this.getMutablePipelineInstructionBuilder();
720             ib.setOrder(0);
721             ib.setKey(new InstructionKey(0));
722             instructionsList.add(ib.build());
723             isb.setInstruction(instructionsList);
724
725             flowBuilder.setInstructions(isb.build());
726             writeFlow(flowBuilder, nodeBuilder);
727         } else {
728             removeFlow(flowBuilder, nodeBuilder);
729         }
730     }
731
732     public void egressACLTcpSyn(Long dpidLong, String segmentationId, String attachedMac, boolean write,
733                                 Integer securityRulePortMin, Integer priority) {
734         PortNumber tcpPort = new PortNumber(securityRulePortMin);
735
736         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
737         FlowBuilder flowBuilder = new FlowBuilder();
738         String flowName = "Ucast_this.getTable()" + segmentationId + "_" + attachedMac + securityRulePortMin;
739         FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(priority);
740
741         MatchBuilder matchBuilder = new MatchBuilder();
742         MatchUtils.createSmacTcpSyn(matchBuilder, attachedMac, tcpPort, Constants.TCP_SYN, segmentationId);
743         flowBuilder.setMatch(matchBuilder.build());
744
745         if (write) {
746             // Instantiate the Builders for the OF Actions and Instructions
747             InstructionsBuilder isb = new InstructionsBuilder();
748             List<Instruction> instructionsList = Lists.newArrayList();
749
750             InstructionBuilder ib = this.getMutablePipelineInstructionBuilder();
751             ib.setOrder(0);
752             ib.setKey(new InstructionKey(0));
753             instructionsList.add(ib.build());
754             isb.setInstruction(instructionsList);
755
756             flowBuilder.setInstructions(isb.build());
757             writeFlow(flowBuilder, nodeBuilder);
758         } else {
759             removeFlow(flowBuilder, nodeBuilder);
760         }
761     }
762
763     /**
764      * Adds flow to allow any DHCP client traffic.
765      *
766      * @param dpidLong the dpid
767      * @param write whether to write or delete the flow
768      * @param priority the priority
769      */
770     private void egressAclDhcpAllowClientTrafficFromVm(Long dpidLong,
771                                                        boolean write, Integer priority) {
772         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
773         String flowName = "Egress_DHCP_Client"  + "_Permit_";
774         MatchBuilder matchBuilder = new MatchBuilder();
775         MatchUtils.createDhcpMatch(matchBuilder, DHCP_DESTINATION_PORT, DHCP_SOURCE_PORT);
776         syncFlow(flowName, nodeBuilder, matchBuilder, priority, write, false, false);
777     }
778
779     /**
780      * Adds rule to prevent DHCP spoofing by the vm attached to the port.
781      *
782      * @param dpidLong the dpid
783      * @param localPort the local port
784      * @param write is write or delete
785      * @param priority  the priority
786      */
787     private void egressAclDhcpDropServerTrafficfromVm(Long dpidLong, long localPort,
788                                                       boolean write, Integer priority) {
789
790         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
791         String flowName = "Egress_DHCP_Server" + "_" + localPort + "_DROP_";
792         MatchBuilder matchBuilder = new MatchBuilder();
793         MatchUtils.createInPortMatch(matchBuilder, dpidLong, localPort);
794         MatchUtils.createDhcpMatch(matchBuilder, DHCP_SOURCE_PORT, DHCP_DESTINATION_PORT);
795         syncFlow(flowName, nodeBuilder, matchBuilder, priority, write, true, false);
796     }
797
798     /**
799      * Adds rule to check legitimate ip/mac pair for each packet from the vm.
800      *
801      * @param dpidLong the dpid
802      * @param localPort the local port
803      * @param srcIp the vm ip address
804      * @param attachedMac the vm mac address
805      * @param priority  the priority
806      * @param write is write or delete
807      */
808     private void egressAclAllowTrafficFromVmIpMacPair(Long dpidLong, long localPort,
809                                                       String attachedMac, String srcIp,
810                                                       Integer priority, boolean write) {
811         NodeBuilder nodeBuilder = FlowUtils.createNodeBuilder(dpidLong);
812         String flowName = "Egress_Allow_VM_IP_MAC" + "_" + localPort + attachedMac + "_Permit_";
813         MatchBuilder matchBuilder = new MatchBuilder();
814         MatchUtils.createSrcL3Ipv4MatchWithMac(matchBuilder, new Ipv4Prefix(srcIp),new MacAddress(attachedMac));
815         MatchUtils.createInPortMatch(matchBuilder, dpidLong, localPort);
816         LOG.debug("egressAclAllowTrafficFromVmIpMacPair: MatchBuilder contains: {}", matchBuilder);
817         syncFlow(flowName, nodeBuilder, matchBuilder, priority, write, false, false);
818     }
819
820     /**
821      * Add or remove flow to the node.
822      *
823      * @param flowName the the flow id
824      * @param nodeBuilder the node builder
825      * @param matchBuilder the matchbuilder
826      * @param priority the protocol priority
827      * @param write whether it is a write
828      * @param drop whether it is a drop or forward
829      * @param isCtCommit commit the connection or CT to track
830      */
831     private void syncFlow(String flowName, NodeBuilder nodeBuilder,
832                           MatchBuilder matchBuilder, Integer priority,
833                           boolean write, boolean drop, boolean isCtCommit) {
834         MatchBuilder matchBuilder1 = matchBuilder;
835         if (isCtCommit) {
836             matchBuilder1 = MatchUtils.addCtState(matchBuilder1,0x81, 0x81);
837         }
838         FlowBuilder flowBuilder = new FlowBuilder();
839         flowBuilder.setMatch(matchBuilder1.build());
840         FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(priority);
841
842         if (write) {
843             InstructionBuilder ib = this.getMutablePipelineInstructionBuilder();
844             InstructionBuilder ib1 = new InstructionBuilder();
845             ActionBuilder ab = new ActionBuilder();
846             ApplyActionsBuilder aab = new ApplyActionsBuilder();
847             if (drop) {
848                 InstructionUtils.createDropInstructions(ib);
849             }
850             ib.setOrder(0);
851             ib.setKey(new InstructionKey(0));
852             InstructionsBuilder isb = new InstructionsBuilder();
853             List<Instruction> instructionsList = Lists.newArrayList();
854             instructionsList.add(ib.build());
855             if (isCtCommit) {
856                 LOG.info("Adding Conntarck rule, flowname = " + flowName);
857                 ab.setAction(ActionUtils.nxConntrackAction(1, 0L, 0, (short)0xff));
858                 ab.setOrder(0);
859                 ab.setKey(new ActionKey(0));
860                 List<Action> actionList = Lists.newArrayList();
861                 actionList.add(ab.build());
862                 aab.setAction(actionList);
863                 ib1.setOrder(1);
864                 ib1.setKey(new InstructionKey(1));
865                 ib1.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
866                 instructionsList.add(ib1.build());
867             }
868             isb.setInstruction(instructionsList);
869             flowBuilder.setInstructions(isb.build());
870             writeFlow(flowBuilder, nodeBuilder);
871         } else {
872             removeFlow(flowBuilder, nodeBuilder);
873         }
874     }
875
876     @Override
877     public void setDependencies(BundleContext bundleContext, ServiceReference serviceReference) {
878         super.setDependencies(bundleContext.getServiceReference(EgressAclProvider.class.getName()), this);
879         securityServicesManager =
880                 (SecurityServicesManager) ServiceHelper.getGlobalInstance(SecurityServicesManager.class, this);
881         securityGroupCacheManger =
882                 (SecurityGroupCacheManger) ServiceHelper.getGlobalInstance(SecurityGroupCacheManger.class, this);
883     }
884
885     @Override
886     public void setDependencies(Object impl) {}
887 }