affbb3a72267b2e13ff2e004092f9326c90f7206
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / flow / PortSecurity.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, 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.groupbasedpolicy.renderer.ofoverlay.flow;
10
11 import java.util.Collections;
12 import java.util.List;
13 import java.util.ArrayList;
14 import java.util.HashSet;
15 import java.util.Set;
16
17 import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
18 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
19 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
20 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.endpoint.EndpointManager;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.go.to.table._case.GoToTable;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
35
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.ExternalImplicitGroup;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.Segmentation;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2FloodDomain;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlanGpe;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 /**
53  * Manage the table that enforces port security
54  *
55  */
56 public class PortSecurity extends FlowTable {
57     protected static final Logger LOG =
58             LoggerFactory.getLogger(PortSecurity.class);
59
60     public static short TABLE_ID;
61
62     public PortSecurity(OfContext ctx, short tableId) {
63         super(ctx);
64         TABLE_ID=tableId;
65     }
66
67     @Override
68     public short getTableId() {
69         return TABLE_ID;
70     }
71
72     @Override
73     public void sync(NodeId nodeId, OfWriter ofWriter) {
74
75         // Allow traffic from tunnel ports
76         NodeConnectorId vxLanTunnel = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlan.class);
77         NodeConnectorId vxLanGpeTunnel = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlanGpe.class);
78         if (vxLanTunnel != null)
79             ofWriter.writeFlow(nodeId, TABLE_ID, allowFromPort(vxLanTunnel));
80         if (vxLanGpeTunnel != null)
81             ofWriter.writeFlow(nodeId, TABLE_ID, allowFromPort(vxLanGpeTunnel));
82
83         // Default drop all
84         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(1, null, TABLE_ID));
85
86         // Drop IP traffic that doesn't match a source IP rule
87         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(110, FlowUtils.ARP, TABLE_ID));
88         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(111, FlowUtils.IPv4, TABLE_ID));
89         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(112, FlowUtils.IPv6, TABLE_ID));
90
91         Set<TenantId> tenantIds = new HashSet<>();
92         for (Endpoint ep : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
93             OfOverlayContext ofc = ep.getAugmentation(OfOverlayContext.class);
94             if (ofc == null || ofc.getNodeConnectorId() == null) {
95                 LOG.info("Endpoint {} does not contain node-connector-id. OFOverlay ignores the endpoint.",
96                         ep.getKey());
97                 continue;
98             }
99
100             tenantIds.add(ep.getTenant());
101             Set<ExternalImplicitGroup> eigs = getExternalImplicitGroupsForTenant(ep.getTenant());
102             if (EndpointManager.isInternal(ep, eigs)) {
103                 // Allow layer 3 traffic (ARP and IP) with the correct
104                 // source IP, MAC, and source port
105                 l3flow(ofWriter, nodeId, ep, ofc, 120, false);
106                 l3flow(ofWriter, nodeId, ep, ofc, 121, true);
107                 ofWriter.writeFlow(nodeId, TABLE_ID, l3DhcpDoraFlow(ep, ofc, 115));
108
109                 // Allow layer 2 traffic with the correct source MAC and
110                 // source port (note lower priority than drop IP rules)
111                 ofWriter.writeFlow(nodeId, TABLE_ID, l2flow(ep, ofc, 100));
112             } else { // EP is external
113                 if (LOG.isTraceEnabled()) {
114                     LOG.trace("External Endpoint is ignored in PortSecurity: {}", ep);
115                 }
116             }
117         }
118
119         for (TenantId tenantId : tenantIds) {
120             for (NodeConnectorId nc : ctx.getSwitchManager().getExternalPorts(nodeId)) {
121                 // TODO Bug 3546 - Difficult: External port is unrelated to Tenant, L3C, L2BD..
122                 for (Flow flow : popVlanTagsOnExternalPort(nc, tenantId, 210)) {
123                     // tagged frames have to be untagged when entering policy domain
124                     ofWriter.writeFlow(nodeId, TABLE_ID, flow);
125                 }
126                 // allowing untagged frames entering policy domain
127                 ofWriter.writeFlow(nodeId, TABLE_ID, allowFromExternalPort(nc, 200));
128             }
129         }
130     }
131
132     private Set<ExternalImplicitGroup> getExternalImplicitGroupsForTenant(TenantId tenantId) {
133         IndexedTenant tenant = ctx.getTenant(tenantId);
134         if (tenant == null) {
135             return Collections.emptySet();
136         }
137         return tenant.getExternalImplicitGroups();
138     }
139
140     private Flow allowFromPort(NodeConnectorId port) {
141         Match match = new MatchBuilder()
142                 .setInPort(port)
143                 .build();
144         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allow", match);
145         FlowBuilder flowb = base()
146                 .setId(flowid)
147                 .setPriority(300)
148                 .setMatch(match)
149                 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()));
150         return flowb.build();
151     }
152
153     private Flow l2flow(Endpoint ep, OfOverlayContext ofc, Integer priority) {
154         Match match = new MatchBuilder()
155                 .setEthernetMatch(
156                         FlowUtils.ethernetMatch(ep.getMacAddress(), null, null))
157                 .setInPort(ofc.getNodeConnectorId())
158                 .build();
159         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "L2", match);
160         FlowBuilder flowb = base()
161                 .setPriority(priority)
162                 .setId(flowid)
163                 .setMatch(match)
164                 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()));
165
166         return flowb.build();
167     }
168
169     private Flow l3DhcpDoraFlow(Endpoint ep, OfOverlayContext ofc, Integer priority) {
170
171         //TODO: Handle IPv6 DORA
172         Long etherType = FlowUtils.IPv4;
173         // DHCP DORA destination is broadcast
174         String ikey = "255.255.255.255/32";
175         Layer3Match m = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
176
177         Match match = new MatchBuilder()
178                 .setEthernetMatch(
179                         FlowUtils.ethernetMatch(ep.getMacAddress(),
180                         null,
181                         etherType))
182                 .setLayer3Match(m)
183                 .setInPort(ofc.getNodeConnectorId())
184                 .build();
185         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "dhcp", match);
186         return base()
187                 .setPriority(priority)
188                 .setId(flowid)
189                 .setMatch(match)
190                 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()))
191                 .build();
192     }
193
194     private void l3flow(OfWriter ofWriter, NodeId nodeId,
195                         Endpoint ep, OfOverlayContext ofc,
196                         Integer priority, boolean arp) {
197         if (ep.getL3Address() == null)
198             return;
199         for (L3Address l3 : ep.getL3Address()) {
200             if (l3.getIpAddress() == null)
201                 continue;
202             Layer3Match m;
203             Long etherType;
204             String ikey;
205             if (l3.getIpAddress().getIpv4Address() != null) {
206                 ikey = l3.getIpAddress().getIpv4Address().getValue() + "/32";
207                 if (arp) {
208                     m = new ArpMatchBuilder()
209                             .setArpSourceTransportAddress(new Ipv4Prefix(ikey))
210                             .build();
211                     etherType = FlowUtils.ARP;
212                 } else {
213                     m = new Ipv4MatchBuilder()
214                             .setIpv4Source(new Ipv4Prefix(ikey))
215                             .build();
216                     etherType = FlowUtils.IPv4;
217                 }
218             } else if (l3.getIpAddress().getIpv6Address() != null) {
219                 if (arp)
220                     continue;
221                 ikey = l3.getIpAddress().getIpv6Address().getValue() + "/128";
222                 m = new Ipv6MatchBuilder()
223                         .setIpv6Source(new Ipv6Prefix(ikey))
224                         .build();
225                 etherType = FlowUtils.IPv6;
226             } else {
227                 continue;
228             }
229             Match match = new MatchBuilder()
230                     .setEthernetMatch(
231                             FlowUtils.ethernetMatch(ep.getMacAddress(),
232                             null,
233                             etherType))
234                     .setLayer3Match(m)
235                     .setInPort(ofc.getNodeConnectorId())
236                     .build();
237             FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "L3", match);
238             Flow flow = base()
239                     .setPriority(priority)
240                     .setId(flowid)
241                     .setMatch(match)
242                     .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()))
243                     .build();
244
245             ofWriter.writeFlow(nodeId, TABLE_ID,flow);
246         }
247     }
248
249     private Flow allowFromExternalPort(NodeConnectorId nc, Integer priority) {
250         Match match = new MatchBuilder().setInPort(nc).build();
251         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allowExternal", match);
252         FlowBuilder flowb = base().setId(flowid)
253             .setPriority(priority)
254             .setMatch(match)
255             .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_INGRESS_NAT()));
256         return flowb.build();
257     }
258
259     /**
260      * Pops VLAN tag for inbound traffic.
261      *
262      * @param nc should be external for now
263      * @param tenantId of {@link Tenant} from which {@link L2FloodDomain}s are read from which VLAN IDs are resolved.
264      * @param priority of flows in the table
265      * @return {@link Flow}s which match on ingress port, and VLAN ID to pop.
266      *         {@link GoToTable} Instructions are set to INGRESS NAT table.
267      */
268     private List<Flow> popVlanTagsOnExternalPort(NodeConnectorId nc, TenantId tenantId, Integer priority) {
269         List<Flow> flows = new ArrayList<>();
270         if(ctx.getTenant(tenantId) != null) {
271             for (L2FloodDomain l2Fd : ctx.getTenant(tenantId).getTenant().getForwardingContext().getL2FloodDomain()) {
272                 Segmentation segmentation = l2Fd.getAugmentation(Segmentation.class);
273                 if (segmentation != null) {
274                     Integer vlanId = segmentation.getSegmentationId();
275                     flows.add(buildPopVlanFlow(nc, vlanId, priority));
276                 }
277             }
278         }
279         return flows;
280     }
281
282     private Flow buildPopVlanFlow(NodeConnectorId nc, Integer vlanId, int priority) {
283         Match match = new MatchBuilder()
284             .setVlanMatch(FlowUtils.vlanMatch(vlanId, true))
285             .setInPort(nc)
286             .build();
287         List<Instruction> instructions = new ArrayList<>();
288         instructions.add(FlowUtils.popVlanInstruction(0));
289         instructions.add(new InstructionBuilder().setOrder(1)
290              // TODO for now matches on external flows are passed to ingress nat table
291             .setInstruction(FlowUtils.gotoTableIns(ctx.getPolicyManager().getTABLEID_INGRESS_NAT()))
292             .build());
293         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allowExternalPopVlan", match);
294         return base().setPriority(priority)
295             .setId(flowid)
296             .setMatch(match)
297             .setInstructions(new InstructionsBuilder().setInstruction(instructions).build())
298             .build();
299     }
300 }