2 * Copyright (c) 2014 Cisco Systems, 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.groupbasedpolicy.renderer.ofoverlay.flow;
11 import java.util.Collections;
12 import java.util.List;
13 import java.util.ArrayList;
14 import java.util.HashSet;
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;
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;
53 * <h1>Manage the table that enforces port security. Initial flows in group-based policy pipeline (table=0)</h1>
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.
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>
65 * - {@link GoToTable} SOURCE MAPPER table
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>
75 * - {@link GoToTable} SOURCE MAPPER table
77 * <i>L3 Arp flow:</i><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>
85 * - {@link GoToTable} SOURCE MAPPER table
87 * <i>L3 Dhcp dora flow:</i><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>
95 * - {@link GoToTable} SOURCE MAPPER table
97 * Higher-priority flows providing VLAN support for external networks. Created when node contains external ports
99 * <i>Allow from external:</i><br>
102 * - in_port, {@link NodeConnectorId}<br>
104 * - {@link GoToTable} INGRESS NAT table
106 * <i>Flow that pops VLAN tag for inbound traffic:</i><br>
108 * See {@link #popVlanTagsOnExternalPort}
110 * Highest priority flows used to direct traffic coming from tunnel (SFC). These flows are created always
112 * <i>Allow from tunnel:</i><br>
115 * - in_port (has to be tunnel port), {@link NodeConnectorId}<br>
117 * - {@link GoToTable} SOURCE MAPPER table
120 public class PortSecurity extends FlowTable {
121 protected static final Logger LOG =
122 LoggerFactory.getLogger(PortSecurity.class);
124 public static short TABLE_ID;
126 public PortSecurity(OfContext ctx, short tableId) {
132 public short getTableId() {
137 public void sync(NodeId nodeId, OfWriter ofWriter) {
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));
148 ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(1, null, TABLE_ID));
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));
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.",
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));
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);
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);
190 // Allowing untagged frames entering policy domain
191 ofWriter.writeFlow(nodeId, TABLE_ID, allowFromExternalPort(nc, 200));
196 private Set<ExternalImplicitGroup> getExternalImplicitGroupsForTenant(TenantId tenantId) {
197 IndexedTenant tenant = ctx.getTenant(tenantId);
198 if (tenant == null) {
199 return Collections.emptySet();
201 return tenant.getExternalImplicitGroups();
204 private Flow allowFromPort(NodeConnectorId port) {
205 Match match = new MatchBuilder()
208 FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allow", match);
209 FlowBuilder flowb = base()
213 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()));
214 return flowb.build();
217 private Flow l2flow(Endpoint ep, OfOverlayContext ofc, Integer priority) {
218 Match match = new MatchBuilder()
220 FlowUtils.ethernetMatch(ep.getMacAddress(), null, null))
221 .setInPort(ofc.getNodeConnectorId())
223 FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "L2", match);
224 FlowBuilder flowb = base()
225 .setPriority(priority)
228 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()));
230 return flowb.build();
233 private Flow l3DhcpDoraFlow(Endpoint ep, OfOverlayContext ofc, Integer priority) {
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();
241 Match match = new MatchBuilder()
243 FlowUtils.ethernetMatch(ep.getMacAddress(),
247 .setInPort(ofc.getNodeConnectorId())
249 FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "dhcp", match);
251 .setPriority(priority)
254 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()))
258 private void l3flow(OfWriter ofWriter, NodeId nodeId,
259 Endpoint ep, OfOverlayContext ofc,
260 Integer priority, boolean arp) {
261 if (ep.getL3Address() == null)
263 for (L3Address l3 : ep.getL3Address()) {
264 if (l3.getIpAddress() == null)
269 if (l3.getIpAddress().getIpv4Address() != null) {
270 ikey = l3.getIpAddress().getIpv4Address().getValue() + "/32";
272 m = new ArpMatchBuilder()
273 .setArpSourceTransportAddress(new Ipv4Prefix(ikey))
275 etherType = FlowUtils.ARP;
277 m = new Ipv4MatchBuilder()
278 .setIpv4Source(new Ipv4Prefix(ikey))
280 etherType = FlowUtils.IPv4;
282 } else if (l3.getIpAddress().getIpv6Address() != null) {
285 ikey = l3.getIpAddress().getIpv6Address().getValue() + "/128";
286 m = new Ipv6MatchBuilder()
287 .setIpv6Source(new Ipv6Prefix(ikey))
289 etherType = FlowUtils.IPv6;
293 Match match = new MatchBuilder()
295 FlowUtils.ethernetMatch(ep.getMacAddress(),
299 .setInPort(ofc.getNodeConnectorId())
301 FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "L3", match);
303 .setPriority(priority)
306 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()))
309 ofWriter.writeFlow(nodeId, TABLE_ID,flow);
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)
319 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_INGRESS_NAT()));
320 return flowb.build();
324 * Pops VLAN tag for inbound traffic.
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.
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));
346 private Flow buildPopVlanFlow(NodeConnectorId nc, Integer vlanId, int priority) {
347 Match match = new MatchBuilder()
348 .setVlanMatch(FlowUtils.vlanMatch(vlanId, true))
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()))
357 FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allowExternalPopVlan", match);
358 return base().setPriority(priority)
361 .setInstructions(new InstructionsBuilder().setInstruction(instructions).build())