blob: ae97a5498105f0b9ffda857c5704a9c0912593e4 [file] [log] [blame]
Carmelo Casconeb5324e72018-11-25 02:26:32 -08001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Carmelo Cascone36d5e7a2019-09-25 01:02:53 -070017package org.onosproject.pipelines.fabric.impl.behaviour.pipeliner;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080018
19import com.google.common.collect.Lists;
20import org.onlab.packet.Ethernet;
21import org.onlab.packet.MacAddress;
22import org.onlab.packet.VlanId;
23import org.onosproject.net.DeviceId;
24import org.onosproject.net.PortNumber;
25import org.onosproject.net.flow.DefaultTrafficSelector;
26import org.onosproject.net.flow.DefaultTrafficTreatment;
27import org.onosproject.net.flow.FlowRule;
28import org.onosproject.net.flow.TrafficSelector;
29import org.onosproject.net.flow.TrafficTreatment;
30import org.onosproject.net.flow.criteria.Criterion;
31import org.onosproject.net.flow.criteria.EthCriterion;
32import org.onosproject.net.flow.criteria.PiCriterion;
33import org.onosproject.net.flow.criteria.PortCriterion;
34import org.onosproject.net.flow.criteria.VlanIdCriterion;
Daniele Moro15a7c122019-10-29 18:45:33 -070035import org.onosproject.net.flow.instructions.Instructions;
36import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080037import org.onosproject.net.flowobjective.FilteringObjective;
Daniele Moro15a7c122019-10-29 18:45:33 -070038import org.onosproject.net.flowobjective.Objective;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080039import org.onosproject.net.flowobjective.ObjectiveError;
40import org.onosproject.net.pi.runtime.PiAction;
41import org.onosproject.net.pi.runtime.PiActionParam;
Carmelo Cascone36d5e7a2019-09-25 01:02:53 -070042import org.onosproject.pipelines.fabric.impl.behaviour.FabricCapabilities;
43import org.onosproject.pipelines.fabric.impl.behaviour.FabricConstants;
Daniele Moro15a7c122019-10-29 18:45:33 -070044import org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080045
46import java.util.Collection;
47import java.util.List;
48
49import static java.lang.String.format;
Daniele Moro15a7c122019-10-29 18:45:33 -070050import static org.onosproject.net.flow.criteria.Criterion.Type.INNER_VLAN_VID;
51import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
Carmelo Cascone36d5e7a2019-09-25 01:02:53 -070052import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.criterion;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080053
54/**
55 * ObjectiveTranslator implementation for FilteringObjective.
56 */
57class FilteringObjectiveTranslator
58 extends AbstractObjectiveTranslator<FilteringObjective> {
59
60 // Forwarding types from fabric.p4.
61 static final byte FWD_MPLS = 1;
62 static final byte FWD_IPV4_ROUTING = 2;
63 static final byte FWD_IPV6_ROUTING = 3;
64
65 private static final byte[] ONE = new byte[]{1};
66 private static final byte[] ZERO = new byte[]{0};
67
Daniele Moro693d76f2019-09-24 14:34:07 -070068 private static final short ETH_TYPE_EXACT_MASK = (short) 0xFFFF;
69
Carmelo Casconeb5324e72018-11-25 02:26:32 -080070 private static final PiAction DENY = PiAction.builder()
71 .withId(FabricConstants.FABRIC_INGRESS_FILTERING_DENY)
72 .build();
73
Carmelo Casconeb5324e72018-11-25 02:26:32 -080074
75 FilteringObjectiveTranslator(DeviceId deviceId, FabricCapabilities capabilities) {
76 super(deviceId, capabilities);
77 }
78
79 @Override
80 public ObjectiveTranslation doTranslate(FilteringObjective obj)
81 throws FabricPipelinerException {
82
83 final ObjectiveTranslation.Builder resultBuilder =
84 ObjectiveTranslation.builder();
85
86 if (obj.key() == null || obj.key().type() != Criterion.Type.IN_PORT) {
87 throw new FabricPipelinerException(
88 format("Unsupported or missing filtering key: key=%s", obj.key()),
89 ObjectiveError.BADPARAMS);
90 }
91
92 final PortCriterion inPort = (PortCriterion) obj.key();
Daniele Moro7c3a0022019-07-12 13:38:34 -070093
94 final VlanIdCriterion outerVlan = (VlanIdCriterion) criterion(
Carmelo Casconeb5324e72018-11-25 02:26:32 -080095 obj.conditions(), Criterion.Type.VLAN_VID);
Daniele Moro7c3a0022019-07-12 13:38:34 -070096 final VlanIdCriterion innerVlan = (VlanIdCriterion) criterion(
97 obj.conditions(), Criterion.Type.INNER_VLAN_VID);
Carmelo Casconeb5324e72018-11-25 02:26:32 -080098 final EthCriterion ethDst = (EthCriterion) criterion(
99 obj.conditions(), Criterion.Type.ETH_DST);
100 final EthCriterion ethDstMasked = (EthCriterion) criterion(
101 obj.conditions(), Criterion.Type.ETH_DST_MASKED);
102
Daniele Moro7c3a0022019-07-12 13:38:34 -0700103 ingressPortVlanRule(obj, inPort, outerVlan, innerVlan, resultBuilder);
Daniele Moro15a7c122019-10-29 18:45:33 -0700104 if (shouldAddFwdClassifierRule(obj)) {
105 fwdClassifierRules(obj, inPort, ethDst, ethDstMasked, resultBuilder);
106 } else {
107 log.debug("Skipping fwd classifier rules for device {}.", deviceId);
108 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800109 return resultBuilder.build();
110 }
111
Daniele Moro15a7c122019-10-29 18:45:33 -0700112 private boolean shouldAddFwdClassifierRule(FilteringObjective obj) {
113 // NOTE: in fabric pipeline the forwarding classifier acts similarly
114 // to the TMAC table of OFDPA that matches on input port.
115
116 // Forwarding classifier rules should be added to translation when:
117 // - the operation is ADD OR
118 // - it doesn't refer to double tagged traffic OR
119 // - it refers to double tagged traffic
120 // and SR is triggering the removal of forwarding classifier rules.
121 return obj.op() == Objective.Operation.ADD ||
122 !isDoubleTagged(obj) ||
123 (isDoubleTagged(obj) && isLastDoubleTaggedForPort(obj));
124 }
125
126 /**
127 * Check if the given filtering objective is the last filtering objective
128 * for a double-tagged host for a specific port.
129 * <p>
130 * {@see org.onosproject.segmentrouting.RoutingRulePopulator#buildDoubleTaggedFilteringObj()}
131 * {@see org.onosproject.segmentrouting.RoutingRulePopulator#processDoubleTaggedFilter()}
132 *
133 * @param obj Filtering objective to check.
134 * @return True if SR is signaling to remove the forwarding classifier rule,
135 * false otherwise.
136 */
137 private boolean isLastDoubleTaggedForPort(FilteringObjective obj) {
138 Instructions.MetadataInstruction meta = obj.meta().writeMetadata();
139 // SR is setting this metadata when a double tagged filtering objective
140 // is removed and no other hosts is sharing the same input port.
141 return (meta != null && (meta.metadata() & meta.metadataMask()) == 1);
142 }
143
144 private boolean isDoubleTagged(FilteringObjective obj) {
145 return obj.meta() != null &&
146 FabricUtils.l2Instruction(obj.meta(), L2ModificationInstruction.L2SubType.VLAN_POP) != null &&
147 FabricUtils.criterion(obj.conditions(), VLAN_VID) != null &&
148 FabricUtils.criterion(obj.conditions(), INNER_VLAN_VID) != null;
149 }
150
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800151 private void ingressPortVlanRule(
152 FilteringObjective obj,
153 Criterion inPortCriterion,
Daniele Moro7c3a0022019-07-12 13:38:34 -0700154 VlanIdCriterion outerVlanCriterion,
155 VlanIdCriterion innerVlanCriterion,
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800156 ObjectiveTranslation.Builder resultBuilder)
157 throws FabricPipelinerException {
158
Daniele Moro7c3a0022019-07-12 13:38:34 -0700159 final boolean outerVlanValid = outerVlanCriterion != null
160 && !outerVlanCriterion.vlanId().equals(VlanId.NONE);
161 final boolean innerVlanValid = innerVlanCriterion != null
162 && !innerVlanCriterion.vlanId().equals(VlanId.NONE);
163
Daniele Morodd0568b2019-11-01 14:01:46 -0700164 if (innerVlanValid && !capabilities.supportDoubleVlanTerm()) {
165 throw new FabricPipelinerException(
166 "Found 2 VLAN IDs, but the pipeline does not support double VLAN termination",
167 ObjectiveError.UNSUPPORTED);
168 }
169
Daniele Moro7c3a0022019-07-12 13:38:34 -0700170 final PiCriterion piCriterion = PiCriterion.builder()
171 .matchExact(FabricConstants.HDR_VLAN_IS_VALID, outerVlanValid ? ONE : ZERO)
172 .build();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800173
174 final TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
175 .add(inPortCriterion)
Daniele Moro7c3a0022019-07-12 13:38:34 -0700176 .add(piCriterion);
177 if (outerVlanValid) {
178 selector.add(outerVlanCriterion);
179 }
180 if (innerVlanValid) {
181 selector.add(innerVlanCriterion);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800182 }
183
184 final TrafficTreatment treatment;
185 if (obj.type().equals(FilteringObjective.Type.DENY)) {
186 treatment = DefaultTrafficTreatment.builder()
187 .piTableAction(DENY)
188 .build();
189 } else {
190 treatment = obj.meta() == null
191 ? DefaultTrafficTreatment.emptyTreatment() : obj.meta();
192 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800193 resultBuilder.addFlowRule(flowRule(
194 obj, FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN,
195 selector.build(), treatment));
196 }
197
198 private void fwdClassifierRules(
199 FilteringObjective obj,
200 PortCriterion inPortCriterion,
201 EthCriterion ethDstCriterion,
202 EthCriterion ethDstMaskedCriterion,
203 ObjectiveTranslation.Builder resultBuilder)
204 throws FabricPipelinerException {
205
206 final List<FlowRule> flowRules = Lists.newArrayList();
207
208 final PortNumber inPort = inPortCriterion.port();
209 if (ethDstCriterion == null) {
210 if (ethDstMaskedCriterion == null) {
211 // No match. Do bridging (default action).
212 return;
213 }
214 // Masked fwd classifier rule
215 final MacAddress dstMac = ethDstMaskedCriterion.mac();
216 final MacAddress dstMacMask = ethDstMaskedCriterion.mask();
217 flowRules.add(maskedFwdClassifierRule(inPort, dstMac, dstMacMask, obj));
218 } else {
219 final MacAddress dstMac = ethDstCriterion.mac();
220 flowRules.addAll(ipFwdClassifierRules(inPort, dstMac, obj));
Daniele Moro693d76f2019-09-24 14:34:07 -0700221 flowRules.addAll(mplsFwdClassifierRules(inPort, dstMac, obj));
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800222 }
223
224 for (FlowRule f : flowRules) {
225 resultBuilder.addFlowRule(f);
226 }
227 }
228
229 private FlowRule maskedFwdClassifierRule(
230 PortNumber inPort, MacAddress dstMac, MacAddress dstMacMask,
231 FilteringObjective obj)
232 throws FabricPipelinerException {
233 final TrafficTreatment treatment;
234 final short ethType;
235 if (dstMac.equals(MacAddress.IPV4_MULTICAST)
236 && dstMacMask.equals(MacAddress.IPV4_MULTICAST_MASK)) {
237 treatment = fwdClassifierTreatment(FWD_IPV4_ROUTING);
238 ethType = Ethernet.TYPE_IPV4;
239 } else if (dstMac.equals(MacAddress.IPV6_MULTICAST)
240 && dstMacMask.equals(MacAddress.IPV6_MULTICAST_MASK)) {
241 treatment = fwdClassifierTreatment(FWD_IPV6_ROUTING);
242 ethType = Ethernet.TYPE_IPV6;
243 } else {
244 throw new FabricPipelinerException(format(
245 "Unsupported masked Ethernet address for fwd " +
246 "classifier rule (mac=%s, mask=%s)",
247 dstMac, dstMacMask));
248 }
249 return fwdClassifierRule(inPort, ethType, dstMac, dstMacMask, treatment, obj);
250 }
251
252 private Collection<FlowRule> ipFwdClassifierRules(
253 PortNumber inPort, MacAddress dstMac, FilteringObjective obj)
254 throws FabricPipelinerException {
255 final Collection<FlowRule> flowRules = Lists.newArrayList();
256 flowRules.add(fwdClassifierRule(
257 inPort, Ethernet.TYPE_IPV4, dstMac, null,
258 fwdClassifierTreatment(FWD_IPV4_ROUTING), obj));
259 flowRules.add(fwdClassifierRule(
260 inPort, Ethernet.TYPE_IPV6, dstMac, null,
261 fwdClassifierTreatment(FWD_IPV6_ROUTING), obj));
262 return flowRules;
263 }
264
Daniele Moro693d76f2019-09-24 14:34:07 -0700265 private Collection<FlowRule> mplsFwdClassifierRules(
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800266 PortNumber inPort, MacAddress dstMac, FilteringObjective obj)
267 throws FabricPipelinerException {
Daniele Moro693d76f2019-09-24 14:34:07 -0700268 // Forwarding classifier for MPLS is composed of 2 rules
269 // with higher priority wrt standard forwarding classifier rules,
270 // this is due to overlap on ternary matching.
271 TrafficTreatment treatment = fwdClassifierTreatment(FWD_MPLS);
272 final PiCriterion ethTypeMplsIpv4 = PiCriterion.builder()
273 .matchTernary(FabricConstants.HDR_ETH_TYPE,
274 Ethernet.MPLS_UNICAST, ETH_TYPE_EXACT_MASK)
275 .matchExact(FabricConstants.HDR_IP_ETH_TYPE,
276 Ethernet.TYPE_IPV4)
277 .build();
278 final TrafficSelector selectorMplsIpv4 = DefaultTrafficSelector.builder()
279 .matchInPort(inPort)
280 .matchPi(ethTypeMplsIpv4)
281 .matchEthDstMasked(dstMac, MacAddress.EXACT_MASK)
282 .build();
283
284 final PiCriterion ethTypeMplsIpv6 = PiCriterion.builder()
285 .matchTernary(FabricConstants.HDR_ETH_TYPE,
286 Ethernet.MPLS_UNICAST, ETH_TYPE_EXACT_MASK)
287 .matchExact(FabricConstants.HDR_IP_ETH_TYPE,
288 Ethernet.TYPE_IPV6)
289 .build();
290 final TrafficSelector selectorMplsIpv6 = DefaultTrafficSelector.builder()
291 .matchInPort(inPort)
292 .matchPi(ethTypeMplsIpv6)
293 .matchEthDstMasked(dstMac, MacAddress.EXACT_MASK)
294 .build();
295
296 return List.of(
297 flowRule(obj, FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER,
298 selectorMplsIpv4, treatment, obj.priority() + 1),
299 flowRule(obj, FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER,
300 selectorMplsIpv6, treatment, obj.priority() + 1)
301 );
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800302 }
303
304 private FlowRule fwdClassifierRule(
305 PortNumber inPort, short ethType, MacAddress dstMac, MacAddress dstMacMask,
306 TrafficTreatment treatment, FilteringObjective obj)
307 throws FabricPipelinerException {
Daniele Moro693d76f2019-09-24 14:34:07 -0700308 // Match on ip_eth_type that is the eth_type of the L3 protocol.
309 // i.e., if the packet has an IP header, ip_eth_type should
310 // contain the corresponding eth_type (for IPv4 or IPv6)
311 final PiCriterion ethTypeCriterion = PiCriterion.builder()
312 .matchExact(FabricConstants.HDR_IP_ETH_TYPE, ethType)
313 .build();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800314 final TrafficSelector selector = DefaultTrafficSelector.builder()
315 .matchInPort(inPort)
Daniele Moro693d76f2019-09-24 14:34:07 -0700316 .matchPi(ethTypeCriterion)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800317 .matchEthDstMasked(dstMac, dstMacMask == null
318 ? MacAddress.EXACT_MASK : dstMacMask)
319 .build();
320 return flowRule(
321 obj, FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER,
322 selector, treatment);
323 }
324
325 private TrafficTreatment fwdClassifierTreatment(byte fwdType) {
326 final PiActionParam param = new PiActionParam(FabricConstants.FWD_TYPE, fwdType);
327 final PiAction action = PiAction.builder()
328 .withId(FabricConstants.FABRIC_INGRESS_FILTERING_SET_FORWARDING_TYPE)
329 .withParameter(param)
330 .build();
331 return DefaultTrafficTreatment.builder()
332 .piTableAction(action)
333 .build();
334
335 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800336}