Bug5427: Added JavaDoc for OfOverlay rendeder (flow description)
[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  * <h1>Manage the table that enforces port security. Initial flows in group-based policy pipeline (table=0)</h1>
54  *
55  * Lower-priority flows are leading flows for all traffic incoming from endpoints associated to gbp classifier.<br>
56  * Created when an {@link Endpoint} is internal and contains {@link OfOverlayContext} augmentation. Several flows of
57  * this kind are produced.
58  *<p>
59  * <i>L2 flow:</i><br>
60  * Priority = 100<br>
61  * Matches:<br>
62  *      - in_port, {@link NodeConnectorId}
63  *      - dl_src {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress}<br>
64  * Actions:<br>
65  *      - {@link GoToTable} SOURCE MAPPER table
66  *<p>
67  * <i>L3 flow:</i><br>
68  * Priority = 120<br>
69  * Matches:<br>
70  *      - ip, (ethertype)<br>
71  *      - in_port, {@link NodeConnectorId}<br>
72  *      - dl_src {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress}<br>
73  *      - nw_src (source ip address)<br>
74  * Actions:<br>
75  *      - {@link GoToTable} SOURCE MAPPER table
76  *<p>
77  * <i>L3 Arp flow:</i><br>
78  * Priority = 121<br>
79  * Matches:<br>
80  *      - arp, (ethertype)<br>
81  *      - in_port, {@link NodeConnectorId}<br>
82  *      - dl_src {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress}<br>
83  *      - arp_spa (arp source transport address)<br>
84  * Actions:<br>
85  *      - {@link GoToTable} SOURCE MAPPER table
86  *<p>
87  * <i>L3 Dhcp dora flow:</i><br>
88  * Priority = 115<br>
89  * Matches:<br>
90  *      - ip, (ethertype)<br>
91  *      - in_port, {@link NodeConnectorId}<br>
92  *      - dl_src {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress}<br>
93  *      - nw_dst (destination ip address)<br>
94  * Actions:<br>
95  *      - {@link GoToTable} SOURCE MAPPER table
96  *<p>
97  * Higher-priority flows providing VLAN support for external networks. Created when node contains external ports
98  *<p>
99  * <i>Allow from external:</i><br>
100  * Priority = 200<br>
101  * Matches:<br>
102  *      - in_port, {@link NodeConnectorId}<br>
103  * Actions:<br>
104  *      - {@link GoToTable} INGRESS NAT table
105  *<p>
106  * <i>Flow that pops VLAN tag for inbound traffic:</i><br>
107  * Priority = 210<br>
108  * See {@link #popVlanTagsOnExternalPort}
109  *<p>
110  * Highest priority flows used to direct traffic coming from tunnel (SFC). These flows are created always
111  *<p>
112  * <i>Allow from tunnel:</i><br>
113  * Priority = 300<br>
114  * Matches:<br>
115  *      - in_port (has to be tunnel port), {@link NodeConnectorId}<br>
116  * Actions:<br>
117  *      - {@link GoToTable} SOURCE MAPPER table
118  *
119  */
120 public class PortSecurity extends FlowTable {
121     protected static final Logger LOG =
122             LoggerFactory.getLogger(PortSecurity.class);
123
124     public static short TABLE_ID;
125
126     public PortSecurity(OfContext ctx, short tableId) {
127         super(ctx);
128         TABLE_ID = tableId;
129     }
130
131     @Override
132     public short getTableId() {
133         return TABLE_ID;
134     }
135
136     @Override
137     public void sync(NodeId nodeId, OfWriter ofWriter) {
138
139         // Allow traffic from tunnel ports
140         NodeConnectorId vxLanTunnel = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlan.class);
141         NodeConnectorId vxLanGpeTunnel = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlanGpe.class);
142         if (vxLanTunnel != null)
143             ofWriter.writeFlow(nodeId, TABLE_ID, allowFromPort(vxLanTunnel));
144         if (vxLanGpeTunnel != null)
145             ofWriter.writeFlow(nodeId, TABLE_ID, allowFromPort(vxLanGpeTunnel));
146
147         // Default drop all
148         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(1, null, TABLE_ID));
149
150         // Drop IP traffic that doesn't match a source IP rule
151         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(110, FlowUtils.ARP, TABLE_ID));
152         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(111, FlowUtils.IPv4, TABLE_ID));
153         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(112, FlowUtils.IPv6, TABLE_ID));
154
155         Set<TenantId> tenantIds = new HashSet<>();
156         for (Endpoint ep : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
157             OfOverlayContext ofc = ep.getAugmentation(OfOverlayContext.class);
158             if (ofc == null || ofc.getNodeConnectorId() == null) {
159                 LOG.info("Endpoint {} does not contain node-connector-id. OFOverlay ignores the endpoint.",
160                         ep.getKey());
161                 continue;
162             }
163
164             tenantIds.add(ep.getTenant());
165             Set<ExternalImplicitGroup> eigs = getExternalImplicitGroupsForTenant(ep.getTenant());
166             if (EndpointManager.isInternal(ep, eigs)) {
167                 // Allow layer 3 traffic (ARP and IP) with the correct
168                 // source IP, MAC, and source port
169                 l3flow(ofWriter, nodeId, ep, ofc, 120, false);
170                 l3flow(ofWriter, nodeId, ep, ofc, 121, true);
171                 ofWriter.writeFlow(nodeId, TABLE_ID, l3DhcpDoraFlow(ep, ofc, 115));
172
173                 // Allow layer 2 traffic with the correct source MAC and
174                 // source port (note lower priority than drop IP rules)
175                 ofWriter.writeFlow(nodeId, TABLE_ID, l2flow(ep, ofc, 100));
176             } else { // EP is external
177                 if (LOG.isTraceEnabled()) {
178                     LOG.trace("External Endpoint is ignored in PortSecurity: {}", ep);
179                 }
180             }
181         }
182
183         for (TenantId tenantId : tenantIds) {
184             for (NodeConnectorId nc : ctx.getSwitchManager().getExternalPorts(nodeId)) {
185                 // TODO Bug 3546 - Difficult: External port is unrelated to Tenant, L3C, L2BD..
186                 for (Flow flow : popVlanTagsOnExternalPort(nc, tenantId, 210)) {
187                     // Tagged frames have to be untagged when entering policy domain
188                     ofWriter.writeFlow(nodeId, TABLE_ID, flow);
189                 }
190                 // Allowing untagged frames entering policy domain
191                 ofWriter.writeFlow(nodeId, TABLE_ID, allowFromExternalPort(nc, 200));
192             }
193         }
194     }
195
196     private Set<ExternalImplicitGroup> getExternalImplicitGroupsForTenant(TenantId tenantId) {
197         IndexedTenant tenant = ctx.getTenant(tenantId);
198         if (tenant == null) {
199             return Collections.emptySet();
200         }
201         return tenant.getExternalImplicitGroups();
202     }
203
204     private Flow allowFromPort(NodeConnectorId port) {
205         Match match = new MatchBuilder()
206                 .setInPort(port)
207                 .build();
208         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allow", match);
209         FlowBuilder flowb = base()
210                 .setId(flowid)
211                 .setPriority(300)
212                 .setMatch(match)
213                 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()));
214         return flowb.build();
215     }
216
217     private Flow l2flow(Endpoint ep, OfOverlayContext ofc, Integer priority) {
218         Match match = new MatchBuilder()
219                 .setEthernetMatch(
220                         FlowUtils.ethernetMatch(ep.getMacAddress(), null, null))
221                 .setInPort(ofc.getNodeConnectorId())
222                 .build();
223         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "L2", match);
224         FlowBuilder flowb = base()
225                 .setPriority(priority)
226                 .setId(flowid)
227                 .setMatch(match)
228                 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()));
229
230         return flowb.build();
231     }
232
233     private Flow l3DhcpDoraFlow(Endpoint ep, OfOverlayContext ofc, Integer priority) {
234
235         //TODO: Handle IPv6 DORA
236         Long etherType = FlowUtils.IPv4;
237         // DHCP DORA destination is broadcast
238         String ikey = "255.255.255.255/32";
239         Layer3Match m = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
240
241         Match match = new MatchBuilder()
242                 .setEthernetMatch(
243                         FlowUtils.ethernetMatch(ep.getMacAddress(),
244                         null,
245                         etherType))
246                 .setLayer3Match(m)
247                 .setInPort(ofc.getNodeConnectorId())
248                 .build();
249         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "dhcp", match);
250         return base()
251                 .setPriority(priority)
252                 .setId(flowid)
253                 .setMatch(match)
254                 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()))
255                 .build();
256     }
257
258     private void l3flow(OfWriter ofWriter, NodeId nodeId,
259                         Endpoint ep, OfOverlayContext ofc,
260                         Integer priority, boolean arp) {
261         if (ep.getL3Address() == null)
262             return;
263         for (L3Address l3 : ep.getL3Address()) {
264             if (l3.getIpAddress() == null)
265                 continue;
266             Layer3Match m;
267             Long etherType;
268             String ikey;
269             if (l3.getIpAddress().getIpv4Address() != null) {
270                 ikey = l3.getIpAddress().getIpv4Address().getValue() + "/32";
271                 if (arp) {
272                     m = new ArpMatchBuilder()
273                             .setArpSourceTransportAddress(new Ipv4Prefix(ikey))
274                             .build();
275                     etherType = FlowUtils.ARP;
276                 } else {
277                     m = new Ipv4MatchBuilder()
278                             .setIpv4Source(new Ipv4Prefix(ikey))
279                             .build();
280                     etherType = FlowUtils.IPv4;
281                 }
282             } else if (l3.getIpAddress().getIpv6Address() != null) {
283                 if (arp)
284                     continue;
285                 ikey = l3.getIpAddress().getIpv6Address().getValue() + "/128";
286                 m = new Ipv6MatchBuilder()
287                         .setIpv6Source(new Ipv6Prefix(ikey))
288                         .build();
289                 etherType = FlowUtils.IPv6;
290             } else {
291                 continue;
292             }
293             Match match = new MatchBuilder()
294                     .setEthernetMatch(
295                             FlowUtils.ethernetMatch(ep.getMacAddress(),
296                             null,
297                             etherType))
298                     .setLayer3Match(m)
299                     .setInPort(ofc.getNodeConnectorId())
300                     .build();
301             FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "L3", match);
302             Flow flow = base()
303                     .setPriority(priority)
304                     .setId(flowid)
305                     .setMatch(match)
306                     .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()))
307                     .build();
308
309             ofWriter.writeFlow(nodeId, TABLE_ID,flow);
310         }
311     }
312
313     private Flow allowFromExternalPort(NodeConnectorId nc, Integer priority) {
314         Match match = new MatchBuilder().setInPort(nc).build();
315         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allowExternal", match);
316         FlowBuilder flowb = base().setId(flowid)
317             .setPriority(priority)
318             .setMatch(match)
319             .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_INGRESS_NAT()));
320         return flowb.build();
321     }
322
323     /**
324      * Pops VLAN tag for inbound traffic.
325      *
326      * @param nc should be external for now
327      * @param tenantId of {@link Tenant} from which {@link L2FloodDomain}s are read from which VLAN IDs are resolved.
328      * @param priority of flows in the table
329      * @return {@link Flow}s which match on ingress port, and VLAN ID to pop.
330      *         {@link GoToTable} Instructions are set to INGRESS NAT table.
331      */
332     private List<Flow> popVlanTagsOnExternalPort(NodeConnectorId nc, TenantId tenantId, Integer priority) {
333         List<Flow> flows = new ArrayList<>();
334         if(ctx.getTenant(tenantId) != null) {
335             for (L2FloodDomain l2Fd : ctx.getTenant(tenantId).getTenant().getForwardingContext().getL2FloodDomain()) {
336                 Segmentation segmentation = l2Fd.getAugmentation(Segmentation.class);
337                 if (segmentation != null) {
338                     Integer vlanId = segmentation.getSegmentationId();
339                     flows.add(buildPopVlanFlow(nc, vlanId, priority));
340                 }
341             }
342         }
343         return flows;
344     }
345
346     private Flow buildPopVlanFlow(NodeConnectorId nc, Integer vlanId, int priority) {
347         Match match = new MatchBuilder()
348             .setVlanMatch(FlowUtils.vlanMatch(vlanId, true))
349             .setInPort(nc)
350             .build();
351         List<Instruction> instructions = new ArrayList<>();
352         instructions.add(FlowUtils.popVlanInstruction(0));
353         instructions.add(new InstructionBuilder().setOrder(1)
354              // TODO for now matches on external flows are passed to ingress nat table
355             .setInstruction(FlowUtils.gotoTableIns(ctx.getPolicyManager().getTABLEID_INGRESS_NAT()))
356             .build());
357         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allowExternalPopVlan", match);
358         return base().setPriority(priority)
359             .setId(flowid)
360             .setMatch(match)
361             .setInstructions(new InstructionsBuilder().setInstruction(instructions).build())
362             .build();
363     }
364 }