blob: 39d100e4a99c83ff2c286a6c83c269333fec626c [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;
Carmelo Cascone8f6b5cd2020-06-30 18:56:12 -070043import org.onosproject.pipelines.fabric.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
pierventref29d97b2021-01-08 17:35:47 +010074 private static final int INTERFACE_CONFIG_UPDATE = 2;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080075
76 FilteringObjectiveTranslator(DeviceId deviceId, FabricCapabilities capabilities) {
77 super(deviceId, capabilities);
78 }
79
80 @Override
81 public ObjectiveTranslation doTranslate(FilteringObjective obj)
82 throws FabricPipelinerException {
83
84 final ObjectiveTranslation.Builder resultBuilder =
85 ObjectiveTranslation.builder();
86
87 if (obj.key() == null || obj.key().type() != Criterion.Type.IN_PORT) {
88 throw new FabricPipelinerException(
89 format("Unsupported or missing filtering key: key=%s", obj.key()),
90 ObjectiveError.BADPARAMS);
91 }
92
93 final PortCriterion inPort = (PortCriterion) obj.key();
Daniele Moro7c3a0022019-07-12 13:38:34 -070094
95 final VlanIdCriterion outerVlan = (VlanIdCriterion) criterion(
Carmelo Casconeb5324e72018-11-25 02:26:32 -080096 obj.conditions(), Criterion.Type.VLAN_VID);
Daniele Moro7c3a0022019-07-12 13:38:34 -070097 final VlanIdCriterion innerVlan = (VlanIdCriterion) criterion(
98 obj.conditions(), Criterion.Type.INNER_VLAN_VID);
Carmelo Casconeb5324e72018-11-25 02:26:32 -080099 final EthCriterion ethDst = (EthCriterion) criterion(
100 obj.conditions(), Criterion.Type.ETH_DST);
101 final EthCriterion ethDstMasked = (EthCriterion) criterion(
102 obj.conditions(), Criterion.Type.ETH_DST_MASKED);
103
Daniele Moro7c3a0022019-07-12 13:38:34 -0700104 ingressPortVlanRule(obj, inPort, outerVlan, innerVlan, resultBuilder);
pierventref29d97b2021-01-08 17:35:47 +0100105 if (shouldModifyFwdClassifierTable(obj)) {
Daniele Moro15a7c122019-10-29 18:45:33 -0700106 fwdClassifierRules(obj, inPort, ethDst, ethDstMasked, resultBuilder);
107 } else {
108 log.debug("Skipping fwd classifier rules for device {}.", deviceId);
109 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800110 return resultBuilder.build();
111 }
112
pierventref29d97b2021-01-08 17:35:47 +0100113 private boolean shouldModifyFwdClassifierTable(FilteringObjective obj) {
Daniele Moro15a7c122019-10-29 18:45:33 -0700114 // NOTE: in fabric pipeline the forwarding classifier acts similarly
115 // to the TMAC table of OFDPA that matches on input port.
pierventref29d97b2021-01-08 17:35:47 +0100116 // NOTE: that SR signals when it is a port update event by not setting
117 // the INTERFACE_CONFIG_UPDATE metadata. During the INTERFACE_CONFIG_UPDATE
118 // there is no need to add/remove rules in the fwd_classifier table.
119 // NOTE: that in scenarios like (T, N) -> T where we remove only the native
120 // VLAN there is not an ADD following the remove.
Daniele Moro15a7c122019-10-29 18:45:33 -0700121
pierventref29d97b2021-01-08 17:35:47 +0100122 // Forwarding classifier rules should be added/removed to translation when:
123 // - the operation is ADD
124 // AND it is a port update event (ADD or UPDATE) OR
125 // - it doesn't refer to double tagged traffic
126 // AND it is a port REMOVE event OR
Daniele Moro15a7c122019-10-29 18:45:33 -0700127 // - it refers to double tagged traffic
128 // and SR is triggering the removal of forwarding classifier rules.
pierventref29d97b2021-01-08 17:35:47 +0100129 return (obj.op() == Objective.Operation.ADD && !isInterfaceConfigUpdate(obj)) ||
130 (!isDoubleTagged(obj) && !isInterfaceConfigUpdate(obj)) ||
Daniele Moro15a7c122019-10-29 18:45:33 -0700131 (isDoubleTagged(obj) && isLastDoubleTaggedForPort(obj));
132 }
133
134 /**
pierventref29d97b2021-01-08 17:35:47 +0100135 * Check if the given filtering objective is triggered by a interface config change.
136 *
137 * @param obj Filtering objective to check.
138 * @return True if SR is signaling to not remove the forwarding classifier rule,
139 * false otherwise.
140 */
141 private boolean isInterfaceConfigUpdate(FilteringObjective obj) {
142 if (obj.meta() == null) {
143 return false;
144 }
145 Instructions.MetadataInstruction meta = obj.meta().writeMetadata();
146 // SR is setting this metadata when an interface config update has
147 // been performed and thus fwd classifier rules should not be removed
148 return (meta != null && (meta.metadata() & meta.metadataMask()) == INTERFACE_CONFIG_UPDATE);
149 }
150
151 /**
Daniele Moro15a7c122019-10-29 18:45:33 -0700152 * Check if the given filtering objective is the last filtering objective
153 * for a double-tagged host for a specific port.
154 * <p>
155 * {@see org.onosproject.segmentrouting.RoutingRulePopulator#buildDoubleTaggedFilteringObj()}
156 * {@see org.onosproject.segmentrouting.RoutingRulePopulator#processDoubleTaggedFilter()}
157 *
158 * @param obj Filtering objective to check.
159 * @return True if SR is signaling to remove the forwarding classifier rule,
160 * false otherwise.
161 */
162 private boolean isLastDoubleTaggedForPort(FilteringObjective obj) {
163 Instructions.MetadataInstruction meta = obj.meta().writeMetadata();
164 // SR is setting this metadata when a double tagged filtering objective
165 // is removed and no other hosts is sharing the same input port.
166 return (meta != null && (meta.metadata() & meta.metadataMask()) == 1);
167 }
168
169 private boolean isDoubleTagged(FilteringObjective obj) {
170 return obj.meta() != null &&
171 FabricUtils.l2Instruction(obj.meta(), L2ModificationInstruction.L2SubType.VLAN_POP) != null &&
172 FabricUtils.criterion(obj.conditions(), VLAN_VID) != null &&
173 FabricUtils.criterion(obj.conditions(), INNER_VLAN_VID) != null;
174 }
175
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800176 private void ingressPortVlanRule(
177 FilteringObjective obj,
178 Criterion inPortCriterion,
Daniele Moro7c3a0022019-07-12 13:38:34 -0700179 VlanIdCriterion outerVlanCriterion,
180 VlanIdCriterion innerVlanCriterion,
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800181 ObjectiveTranslation.Builder resultBuilder)
182 throws FabricPipelinerException {
183
Daniele Moro7c3a0022019-07-12 13:38:34 -0700184 final boolean outerVlanValid = outerVlanCriterion != null
185 && !outerVlanCriterion.vlanId().equals(VlanId.NONE);
186 final boolean innerVlanValid = innerVlanCriterion != null
187 && !innerVlanCriterion.vlanId().equals(VlanId.NONE);
188
Daniele Morodd0568b2019-11-01 14:01:46 -0700189 if (innerVlanValid && !capabilities.supportDoubleVlanTerm()) {
190 throw new FabricPipelinerException(
191 "Found 2 VLAN IDs, but the pipeline does not support double VLAN termination",
192 ObjectiveError.UNSUPPORTED);
193 }
194
Daniele Moro7c3a0022019-07-12 13:38:34 -0700195 final PiCriterion piCriterion = PiCriterion.builder()
196 .matchExact(FabricConstants.HDR_VLAN_IS_VALID, outerVlanValid ? ONE : ZERO)
197 .build();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800198
199 final TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
200 .add(inPortCriterion)
Daniele Moro7c3a0022019-07-12 13:38:34 -0700201 .add(piCriterion);
202 if (outerVlanValid) {
203 selector.add(outerVlanCriterion);
204 }
205 if (innerVlanValid) {
206 selector.add(innerVlanCriterion);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800207 }
208
209 final TrafficTreatment treatment;
210 if (obj.type().equals(FilteringObjective.Type.DENY)) {
211 treatment = DefaultTrafficTreatment.builder()
212 .piTableAction(DENY)
213 .build();
214 } else {
215 treatment = obj.meta() == null
216 ? DefaultTrafficTreatment.emptyTreatment() : obj.meta();
217 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800218 resultBuilder.addFlowRule(flowRule(
219 obj, FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN,
220 selector.build(), treatment));
221 }
222
223 private void fwdClassifierRules(
224 FilteringObjective obj,
225 PortCriterion inPortCriterion,
226 EthCriterion ethDstCriterion,
227 EthCriterion ethDstMaskedCriterion,
228 ObjectiveTranslation.Builder resultBuilder)
229 throws FabricPipelinerException {
230
231 final List<FlowRule> flowRules = Lists.newArrayList();
232
233 final PortNumber inPort = inPortCriterion.port();
234 if (ethDstCriterion == null) {
235 if (ethDstMaskedCriterion == null) {
236 // No match. Do bridging (default action).
237 return;
238 }
239 // Masked fwd classifier rule
240 final MacAddress dstMac = ethDstMaskedCriterion.mac();
241 final MacAddress dstMacMask = ethDstMaskedCriterion.mask();
242 flowRules.add(maskedFwdClassifierRule(inPort, dstMac, dstMacMask, obj));
243 } else {
244 final MacAddress dstMac = ethDstCriterion.mac();
245 flowRules.addAll(ipFwdClassifierRules(inPort, dstMac, obj));
Daniele Moro693d76f2019-09-24 14:34:07 -0700246 flowRules.addAll(mplsFwdClassifierRules(inPort, dstMac, obj));
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800247 }
248
249 for (FlowRule f : flowRules) {
250 resultBuilder.addFlowRule(f);
251 }
252 }
253
254 private FlowRule maskedFwdClassifierRule(
255 PortNumber inPort, MacAddress dstMac, MacAddress dstMacMask,
256 FilteringObjective obj)
257 throws FabricPipelinerException {
258 final TrafficTreatment treatment;
259 final short ethType;
260 if (dstMac.equals(MacAddress.IPV4_MULTICAST)
261 && dstMacMask.equals(MacAddress.IPV4_MULTICAST_MASK)) {
262 treatment = fwdClassifierTreatment(FWD_IPV4_ROUTING);
263 ethType = Ethernet.TYPE_IPV4;
264 } else if (dstMac.equals(MacAddress.IPV6_MULTICAST)
265 && dstMacMask.equals(MacAddress.IPV6_MULTICAST_MASK)) {
266 treatment = fwdClassifierTreatment(FWD_IPV6_ROUTING);
267 ethType = Ethernet.TYPE_IPV6;
268 } else {
269 throw new FabricPipelinerException(format(
270 "Unsupported masked Ethernet address for fwd " +
271 "classifier rule (mac=%s, mask=%s)",
272 dstMac, dstMacMask));
273 }
274 return fwdClassifierRule(inPort, ethType, dstMac, dstMacMask, treatment, obj);
275 }
276
277 private Collection<FlowRule> ipFwdClassifierRules(
278 PortNumber inPort, MacAddress dstMac, FilteringObjective obj)
279 throws FabricPipelinerException {
280 final Collection<FlowRule> flowRules = Lists.newArrayList();
281 flowRules.add(fwdClassifierRule(
282 inPort, Ethernet.TYPE_IPV4, dstMac, null,
283 fwdClassifierTreatment(FWD_IPV4_ROUTING), obj));
284 flowRules.add(fwdClassifierRule(
285 inPort, Ethernet.TYPE_IPV6, dstMac, null,
286 fwdClassifierTreatment(FWD_IPV6_ROUTING), obj));
287 return flowRules;
288 }
289
Daniele Moro693d76f2019-09-24 14:34:07 -0700290 private Collection<FlowRule> mplsFwdClassifierRules(
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800291 PortNumber inPort, MacAddress dstMac, FilteringObjective obj)
292 throws FabricPipelinerException {
Daniele Moro693d76f2019-09-24 14:34:07 -0700293 // Forwarding classifier for MPLS is composed of 2 rules
294 // with higher priority wrt standard forwarding classifier rules,
295 // this is due to overlap on ternary matching.
296 TrafficTreatment treatment = fwdClassifierTreatment(FWD_MPLS);
297 final PiCriterion ethTypeMplsIpv4 = PiCriterion.builder()
298 .matchTernary(FabricConstants.HDR_ETH_TYPE,
299 Ethernet.MPLS_UNICAST, ETH_TYPE_EXACT_MASK)
300 .matchExact(FabricConstants.HDR_IP_ETH_TYPE,
301 Ethernet.TYPE_IPV4)
302 .build();
303 final TrafficSelector selectorMplsIpv4 = DefaultTrafficSelector.builder()
304 .matchInPort(inPort)
305 .matchPi(ethTypeMplsIpv4)
306 .matchEthDstMasked(dstMac, MacAddress.EXACT_MASK)
307 .build();
308
309 final PiCriterion ethTypeMplsIpv6 = PiCriterion.builder()
310 .matchTernary(FabricConstants.HDR_ETH_TYPE,
311 Ethernet.MPLS_UNICAST, ETH_TYPE_EXACT_MASK)
312 .matchExact(FabricConstants.HDR_IP_ETH_TYPE,
313 Ethernet.TYPE_IPV6)
314 .build();
315 final TrafficSelector selectorMplsIpv6 = DefaultTrafficSelector.builder()
316 .matchInPort(inPort)
317 .matchPi(ethTypeMplsIpv6)
318 .matchEthDstMasked(dstMac, MacAddress.EXACT_MASK)
319 .build();
320
321 return List.of(
322 flowRule(obj, FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER,
323 selectorMplsIpv4, treatment, obj.priority() + 1),
324 flowRule(obj, FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER,
325 selectorMplsIpv6, treatment, obj.priority() + 1)
326 );
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800327 }
328
329 private FlowRule fwdClassifierRule(
330 PortNumber inPort, short ethType, MacAddress dstMac, MacAddress dstMacMask,
331 TrafficTreatment treatment, FilteringObjective obj)
332 throws FabricPipelinerException {
Daniele Moro693d76f2019-09-24 14:34:07 -0700333 // Match on ip_eth_type that is the eth_type of the L3 protocol.
334 // i.e., if the packet has an IP header, ip_eth_type should
335 // contain the corresponding eth_type (for IPv4 or IPv6)
336 final PiCriterion ethTypeCriterion = PiCriterion.builder()
337 .matchExact(FabricConstants.HDR_IP_ETH_TYPE, ethType)
338 .build();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800339 final TrafficSelector selector = DefaultTrafficSelector.builder()
340 .matchInPort(inPort)
Daniele Moro693d76f2019-09-24 14:34:07 -0700341 .matchPi(ethTypeCriterion)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800342 .matchEthDstMasked(dstMac, dstMacMask == null
343 ? MacAddress.EXACT_MASK : dstMacMask)
344 .build();
345 return flowRule(
346 obj, FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER,
347 selector, treatment);
348 }
349
350 private TrafficTreatment fwdClassifierTreatment(byte fwdType) {
351 final PiActionParam param = new PiActionParam(FabricConstants.FWD_TYPE, fwdType);
352 final PiAction action = PiAction.builder()
353 .withId(FabricConstants.FABRIC_INGRESS_FILTERING_SET_FORWARDING_TYPE)
354 .withParameter(param)
355 .build();
356 return DefaultTrafficTreatment.builder()
357 .piTableAction(action)
358 .build();
359
360 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800361}