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