blob: 86b893d000446c5003edc3352694bfe7a46cba89 [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
164 final PiCriterion piCriterion = PiCriterion.builder()
165 .matchExact(FabricConstants.HDR_VLAN_IS_VALID, outerVlanValid ? ONE : ZERO)
166 .build();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800167
168 final TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
169 .add(inPortCriterion)
Daniele Moro7c3a0022019-07-12 13:38:34 -0700170 .add(piCriterion);
171 if (outerVlanValid) {
172 selector.add(outerVlanCriterion);
173 }
174 if (innerVlanValid) {
175 selector.add(innerVlanCriterion);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800176 }
177
178 final TrafficTreatment treatment;
179 if (obj.type().equals(FilteringObjective.Type.DENY)) {
180 treatment = DefaultTrafficTreatment.builder()
181 .piTableAction(DENY)
182 .build();
183 } else {
184 treatment = obj.meta() == null
185 ? DefaultTrafficTreatment.emptyTreatment() : obj.meta();
186 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800187 resultBuilder.addFlowRule(flowRule(
188 obj, FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN,
189 selector.build(), treatment));
190 }
191
192 private void fwdClassifierRules(
193 FilteringObjective obj,
194 PortCriterion inPortCriterion,
195 EthCriterion ethDstCriterion,
196 EthCriterion ethDstMaskedCriterion,
197 ObjectiveTranslation.Builder resultBuilder)
198 throws FabricPipelinerException {
199
200 final List<FlowRule> flowRules = Lists.newArrayList();
201
202 final PortNumber inPort = inPortCriterion.port();
203 if (ethDstCriterion == null) {
204 if (ethDstMaskedCriterion == null) {
205 // No match. Do bridging (default action).
206 return;
207 }
208 // Masked fwd classifier rule
209 final MacAddress dstMac = ethDstMaskedCriterion.mac();
210 final MacAddress dstMacMask = ethDstMaskedCriterion.mask();
211 flowRules.add(maskedFwdClassifierRule(inPort, dstMac, dstMacMask, obj));
212 } else {
213 final MacAddress dstMac = ethDstCriterion.mac();
214 flowRules.addAll(ipFwdClassifierRules(inPort, dstMac, obj));
Daniele Moro693d76f2019-09-24 14:34:07 -0700215 flowRules.addAll(mplsFwdClassifierRules(inPort, dstMac, obj));
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800216 }
217
218 for (FlowRule f : flowRules) {
219 resultBuilder.addFlowRule(f);
220 }
221 }
222
223 private FlowRule maskedFwdClassifierRule(
224 PortNumber inPort, MacAddress dstMac, MacAddress dstMacMask,
225 FilteringObjective obj)
226 throws FabricPipelinerException {
227 final TrafficTreatment treatment;
228 final short ethType;
229 if (dstMac.equals(MacAddress.IPV4_MULTICAST)
230 && dstMacMask.equals(MacAddress.IPV4_MULTICAST_MASK)) {
231 treatment = fwdClassifierTreatment(FWD_IPV4_ROUTING);
232 ethType = Ethernet.TYPE_IPV4;
233 } else if (dstMac.equals(MacAddress.IPV6_MULTICAST)
234 && dstMacMask.equals(MacAddress.IPV6_MULTICAST_MASK)) {
235 treatment = fwdClassifierTreatment(FWD_IPV6_ROUTING);
236 ethType = Ethernet.TYPE_IPV6;
237 } else {
238 throw new FabricPipelinerException(format(
239 "Unsupported masked Ethernet address for fwd " +
240 "classifier rule (mac=%s, mask=%s)",
241 dstMac, dstMacMask));
242 }
243 return fwdClassifierRule(inPort, ethType, dstMac, dstMacMask, treatment, obj);
244 }
245
246 private Collection<FlowRule> ipFwdClassifierRules(
247 PortNumber inPort, MacAddress dstMac, FilteringObjective obj)
248 throws FabricPipelinerException {
249 final Collection<FlowRule> flowRules = Lists.newArrayList();
250 flowRules.add(fwdClassifierRule(
251 inPort, Ethernet.TYPE_IPV4, dstMac, null,
252 fwdClassifierTreatment(FWD_IPV4_ROUTING), obj));
253 flowRules.add(fwdClassifierRule(
254 inPort, Ethernet.TYPE_IPV6, dstMac, null,
255 fwdClassifierTreatment(FWD_IPV6_ROUTING), obj));
256 return flowRules;
257 }
258
Daniele Moro693d76f2019-09-24 14:34:07 -0700259 private Collection<FlowRule> mplsFwdClassifierRules(
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800260 PortNumber inPort, MacAddress dstMac, FilteringObjective obj)
261 throws FabricPipelinerException {
Daniele Moro693d76f2019-09-24 14:34:07 -0700262 // Forwarding classifier for MPLS is composed of 2 rules
263 // with higher priority wrt standard forwarding classifier rules,
264 // this is due to overlap on ternary matching.
265 TrafficTreatment treatment = fwdClassifierTreatment(FWD_MPLS);
266 final PiCriterion ethTypeMplsIpv4 = PiCriterion.builder()
267 .matchTernary(FabricConstants.HDR_ETH_TYPE,
268 Ethernet.MPLS_UNICAST, ETH_TYPE_EXACT_MASK)
269 .matchExact(FabricConstants.HDR_IP_ETH_TYPE,
270 Ethernet.TYPE_IPV4)
271 .build();
272 final TrafficSelector selectorMplsIpv4 = DefaultTrafficSelector.builder()
273 .matchInPort(inPort)
274 .matchPi(ethTypeMplsIpv4)
275 .matchEthDstMasked(dstMac, MacAddress.EXACT_MASK)
276 .build();
277
278 final PiCriterion ethTypeMplsIpv6 = PiCriterion.builder()
279 .matchTernary(FabricConstants.HDR_ETH_TYPE,
280 Ethernet.MPLS_UNICAST, ETH_TYPE_EXACT_MASK)
281 .matchExact(FabricConstants.HDR_IP_ETH_TYPE,
282 Ethernet.TYPE_IPV6)
283 .build();
284 final TrafficSelector selectorMplsIpv6 = DefaultTrafficSelector.builder()
285 .matchInPort(inPort)
286 .matchPi(ethTypeMplsIpv6)
287 .matchEthDstMasked(dstMac, MacAddress.EXACT_MASK)
288 .build();
289
290 return List.of(
291 flowRule(obj, FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER,
292 selectorMplsIpv4, treatment, obj.priority() + 1),
293 flowRule(obj, FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER,
294 selectorMplsIpv6, treatment, obj.priority() + 1)
295 );
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800296 }
297
298 private FlowRule fwdClassifierRule(
299 PortNumber inPort, short ethType, MacAddress dstMac, MacAddress dstMacMask,
300 TrafficTreatment treatment, FilteringObjective obj)
301 throws FabricPipelinerException {
Daniele Moro693d76f2019-09-24 14:34:07 -0700302 // Match on ip_eth_type that is the eth_type of the L3 protocol.
303 // i.e., if the packet has an IP header, ip_eth_type should
304 // contain the corresponding eth_type (for IPv4 or IPv6)
305 final PiCriterion ethTypeCriterion = PiCriterion.builder()
306 .matchExact(FabricConstants.HDR_IP_ETH_TYPE, ethType)
307 .build();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800308 final TrafficSelector selector = DefaultTrafficSelector.builder()
309 .matchInPort(inPort)
Daniele Moro693d76f2019-09-24 14:34:07 -0700310 .matchPi(ethTypeCriterion)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800311 .matchEthDstMasked(dstMac, dstMacMask == null
312 ? MacAddress.EXACT_MASK : dstMacMask)
313 .build();
314 return flowRule(
315 obj, FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER,
316 selector, treatment);
317 }
318
319 private TrafficTreatment fwdClassifierTreatment(byte fwdType) {
320 final PiActionParam param = new PiActionParam(FabricConstants.FWD_TYPE, fwdType);
321 final PiAction action = PiAction.builder()
322 .withId(FabricConstants.FABRIC_INGRESS_FILTERING_SET_FORWARDING_TYPE)
323 .withParameter(param)
324 .build();
325 return DefaultTrafficTreatment.builder()
326 .piTableAction(action)
327 .build();
328
329 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800330}