Bug 5428: Ovsdb Termination Point is null issue
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / flow / ExternalMapper.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 static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxRegMatch;
12 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.applyActionIns;
13 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.instructions;
14 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxOutputRegAction;
15
16 import java.math.BigInteger;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.List;
20
21 import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
22 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
23 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
24 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.l3endpoint.rev151217.NatAddress;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.Segmentation;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2FloodDomain;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.Subnet;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
43 import org.apache.commons.net.util.SubnetUtils;
44 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 import com.google.common.base.Preconditions;
49
50 /**
51  * <h1>Manage the table that assigns source endpoint group, bridge domain, and
52  * router domain to registers to be used by other tables</h1>
53  *
54  * <i>Push VLAN flow</i><br>
55  * Priority = 222<br>
56  * see {@link #buildPushVlanFlow(Ipv4Address, Integer, int)}<br>
57  * Matches:<br>
58  *      - ethernet type<br>
59  *      - L3 match<br>
60  *      - VLAN match<br>
61  * Actions:<br>
62  *      - set_ethertype (VLAN)<br>
63  *      - output:port (Reg7) {@link NxmNxReg7}<br>
64  * <p>
65  * <i>Push VLAN flow - external domain</i><br>
66  * Priority = 220<br>
67  * see {@link #buildPushVlanFlow(NodeId, int, Integer, int)}<br>
68  * Matches:<br>
69  *      - ethernet type<br>
70  *      - Reg7 {@link NxmNxReg7}<br>
71  *      - Reg5 {@link NxmNxReg5}<br>
72  *      - VLAN match<br>
73  * Actions:<br>
74  *      - set_ethertype (VLAN)<br>
75  *      - output:port (Reg7) {@link NxmNxReg7}<br>
76  * <p>
77  * <i>Default flow</i><br>
78  * Priority = 100<br>
79  * Matches:<br>
80  *      - none<br>
81  * Actions:<br>
82  *      - output:port (Reg7) {@link NxmNxReg7}<br>
83  */
84 public class ExternalMapper extends FlowTable {
85
86     protected static final Logger LOG = LoggerFactory.getLogger(ExternalMapper.class);
87
88     public static short TABLE_ID;
89
90     public ExternalMapper(OfContext ctx, short tableId) {
91         super(ctx);
92         TABLE_ID = tableId;
93     }
94
95     @Override
96     public short getTableId() {
97         return TABLE_ID;
98     }
99
100     @Override
101     public void sync(NodeId nodeId, OfWriter ofWriter) throws Exception {
102
103         // Default drop all
104         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(Integer.valueOf(1), null, TABLE_ID));
105
106         /*
107          * When source address was translated to NAT address, it has to be figured out
108          * whether or not the traffic should be tagged since external interfaces are
109          * considered as trunk ports.
110          *
111          * Subnet to which NAT address belong have to be found so that
112          * corresponding L2FloodDomain can be resolved.
113          *
114          * If the L2FloodDomain contains Segmentation augmentation, a Flow is generated
115          * for applying VLAN tag against traffic with NAT IP as source address.
116          *
117          * Note: NetworkContainment of NAT EndpointL3 point's to subnet of original address.
118          * This is why subnet of NAT IP is resolved here.
119          */
120         Collection<EndpointL3> natL3Endpoints = ctx.getEndpointManager().getL3EndpointsWithNat();
121         if (natL3Endpoints != null) {
122             for (EndpointL3 natL3Ep : natL3Endpoints) {
123                 IpAddress natIpAddress = Preconditions.checkNotNull(natL3Ep.getAugmentation(NatAddress.class),
124                         "NAT address augmentation is missing for NAT endpoint: [{}].", natL3Ep.getKey())
125                     .getNatAddress();
126                 Subnet natIpSubnet = resolveSubnetForIpv4Address(ctx.getTenant(natL3Ep.getTenant()),
127                         Preconditions.checkNotNull(natIpAddress.getIpv4Address(),
128                                 "Endpoint {} does not have IPv4 address in NatAddress augmentation.", natL3Ep.getKey()));
129                 if (natIpSubnet != null && natIpSubnet.getParent() != null) {
130                     L2FloodDomain natEpl2Fd = ctx.getTenant(natL3Ep.getTenant()).resolveL2FloodDomain(
131                             natIpSubnet.getParent());
132                     if (natEpl2Fd.getAugmentation(Segmentation.class) != null) {
133                         Integer vlanId = natEpl2Fd.getAugmentation(Segmentation.class).getSegmentationId();
134                         ofWriter.writeFlow(nodeId, TABLE_ID,
135                                 buildPushVlanFlow(natIpAddress.getIpv4Address(), vlanId, 222));
136                     }
137                 }
138             }
139         }
140
141         /*
142          * Tagging should be also considered when traffic is routed or switched to external domain.
143          *
144          * If the L2FloodDomain of Endpoint contains Segmentation augmentation, a Flow
145          * for applying VLAN tag is generated. The flow matches against REG5 holding
146          * the L2FloodDomain and REG7 holding value of an external interface.
147          */
148         for (Endpoint ep : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
149             L2FloodDomain l2Fd = ctx.getTenant(ep.getTenant()).resolveL2FloodDomain(ep.getNetworkContainment());
150             Segmentation segmentation = l2Fd.getAugmentation(Segmentation.class);
151             if (segmentation == null) {
152                 continue;
153             }
154             Integer vlanId = segmentation.getSegmentationId();
155             for (Flow flow : buildPushVlanFlow(nodeId, OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, ep).getFdId(),
156                     vlanId, 220)) {
157                 ofWriter.writeFlow(nodeId, TABLE_ID, flow);
158             }
159         }
160
161         /*
162          *  Default Egress flow. Other methods may write to this table to augment egress
163          *  functionality, such as bypassing/utilising the NAT table, or ServiceFunctionChaining
164          */
165         ofWriter.writeFlow(nodeId, TABLE_ID, defaultFlow());
166     }
167
168     /**
169      * Generates a {@link Flow} for tagging VLAN traffic based on given arguments.
170      *
171      * @param ipv4Address source IPv4 address
172      * @param vlanId ID of VLAN tag to apply
173      * @param priority priority of the flow in the table
174      * @return {@link Flow} matching IPv4 source address, IPv4 ether-type and VLAN not set.
175      */
176     private Flow buildPushVlanFlow(Ipv4Address ipv4Address, Integer vlanId, int priority) {
177         // It is not needed here to match against external interfaces because
178         // we only use NAT when going to external networks.
179         Ipv4Prefix natIp = new Ipv4Prefix(ipv4Address.getValue() + "/32");
180         Match match = new MatchBuilder()
181             .setEthernetMatch(FlowUtils.ethernetMatch(null, null, Long.valueOf(FlowUtils.IPv4)))
182             .setLayer3Match(new Ipv4MatchBuilder().setIpv4Source(natIp).build())
183             .setVlanMatch(FlowUtils.vlanMatch(0, false))
184             .build();
185         List<ActionBuilder> pushVlanActions = new ArrayList<>();
186         pushVlanActions.addAll(FlowUtils.pushVlanActions(vlanId));
187         pushVlanActions.add(new ActionBuilder().setOrder(0).setAction(nxOutputRegAction(NxmNxReg7.class)));
188         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "external_nat_push_vlan", match);
189         return base().setPriority(priority)
190             .setId(flowid)
191             .setMatch(match)
192             .setInstructions(FlowUtils.instructions(applyActionIns(pushVlanActions)))
193             .build();
194     }
195
196     public static Subnet resolveSubnetForIpv4Address(IndexedTenant t, Ipv4Address ipv4Addr) {
197         Preconditions.checkNotNull(ipv4Addr);
198         if (t == null || t.getTenant() == null || t.getTenant().getForwardingContext() == null) {
199             return null;
200         }
201         List<Subnet> subnets = t.getTenant().getForwardingContext().getSubnet();
202         if (subnets != null) {
203             for (Subnet subnet : subnets) {
204                 if (belongsToSubnet(ipv4Addr, subnet.getIpPrefix().getIpv4Prefix())) {
205                     return subnet;
206                 }
207             }
208         }
209         return null;
210     }
211
212     private static boolean belongsToSubnet(Ipv4Address ipv4Address, Ipv4Prefix subnetPrefix) {
213         SubnetUtils su = new SubnetUtils(subnetPrefix.getValue());
214         SubnetInfo si = su.getInfo();
215         return si.isInRange(ipv4Address.getValue());
216     }
217
218     /**
219      * Generates a {@link Flow} for tagging VLAN traffic based on given arguments.
220      *
221      * @param nodeId of {@link Node} from which external interfaces are resolved
222      * @param fdId {@link L2FloodDomain} ordinal to match against
223      * @param vlanId applied to the traffic
224      * @param priority of flow in the table
225      * @return {@link List} of {@link Flow} matching {@link L2FloodDomain} in REG5,
226      * external interfaces of {@link Node} in REG7 and VLAN not set.
227      */
228     private List<Flow> buildPushVlanFlow(NodeId nodeId, int fdId, Integer vlanId, int priority) {
229         List<Flow> flows = new ArrayList<>();
230         for (Long portNum : ctx.getSwitchManager().getExternalPortNumbers(nodeId)) {
231             MatchBuilder mb = new MatchBuilder().setVlanMatch(FlowUtils.vlanMatch(0, false));
232             addNxRegMatch(mb, RegMatch.of(NxmNxReg7.class, BigInteger.valueOf(portNum).longValue()),
233                     RegMatch.of(NxmNxReg5.class, Long.valueOf(fdId)));
234             Match match = mb.build();
235             List<ActionBuilder> pushVlanActions = new ArrayList<>();
236             pushVlanActions.addAll(FlowUtils.pushVlanActions(vlanId));
237             pushVlanActions.add(new ActionBuilder().setOrder(0).setAction(nxOutputRegAction(NxmNxReg7.class)));
238             FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "external_push_vlan", match);
239             flows.add(base().setPriority(priority)
240                 .setId(flowid)
241                 .setMatch(match)
242                 .setInstructions(FlowUtils.instructions(applyActionIns(pushVlanActions)))
243                 .build());
244         }
245         return flows;
246     }
247
248     private Flow defaultFlow() {
249         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "defaultExternalFlow", null);
250         Flow flow = base().setPriority(100)
251             .setId(flowid)
252             .setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))))
253             .build();
254         return flow;
255     }
256 }