Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2019-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 | |
| 17 | package org.onosproject.pipelines.fabric.impl.behaviour.bng; |
| 18 | |
| 19 | import com.google.common.collect.ImmutableBiMap; |
| 20 | import com.google.common.collect.ImmutableSet; |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 21 | import com.google.common.collect.Iterables; |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 22 | import com.google.common.collect.Lists; |
| 23 | import com.google.common.collect.Maps; |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 24 | import org.onlab.util.ImmutableByteSequence; |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 25 | import org.onosproject.core.ApplicationId; |
| 26 | import org.onosproject.drivers.p4runtime.AbstractP4RuntimeHandlerBehaviour; |
| 27 | import org.onosproject.net.behaviour.BngProgrammable; |
| 28 | import org.onosproject.net.flow.DefaultFlowRule; |
| 29 | import org.onosproject.net.flow.DefaultTrafficSelector; |
| 30 | import org.onosproject.net.flow.DefaultTrafficTreatment; |
| 31 | import org.onosproject.net.flow.FlowRule; |
| 32 | import org.onosproject.net.flow.FlowRuleService; |
| 33 | import org.onosproject.net.flow.TableId; |
| 34 | import org.onosproject.net.flow.TrafficSelector; |
| 35 | import org.onosproject.net.flow.TrafficTreatment; |
| 36 | import org.onosproject.net.flow.criteria.Criterion; |
| 37 | import org.onosproject.net.flow.criteria.PiCriterion; |
| 38 | import org.onosproject.net.pi.model.PiCounterId; |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 39 | import org.onosproject.net.pi.model.PiMatchType; |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 40 | import org.onosproject.net.pi.runtime.PiAction; |
| 41 | import org.onosproject.net.pi.runtime.PiActionParam; |
| 42 | import org.onosproject.net.pi.runtime.PiCounterCell; |
| 43 | import org.onosproject.net.pi.runtime.PiCounterCellData; |
| 44 | import org.onosproject.net.pi.runtime.PiCounterCellHandle; |
| 45 | import org.onosproject.net.pi.runtime.PiCounterCellId; |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 46 | import org.onosproject.net.pi.runtime.PiExactFieldMatch; |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 47 | import org.onosproject.p4runtime.api.P4RuntimeWriteClient; |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 48 | import org.onosproject.pipelines.fabric.impl.behaviour.FabricCapabilities; |
Carmelo Cascone | 2102bfb | 2020-12-04 16:54:24 -0800 | [diff] [blame] | 49 | import org.onosproject.pipelines.fabric.FabricConstants; |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 50 | |
| 51 | import java.util.Collection; |
| 52 | import java.util.List; |
| 53 | import java.util.Map; |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 54 | import java.util.Objects; |
| 55 | import java.util.Optional; |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 56 | import java.util.Set; |
| 57 | import java.util.stream.Collectors; |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 58 | import java.util.stream.StreamSupport; |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 59 | |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 60 | /** |
| 61 | * Implementation of BngProgrammable for fabric.p4. |
| 62 | */ |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 63 | public class FabricBngProgrammable extends AbstractP4RuntimeHandlerBehaviour |
| 64 | implements BngProgrammable { |
| 65 | |
| 66 | // Default priority of the inserted BNG rules. |
| 67 | private static final int DEFAULT_PRIORITY = 10; |
| 68 | // The index at which control plane packets are counted before the attachment is created. |
| 69 | private static final int DEFAULT_CONTROL_INDEX = 0; |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 70 | |
| 71 | private static final ImmutableBiMap<BngCounterType, PiCounterId> COUNTER_MAP = |
| 72 | ImmutableBiMap.<BngCounterType, PiCounterId>builder() |
| 73 | .put(BngCounterType.DOWNSTREAM_RX, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_C_LINE_RX) |
| 74 | .put(BngCounterType.DOWNSTREAM_TX, FabricConstants.FABRIC_EGRESS_BNG_EGRESS_DOWNSTREAM_C_LINE_TX) |
| 75 | .put(BngCounterType.UPSTREAM_TX, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_C_TERMINATED) |
| 76 | .put(BngCounterType.UPSTREAM_DROPPED, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_C_DROPPED) |
| 77 | .put(BngCounterType.CONTROL_PLANE, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_C_CONTROL) |
| 78 | .build(); |
| 79 | |
| 80 | // FIXME: add these counters to the BNG pipeline |
| 81 | private static final ImmutableSet<BngCounterType> UNSUPPORTED_COUNTER = |
| 82 | ImmutableSet.of(BngCounterType.UPSTREAM_RX, BngCounterType.DOWNSTREAM_DROPPED); |
| 83 | |
| 84 | private FlowRuleService flowRuleService; |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 85 | private FabricBngProgrammableService bngProgService; |
| 86 | private FabricCapabilities capabilities; |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 87 | |
| 88 | @Override |
| 89 | protected boolean setupBehaviour(String opName) { |
| 90 | if (!super.setupBehaviour(opName)) { |
| 91 | return false; |
| 92 | } |
| 93 | flowRuleService = handler().get(FlowRuleService.class); |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 94 | bngProgService = handler().get(FabricBngProgrammableService.class); |
| 95 | capabilities = new FabricCapabilities(pipeconf); |
| 96 | |
| 97 | if (!capabilities.supportBng()) { |
| 98 | log.warn("Pipeconf {} on {} does not support BNG capabilities, " + |
| 99 | "cannot perform {}", |
| 100 | pipeconf.id(), deviceId, opName); |
| 101 | return false; |
| 102 | } |
| 103 | |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 104 | return true; |
| 105 | } |
| 106 | |
| 107 | @Override |
| 108 | public boolean init(ApplicationId appId) { |
| 109 | if (setupBehaviour("init()")) { |
| 110 | this.setupPuntToCpu(appId); |
| 111 | return true; |
| 112 | } |
| 113 | return false; |
| 114 | } |
| 115 | |
| 116 | @Override |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 117 | public void cleanUp(ApplicationId appId) { |
Daniele Moro | 2832ec9 | 2019-12-05 22:10:48 -0800 | [diff] [blame] | 118 | if (!setupBehaviour("cleanUp()")) { |
| 119 | return; |
| 120 | } |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 121 | // Remove flow rules. |
| 122 | var flowEntries = flowRuleService.getFlowEntriesById(appId); |
| 123 | flowRuleService.removeFlowRules( |
| 124 | Iterables.toArray(flowEntries, FlowRule.class)); |
| 125 | // Release line IDs found in removed flow rules. |
| 126 | getLineIdsFromFlowRules(flowEntries) |
| 127 | .forEach(this::releaseLineId); |
| 128 | // Reset counters. |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 129 | this.resetControlTrafficCounter(); |
| 130 | } |
| 131 | |
| 132 | @Override |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 133 | public void setupAttachment(Attachment attachment) throws BngProgrammableException { |
Daniele Moro | 2832ec9 | 2019-12-05 22:10:48 -0800 | [diff] [blame] | 134 | if (!setupBehaviour("setupAttachment()")) { |
| 135 | return; |
| 136 | } |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 137 | checkAttachment(attachment); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 138 | List<FlowRule> lstFlowRules = Lists.newArrayList(); |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 139 | lstFlowRules.add(buildTLineMapFlowRule(attachment)); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 140 | // If the line is not active do not generate the rule for the table |
| 141 | // t_pppoe_term_v4 since term_disabled is @defaultonly action |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 142 | if (attachment.lineActive()) { |
| 143 | lstFlowRules.add(buildTPppoeTermV4FlowRule(attachment)); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 144 | } |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 145 | lstFlowRules.add(buildTLineSessionMapFlowRule(attachment)); |
Daniele Moro | 893988e | 2019-11-27 16:14:38 -0800 | [diff] [blame] | 146 | |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 147 | lstFlowRules.forEach(flowRule -> flowRuleService.applyFlowRules(flowRule)); |
| 148 | } |
| 149 | |
| 150 | @Override |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 151 | public void removeAttachment(Attachment attachment) throws BngProgrammableException { |
Daniele Moro | 2832ec9 | 2019-12-05 22:10:48 -0800 | [diff] [blame] | 152 | if (!setupBehaviour("removeAttachment()")) { |
| 153 | return; |
| 154 | } |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 155 | checkAttachment(attachment); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 156 | List<FlowRule> lstFlowRules = Lists.newArrayList(); |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 157 | lstFlowRules.add(buildTLineMapFlowRule(attachment)); |
| 158 | lstFlowRules.add(buildTPppoeTermV4FlowRule(attachment)); |
| 159 | lstFlowRules.add(buildTLineSessionMapFlowRule(attachment)); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 160 | |
| 161 | lstFlowRules.forEach(flowRule -> flowRuleService.removeFlowRules(flowRule)); |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 162 | |
| 163 | releaseLineId(attachment); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 164 | } |
| 165 | |
| 166 | @Override |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 167 | public Map<BngCounterType, PiCounterCellData> readCounters(Attachment attachment) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 168 | throws BngProgrammableException { |
Daniele Moro | 2832ec9 | 2019-12-05 22:10:48 -0800 | [diff] [blame] | 169 | if (!setupBehaviour("readCounters()")) { |
| 170 | return Maps.newHashMap(); |
| 171 | } |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 172 | checkAttachment(attachment); |
| 173 | return readCounters(lineId(attachment), Set.of(BngCounterType.values())); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 174 | } |
| 175 | |
| 176 | @Override |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 177 | public PiCounterCellData readCounter(Attachment attachment, BngCounterType counter) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 178 | throws BngProgrammableException { |
Daniele Moro | 2832ec9 | 2019-12-05 22:10:48 -0800 | [diff] [blame] | 179 | if (!setupBehaviour("readCounter()")) { |
| 180 | return null; |
| 181 | } |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 182 | checkAttachment(attachment); |
| 183 | return readCounters(lineId(attachment), Set.of(counter)) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 184 | .getOrDefault(counter, null); |
| 185 | } |
| 186 | |
| 187 | @Override |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 188 | public void resetCounters(Attachment attachment) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 189 | throws BngProgrammableException { |
Daniele Moro | 2832ec9 | 2019-12-05 22:10:48 -0800 | [diff] [blame] | 190 | if (!setupBehaviour("resetCounters()")) { |
| 191 | return; |
| 192 | } |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 193 | checkAttachment(attachment); |
| 194 | resetCounters(lineId(attachment), Set.of(BngCounterType.values())); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | @Override |
| 198 | public PiCounterCellData readControlTrafficCounter() |
| 199 | throws BngProgrammableException { |
Daniele Moro | 2832ec9 | 2019-12-05 22:10:48 -0800 | [diff] [blame] | 200 | if (!setupBehaviour("readControlTrafficCounter()")) { |
| 201 | return null; |
| 202 | } |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 203 | return readCounters(DEFAULT_CONTROL_INDEX, Set.of(BngCounterType.CONTROL_PLANE)) |
| 204 | .get(BngCounterType.CONTROL_PLANE); |
| 205 | } |
| 206 | |
| 207 | @Override |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 208 | public void resetCounter(Attachment attachment, BngCounterType counter) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 209 | throws BngProgrammableException { |
Daniele Moro | 2832ec9 | 2019-12-05 22:10:48 -0800 | [diff] [blame] | 210 | if (!setupBehaviour("resetCounter()")) { |
| 211 | return; |
| 212 | } |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 213 | checkAttachment(attachment); |
| 214 | resetCounters(lineId(attachment), Set.of(counter)); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 215 | } |
| 216 | |
| 217 | @Override |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 218 | public void resetControlTrafficCounter() { |
Daniele Moro | 2832ec9 | 2019-12-05 22:10:48 -0800 | [diff] [blame] | 219 | if (!setupBehaviour("resetControlTrafficCounter()")) { |
| 220 | return; |
| 221 | } |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 222 | resetCounters(DEFAULT_CONTROL_INDEX, Set.of((BngCounterType.CONTROL_PLANE))); |
| 223 | } |
| 224 | |
| 225 | /** |
| 226 | * Read the specified counter at a specific index. |
| 227 | * |
| 228 | * @param index The index of the counter. |
| 229 | * @param counters The set of counters to read. |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 230 | */ |
| 231 | private Map<BngCounterType, PiCounterCellData> readCounters( |
| 232 | long index, |
| 233 | Set<BngCounterType> counters) throws BngProgrammableException { |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 234 | Map<BngCounterType, PiCounterCellData> readValues = Maps.newHashMap(); |
| 235 | Set<PiCounterCellId> counterCellIds = counters.stream() |
| 236 | .filter(c -> !UNSUPPORTED_COUNTER.contains(c)) |
| 237 | .map(c -> PiCounterCellId.ofIndirect(COUNTER_MAP.get(c), index)) |
| 238 | .collect(Collectors.toSet()); |
| 239 | // Check if there is any counter to read. |
| 240 | if (counterCellIds.size() != 0) { |
| 241 | Set<PiCounterCellHandle> counterCellHandles = counterCellIds.stream() |
| 242 | .map(cId -> PiCounterCellHandle.of(this.deviceId, cId)) |
| 243 | .collect(Collectors.toSet()); |
| 244 | |
| 245 | // Query the device. |
| 246 | Collection<PiCounterCell> counterEntryResponse = client.read( |
| 247 | p4DeviceId, pipeconf) |
| 248 | .handles(counterCellHandles).submitSync() |
| 249 | .all(PiCounterCell.class); |
| 250 | |
| 251 | if (counterEntryResponse.size() == 0) { |
| 252 | throw new BngProgrammableException( |
| 253 | String.format("Error in reading counters %s", counters.toString())); |
| 254 | } |
| 255 | readValues.putAll(counterEntryResponse.stream().collect( |
| 256 | Collectors.toMap(counterCell -> COUNTER_MAP.inverse() |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 257 | .get(counterCell.cellId().counterId()), |
| 258 | PiCounterCell::data))); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 259 | } |
| 260 | return readValues; |
| 261 | } |
| 262 | |
| 263 | /** |
| 264 | * Reset the specified counters at a specific index. |
| 265 | * |
| 266 | * @param index The index of the counter. |
| 267 | * @param counters The set of counters to reset. |
| 268 | */ |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 269 | private void resetCounters(long index, Set<BngCounterType> counters) { |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 270 | Set<PiCounterCellId> counterCellIds = counters.stream() |
| 271 | .filter(c -> !UNSUPPORTED_COUNTER.contains(c)) |
| 272 | .map(c -> PiCounterCellId.ofIndirect(COUNTER_MAP.get(c), index)) |
| 273 | .collect(Collectors.toSet()); |
| 274 | if (counterCellIds.isEmpty()) { |
| 275 | // No counters to reset |
| 276 | log.info("No counters to reset."); |
| 277 | return; |
| 278 | } |
| 279 | Set<PiCounterCell> counterCellData = counterCellIds.stream() |
| 280 | .map(cId -> new PiCounterCell(cId, 0, 0)) |
| 281 | .collect(Collectors.toSet()); |
| 282 | |
| 283 | // Query the device. |
| 284 | Collection<P4RuntimeWriteClient.EntityUpdateResponse> counterEntryResponse = client.write( |
| 285 | p4DeviceId, pipeconf) |
| 286 | .modify(counterCellData).submitSync() |
| 287 | .all(); |
| 288 | counterEntryResponse.stream().filter(counterEntryResp -> !counterEntryResp.isSuccess()) |
| 289 | .forEach(counterEntryResp -> log.warn("A counter was not reset correctly: {}", |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 290 | counterEntryResp.explanation())); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 291 | } |
| 292 | |
| 293 | /** |
Daniele Moro | 6d84c18 | 2019-12-05 22:24:14 -0800 | [diff] [blame] | 294 | * Preliminary check on the submitted attachment. |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 295 | */ |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 296 | private void checkAttachment(Attachment attachment) throws BngProgrammableException { |
| 297 | if (attachment.type() != Attachment.AttachmentType.PPPoE) { |
Daniele Moro | 6d84c18 | 2019-12-05 22:24:14 -0800 | [diff] [blame] | 298 | throw new BngProgrammableException( |
| 299 | "Attachment {} is not a PPPoE Attachment"); |
| 300 | } |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 301 | } |
| 302 | |
| 303 | /** |
| 304 | * Set the punt to CPU rules of the BNG from a specific Application ID. |
| 305 | * |
| 306 | * @param appId Application ID asking to recive BNG control plane packets. |
| 307 | */ |
| 308 | private void setupPuntToCpu(ApplicationId appId) { |
| 309 | for (Criterion c : PuntCpuCriterionFactory.getAllPuntCriterion()) { |
| 310 | FlowRule flPuntCpu = buildTPppoeCpFlowRule((PiCriterion) c, appId); |
| 311 | flowRuleService.applyFlowRules(flPuntCpu); |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | /** |
| 316 | * Build the Flow Rule for the table t_pppoe_term_v4 of the ingress |
| 317 | * upstream. |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 318 | */ |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 319 | private FlowRule buildTPppoeTermV4FlowRule(Attachment attachment) |
| 320 | throws BngProgrammableException { |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 321 | PiCriterion criterion = PiCriterion.builder() |
| 322 | .matchExact(FabricConstants.HDR_LINE_ID, |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 323 | lineId(attachment)) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 324 | .matchExact(FabricConstants.HDR_IPV4_SRC, |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 325 | attachment.ipAddress().toOctets()) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 326 | .matchExact(FabricConstants.HDR_PPPOE_SESSION_ID, |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 327 | attachment.pppoeSessionId()) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 328 | // TODO: match on MAC SRC address (antispoofing) |
| 329 | // .matchExact(FabricConstants.HDR_ETH_SRC, |
| 330 | // attachment.macAddress.toBytes()) |
| 331 | .build(); |
| 332 | TrafficSelector trafficSelector = DefaultTrafficSelector.builder() |
| 333 | .matchPi(criterion) |
| 334 | .build(); |
| 335 | PiAction action = PiAction.builder() |
| 336 | .withId(attachment.lineActive() ? |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 337 | FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_TERM_ENABLED_V4 : |
| 338 | FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_TERM_DISABLED) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 339 | .build(); |
| 340 | TrafficTreatment instTreatment = DefaultTrafficTreatment.builder() |
| 341 | .piTableAction(action) |
| 342 | .build(); |
| 343 | return buildFlowRule(trafficSelector, |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 344 | instTreatment, |
| 345 | FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_T_PPPOE_TERM_V4, |
| 346 | attachment.appId()); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 347 | } |
| 348 | |
| 349 | /** |
| 350 | * Build the Flow Rule for the table t_line_session_map of the ingress |
| 351 | * downstream. |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 352 | */ |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 353 | private FlowRule buildTLineSessionMapFlowRule(Attachment attachment) |
| 354 | throws BngProgrammableException { |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 355 | PiCriterion criterion = PiCriterion.builder() |
| 356 | .matchExact(FabricConstants.HDR_LINE_ID, |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 357 | lineId(attachment)) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 358 | .build(); |
| 359 | TrafficSelector trafficSelector = DefaultTrafficSelector.builder() |
| 360 | .matchPi(criterion) |
| 361 | .build(); |
| 362 | PiAction action; |
| 363 | if (attachment.lineActive()) { |
| 364 | action = PiAction.builder() |
| 365 | .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_SET_SESSION) |
| 366 | .withParameter(new PiActionParam(FabricConstants.PPPOE_SESSION_ID, |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 367 | attachment.pppoeSessionId())) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 368 | .build(); |
| 369 | } else { |
| 370 | action = PiAction.builder() |
| 371 | .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_DROP) |
| 372 | .build(); |
| 373 | } |
| 374 | TrafficTreatment instTreatment = DefaultTrafficTreatment.builder() |
| 375 | .piTableAction(action) |
| 376 | .build(); |
| 377 | return buildFlowRule(trafficSelector, |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 378 | instTreatment, |
| 379 | FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_T_LINE_SESSION_MAP, |
| 380 | attachment.appId()); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 381 | } |
| 382 | |
| 383 | /** |
| 384 | * Build the flow rule for the table t_line_map of the BNG-U (common to both |
| 385 | * upstream and downstream). |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 386 | */ |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 387 | private FlowRule buildTLineMapFlowRule(Attachment attachment) |
| 388 | throws BngProgrammableException { |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 389 | PiCriterion criterion = PiCriterion.builder() |
| 390 | .matchExact(FabricConstants.HDR_S_TAG, |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 391 | attachment.sTag().toShort()) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 392 | .matchExact(FabricConstants.HDR_C_TAG, |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 393 | attachment.cTag().toShort()) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 394 | .build(); |
| 395 | TrafficSelector trafficSelector = DefaultTrafficSelector.builder() |
| 396 | .matchPi(criterion) |
| 397 | .build(); |
| 398 | PiAction action = PiAction.builder() |
| 399 | .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_SET_LINE) |
| 400 | .withParameter(new PiActionParam(FabricConstants.LINE_ID, |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 401 | lineId(attachment))) |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 402 | .build(); |
| 403 | TrafficTreatment instTreatment = DefaultTrafficTreatment.builder() |
| 404 | .piTableAction(action) |
| 405 | .build(); |
| 406 | return buildFlowRule(trafficSelector, |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 407 | instTreatment, |
| 408 | FabricConstants.FABRIC_INGRESS_BNG_INGRESS_T_LINE_MAP, |
| 409 | attachment.appId()); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 410 | } |
| 411 | |
| 412 | /** |
| 413 | * Build the flow rule for the table t_pppoe_cp of the ingress upstream. |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 414 | */ |
| 415 | private FlowRule buildTPppoeCpFlowRule(PiCriterion criterion, ApplicationId appId) { |
| 416 | TrafficSelector trafficSelector = DefaultTrafficSelector.builder() |
| 417 | .matchPi(criterion) |
| 418 | .build(); |
| 419 | TrafficTreatment instTreatment = DefaultTrafficTreatment.builder() |
| 420 | .piTableAction(PiAction.builder() |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 421 | .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_PUNT_TO_CPU) |
| 422 | .build() |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 423 | ) |
| 424 | .build(); |
| 425 | return buildFlowRule(trafficSelector, |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 426 | instTreatment, |
| 427 | FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_T_PPPOE_CP, |
| 428 | appId); |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 429 | } |
| 430 | |
| 431 | private FlowRule buildFlowRule(TrafficSelector trafficSelector, |
| 432 | TrafficTreatment trafficTreatment, |
| 433 | TableId tableId, |
| 434 | ApplicationId appId) { |
| 435 | return DefaultFlowRule.builder() |
| 436 | .forDevice(data().deviceId()) |
| 437 | .withSelector(trafficSelector) |
| 438 | .withTreatment(trafficTreatment) |
| 439 | .withPriority(DEFAULT_PRIORITY) |
| 440 | .forTable(tableId) |
| 441 | .fromApp(appId) |
| 442 | .makePermanent() |
| 443 | .build(); |
| 444 | } |
Carmelo Cascone | da15af8 | 2019-12-09 22:36:48 -0800 | [diff] [blame] | 445 | |
| 446 | private long lineId(Attachment attachment) throws BngProgrammableException { |
| 447 | try { |
| 448 | return bngProgService.getLineIdAllocator(deviceId, capabilities.bngMaxLineCount()).allocate(attachment); |
| 449 | } catch (FabricBngLineIdAllocator.IdExhaustedException e) { |
| 450 | throw new BngProgrammableException("Line IDs exhausted, unable to allocate a new one"); |
| 451 | } |
| 452 | } |
| 453 | |
| 454 | private void releaseLineId(Attachment attachment) { |
| 455 | bngProgService.getLineIdAllocator(deviceId, capabilities.bngMaxLineCount()).release(attachment); |
| 456 | } |
| 457 | |
| 458 | private void releaseLineId(long id) { |
| 459 | bngProgService.getLineIdAllocator(deviceId, capabilities.bngMaxLineCount()).release(id); |
| 460 | } |
| 461 | |
| 462 | private Set<Long> getLineIdsFromFlowRules(Iterable<? extends FlowRule> rules) { |
| 463 | // Extract the line ID found in the flow rule selector. |
| 464 | return StreamSupport.stream(rules.spliterator(), true) |
| 465 | .map(f -> (PiCriterion) f.selector().getCriterion(Criterion.Type.PROTOCOL_INDEPENDENT)) |
| 466 | .filter(Objects::nonNull) |
| 467 | .map(c -> c.fieldMatch(FabricConstants.HDR_LINE_ID)) |
| 468 | .filter(Optional::isPresent) |
| 469 | .map(Optional::get) |
| 470 | .filter(m -> m.type() == PiMatchType.EXACT) |
| 471 | .map(m -> ((PiExactFieldMatch) m).value()) |
| 472 | .map(b -> { |
| 473 | try { |
| 474 | return b.fit(Long.BYTES * 8); |
| 475 | } catch (ImmutableByteSequence.ByteSequenceTrimException e) { |
| 476 | log.error("Invalid line ID found in flow rule: {} is bigger than a long! BUG?", b); |
| 477 | return null; |
| 478 | } |
| 479 | }) |
| 480 | .filter(Objects::nonNull) |
| 481 | .map(b -> b.asReadOnlyBuffer().getLong()) |
| 482 | .collect(Collectors.toSet()); |
| 483 | } |
Daniele Moro | 464e5ed | 2019-07-25 14:45:01 -0700 | [diff] [blame] | 484 | } |