Fixed NAT in OFOverlay based on EIG
[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.Set;
14
15 import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
16 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
17 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
18 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.endpoint.EndpointManager;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.ExternalImplicitGroup;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * Manage the table that enforces port security
43  *
44  */
45 public class PortSecurity extends FlowTable {
46     protected static final Logger LOG =
47             LoggerFactory.getLogger(PortSecurity.class);
48
49     public static short TABLE_ID;
50
51     public PortSecurity(OfContext ctx, short tableId) {
52         super(ctx);
53         this.TABLE_ID=tableId;
54     }
55
56     @Override
57     public short getTableId() {
58         return TABLE_ID;
59     }
60
61     @Override
62     public void sync(NodeId nodeId, OfWriter ofWriter) {
63
64         // Allow traffic from tunnel ports
65         NodeConnectorId tunnelIf = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlan.class);
66         if (tunnelIf != null)
67             ofWriter.writeFlow(nodeId, TABLE_ID, allowFromPort(tunnelIf));
68
69         // Allow traffic from tunnel ports
70         //TODO Bug 3546 - Difficult: External port is unrelated to Tenant, L3C, L2BD..
71
72         Set<NodeConnectorId> external =
73                 ctx.getSwitchManager().getExternalPorts(nodeId);
74         for (NodeConnectorId extIf : external) {
75             ofWriter.writeFlow(nodeId, TABLE_ID, allowFromExternalPort(extIf));
76         }
77
78         // Default drop all
79         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(Integer.valueOf(1), null, TABLE_ID));
80
81         // Drop IP traffic that doesn't match a source IP rule
82         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(Integer.valueOf(110), FlowUtils.ARP, TABLE_ID));
83         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(Integer.valueOf(111), FlowUtils.IPv4, TABLE_ID));
84         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(Integer.valueOf(112), FlowUtils.IPv6, TABLE_ID));
85
86         for (Endpoint ep : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
87             OfOverlayContext ofc = ep.getAugmentation(OfOverlayContext.class);
88             if (ofc == null || ofc.getNodeConnectorId() == null) {
89                 LOG.info("Endpoint {} does not contain node-connector-id. OFOverlay ignores the endpoint.",
90                         ep.getKey());
91                 continue;
92             }
93
94             Set<ExternalImplicitGroup> eigs = getExternalImplicitGroupsForTenant(ep.getTenant());
95             if (EndpointManager.isInternal(ep, eigs)) {
96                 // Allow layer 3 traffic (ARP and IP) with the correct
97                 // source IP, MAC, and source port
98                 l3flow(ofWriter, nodeId, ep, ofc, 120, false);
99                 l3flow(ofWriter, nodeId, ep, ofc, 121, true);
100                 ofWriter.writeFlow(nodeId, TABLE_ID, l3DhcpDoraFlow(ep, ofc, 115));
101
102                 // Allow layer 2 traffic with the correct source MAC and
103                 // source port (note lower priority than drop IP rules)
104                 ofWriter.writeFlow(nodeId, TABLE_ID, l2flow(ep, ofc, 100));
105             } else { // EP is external
106                 if (LOG.isTraceEnabled()) {
107                     LOG.trace("External Endpoint is ignored in PortSecurity: {}", ep);
108                 }
109             }
110         }
111     }
112
113     private Set<ExternalImplicitGroup> getExternalImplicitGroupsForTenant(TenantId tenantId) {
114         IndexedTenant tenant = ctx.getTenant(tenantId);
115         if (tenant == null) {
116             return Collections.emptySet();
117         }
118         return tenant.getExternalImplicitGroups();
119     }
120
121     private Flow allowFromPort(NodeConnectorId port) {
122         Match match = new MatchBuilder()
123                 .setInPort(port)
124                 .build();
125         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allow", match);
126         FlowBuilder flowb = base()
127                 .setId(flowid)
128                 .setPriority(Integer.valueOf(200))
129                 .setMatch(match)
130                 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()));
131         return flowb.build();
132     }
133
134     private Flow allowFromExternalPort(NodeConnectorId port) {
135         Match match = new MatchBuilder()
136                 .setInPort(port)
137                 .build();
138         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allowExternal", match);
139         FlowBuilder flowb = base()
140                 .setId(flowid)
141                 .setPriority(Integer.valueOf(200))
142                 .setMatch(match)
143                 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_INGRESS_NAT()));
144         return flowb.build();
145     }
146
147     private Flow l2flow(Endpoint ep, OfOverlayContext ofc, Integer priority) {
148         Match match = new MatchBuilder()
149                 .setEthernetMatch(
150                         FlowUtils.ethernetMatch(ep.getMacAddress(), null, null))
151                 .setInPort(ofc.getNodeConnectorId())
152                 .build();
153         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "L2", match);
154         FlowBuilder flowb = base()
155                 .setPriority(priority)
156                 .setId(flowid)
157                 .setMatch(match)
158                 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()));
159
160         return flowb.build();
161     }
162
163     private Flow l3DhcpDoraFlow(Endpoint ep, OfOverlayContext ofc, Integer priority) {
164
165         //TODO: Handle IPv6 DORA
166         Long etherType = FlowUtils.IPv4;
167         // DHCP DORA destination is broadcast
168         String ikey = "255.255.255.255/32";
169         Layer3Match m = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
170
171         Match match = new MatchBuilder()
172                 .setEthernetMatch(
173                         FlowUtils.ethernetMatch(ep.getMacAddress(),
174                         null,
175                         etherType))
176                 .setLayer3Match(m)
177                 .setInPort(ofc.getNodeConnectorId())
178                 .build();
179         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "dhcp", match);
180         Flow flow = base()
181                 .setPriority(priority)
182                 .setId(flowid)
183                 .setMatch(match)
184                 .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()))
185                 .build();
186
187         return flow;
188     }
189
190     private void l3flow(OfWriter ofWriter, NodeId nodeId,
191                         Endpoint ep, OfOverlayContext ofc,
192                         Integer priority, boolean arp) {
193         if (ep.getL3Address() == null)
194             return;
195         for (L3Address l3 : ep.getL3Address()) {
196             if (l3.getIpAddress() == null)
197                 continue;
198             Layer3Match m = null;
199             Long etherType = null;
200             String ikey = null;
201             if (l3.getIpAddress().getIpv4Address() != null) {
202                 ikey = l3.getIpAddress().getIpv4Address().getValue() + "/32";
203                 if (arp) {
204                     m = new ArpMatchBuilder()
205                             .setArpSourceTransportAddress(new Ipv4Prefix(ikey))
206                             .build();
207                     etherType = FlowUtils.ARP;
208                 } else {
209                     m = new Ipv4MatchBuilder()
210                             .setIpv4Source(new Ipv4Prefix(ikey))
211                             .build();
212                     etherType = FlowUtils.IPv4;
213                 }
214             } else if (l3.getIpAddress().getIpv6Address() != null) {
215                 if (arp)
216                     continue;
217                 ikey = l3.getIpAddress().getIpv6Address().getValue() + "/128";
218                 m = new Ipv6MatchBuilder()
219                         .setIpv6Source(new Ipv6Prefix(ikey))
220                         .build();
221                 etherType = FlowUtils.IPv6;
222             } else {
223                 continue;
224             }
225             Match match = new MatchBuilder()
226                     .setEthernetMatch(
227                             FlowUtils.ethernetMatch(ep.getMacAddress(),
228                             null,
229                             etherType))
230                     .setLayer3Match(m)
231                     .setInPort(ofc.getNodeConnectorId())
232                     .build();
233             FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "L3", match);
234             Flow flow = base()
235                     .setPriority(priority)
236                     .setId(flowid)
237                     .setMatch(match)
238                     .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()))
239                     .build();
240
241             ofWriter.writeFlow(nodeId, TABLE_ID,flow);
242         }
243     }
244 }