First implementation of BngProgrammable API
- BngProgrammable interface moved to ONOS core
- BngProgrammable implementation in fabric pipeliner

Change-Id: Ia020d19f305d8819eef7f70453b14cb00fd31af8
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/FabricPipeconfManager.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/FabricPipeconfManager.java
index 76122d9..f366e99 100644
--- a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/FabricPipeconfManager.java
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/FabricPipeconfManager.java
@@ -17,6 +17,8 @@
 package org.onosproject.pipelines.fabric.impl;
 
 import org.onosproject.inbandtelemetry.api.IntProgrammable;
+
+import org.onosproject.net.behaviour.BngProgrammable;
 import org.onosproject.net.behaviour.Pipeliner;
 import org.onosproject.net.pi.model.DefaultPiPipeconf;
 import org.onosproject.net.pi.model.PiPipeconf;
@@ -26,6 +28,7 @@
 import org.onosproject.p4runtime.model.P4InfoParserException;
 import org.onosproject.pipelines.fabric.FabricPipeconfService;
 import org.onosproject.pipelines.fabric.impl.behaviour.FabricIntProgrammable;
+import org.onosproject.pipelines.fabric.impl.behaviour.bng.FabricBngProgrammable;
 import org.onosproject.pipelines.fabric.impl.behaviour.FabricInterpreter;
 import org.onosproject.pipelines.fabric.impl.behaviour.pipeliner.FabricPipeliner;
 import org.osgi.service.component.annotations.Activate;
@@ -47,6 +50,7 @@
 
     private static final String INT_PROFILE_SUFFIX = "-int";
     private static final String FULL_PROFILE_SUFFIX = "-full";
+    private static final String BNG_PROFILE_SUFFIX = "-bng";
 
     private static Logger log = getLogger(FabricPipeconfLoader.class);
 
@@ -90,7 +94,10 @@
                 profileName.endsWith(FULL_PROFILE_SUFFIX)) {
             pipeconfBuilder.addBehaviour(IntProgrammable.class, FabricIntProgrammable.class);
         }
-
+        // Add BngProgrammable behavior for BNG-enabled pipelines.
+        if (profileName.endsWith(BNG_PROFILE_SUFFIX)) {
+            pipeconfBuilder.addBehaviour(BngProgrammable.class, FabricBngProgrammable.class);
+        }
         return pipeconfBuilder.build();
     }
 
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/bng/FabricBngProgrammable.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/bng/FabricBngProgrammable.java
new file mode 100644
index 0000000..9b566c0
--- /dev/null
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/bng/FabricBngProgrammable.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.pipelines.fabric.impl.behaviour.bng;
+
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.drivers.p4runtime.AbstractP4RuntimeHandlerBehaviour;
+import org.onosproject.net.behaviour.BngProgrammable;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TableId;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.PiCriterion;
+import org.onosproject.net.pi.model.PiCounterId;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.net.pi.runtime.PiCounterCell;
+import org.onosproject.net.pi.runtime.PiCounterCellData;
+import org.onosproject.net.pi.runtime.PiCounterCellHandle;
+import org.onosproject.net.pi.runtime.PiCounterCellId;
+import org.onosproject.p4runtime.api.P4RuntimeWriteClient;
+import org.onosproject.pipelines.fabric.impl.behaviour.FabricConstants;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class FabricBngProgrammable extends AbstractP4RuntimeHandlerBehaviour
+        implements BngProgrammable {
+
+    // Default priority of the inserted BNG rules.
+    private static final int DEFAULT_PRIORITY = 10;
+    // The index at which control plane packets are counted before the attachment is created.
+    private static final int DEFAULT_CONTROL_INDEX = 0;
+    // FIXME: retrieve this value from the table size in the PipelineModel
+    // Max number of supported attachments, useful to make sure to not read/write non-existing counters.
+    private static final int MAX_SUPPORTED_ATTACHMENTS = 1000;
+
+    private static final ImmutableBiMap<BngCounterType, PiCounterId> COUNTER_MAP =
+            ImmutableBiMap.<BngCounterType, PiCounterId>builder()
+                    .put(BngCounterType.DOWNSTREAM_RX, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_C_LINE_RX)
+                    .put(BngCounterType.DOWNSTREAM_TX, FabricConstants.FABRIC_EGRESS_BNG_EGRESS_DOWNSTREAM_C_LINE_TX)
+                    .put(BngCounterType.UPSTREAM_TX, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_C_TERMINATED)
+                    .put(BngCounterType.UPSTREAM_DROPPED, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_C_DROPPED)
+                    .put(BngCounterType.CONTROL_PLANE, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_C_CONTROL)
+                    .build();
+
+    // FIXME: add these counters to the BNG pipeline
+    private static final ImmutableSet<BngCounterType> UNSUPPORTED_COUNTER =
+            ImmutableSet.of(BngCounterType.UPSTREAM_RX, BngCounterType.DOWNSTREAM_DROPPED);
+
+    private FlowRuleService flowRuleService;
+
+    @Override
+    protected boolean setupBehaviour(String opName) {
+        if (!super.setupBehaviour(opName)) {
+            return false;
+        }
+        flowRuleService = handler().get(FlowRuleService.class);
+        return true;
+    }
+
+    @Override
+    public boolean init(ApplicationId appId) {
+        if (setupBehaviour("init()")) {
+            this.setupPuntToCpu(appId);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void cleanUp(ApplicationId appId) throws BngProgrammableException {
+        flowRuleService.removeFlowRulesById(appId);
+        this.resetControlTrafficCounter();
+    }
+
+    @Override
+    public void setupAttachment(Attachment attachmentInfo) throws BngProgrammableException {
+        checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
+        List<FlowRule> lstFlowRules = Lists.newArrayList();
+        lstFlowRules.add(buildTLineMapFlowRule(attachmentInfo));
+        // If the line is not active do not generate the rule for the table
+        // t_pppoe_term_v4 since term_disabled is @defaultonly action
+        if (attachmentInfo.lineActive()) {
+            lstFlowRules.add(buildTPppoeTermV4FlowRule(attachmentInfo));
+        }
+        lstFlowRules.add(buildTLineSessionMapFlowRule(attachmentInfo));
+        // Clean-up attachment related counters
+        this.resetCounters(attachmentInfo);
+        lstFlowRules.forEach(flowRule -> flowRuleService.applyFlowRules(flowRule));
+    }
+
+    @Override
+    public void removeAttachment(Attachment attachmentInfo) {
+        checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
+        List<FlowRule> lstFlowRules = Lists.newArrayList();
+        lstFlowRules.add(buildTLineMapFlowRule(attachmentInfo));
+        lstFlowRules.add(buildTPppoeTermV4FlowRule(attachmentInfo));
+        lstFlowRules.add(buildTLineSessionMapFlowRule(attachmentInfo));
+
+        lstFlowRules.forEach(flowRule -> flowRuleService.removeFlowRules(flowRule));
+    }
+
+    @Override
+    public Map<BngCounterType, PiCounterCellData> readCounters(Attachment attachmentInfo)
+            throws BngProgrammableException {
+        checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
+        return readCounters(attachmentInfo.attachmentId().id(), Set.of(BngCounterType.values()));
+    }
+
+    @Override
+    public PiCounterCellData readCounter(Attachment attachmentInfo, BngCounterType counter)
+            throws BngProgrammableException {
+        checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
+        return readCounters(attachmentInfo.attachmentId().id(), Set.of(counter))
+                .getOrDefault(counter, null);
+    }
+
+    @Override
+    public void resetCounters(Attachment attachmentInfo)
+            throws BngProgrammableException {
+        checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
+        resetCounters(attachmentInfo.attachmentId().id(), Set.of(BngCounterType.values()));
+    }
+
+    @Override
+    public PiCounterCellData readControlTrafficCounter()
+            throws BngProgrammableException {
+        return readCounters(DEFAULT_CONTROL_INDEX, Set.of(BngCounterType.CONTROL_PLANE))
+                .get(BngCounterType.CONTROL_PLANE);
+    }
+
+    @Override
+    public void resetCounter(Attachment attachmentInfo, BngCounterType counter)
+            throws BngProgrammableException {
+        resetCounters(attachmentInfo.attachmentId().id(), Set.of(counter));
+    }
+
+    @Override
+    public void resetControlTrafficCounter() throws BngProgrammableException {
+        resetCounters(DEFAULT_CONTROL_INDEX, Set.of((BngCounterType.CONTROL_PLANE)));
+    }
+
+    /**
+     * Read the specified counter at a specific index.
+     *
+     * @param index    The index of the counter.
+     * @param counters The set of counters to read.
+     * @throws BngProgrammableException
+     */
+    private Map<BngCounterType, PiCounterCellData> readCounters(
+            long index,
+            Set<BngCounterType> counters) throws BngProgrammableException {
+        checkIndex(index);
+        Map<BngCounterType, PiCounterCellData> readValues = Maps.newHashMap();
+        Set<PiCounterCellId> counterCellIds = counters.stream()
+                .filter(c -> !UNSUPPORTED_COUNTER.contains(c))
+                .map(c -> PiCounterCellId.ofIndirect(COUNTER_MAP.get(c), index))
+                .collect(Collectors.toSet());
+        // Check if there is any counter to read.
+        if (counterCellIds.size() != 0) {
+            Set<PiCounterCellHandle> counterCellHandles = counterCellIds.stream()
+                    .map(cId -> PiCounterCellHandle.of(this.deviceId, cId))
+                    .collect(Collectors.toSet());
+
+            // Query the device.
+            Collection<PiCounterCell> counterEntryResponse = client.read(
+                    p4DeviceId, pipeconf)
+                    .handles(counterCellHandles).submitSync()
+                    .all(PiCounterCell.class);
+
+            if (counterEntryResponse.size() == 0) {
+                throw new BngProgrammableException(
+                        String.format("Error in reading counters %s", counters.toString()));
+            }
+            readValues.putAll(counterEntryResponse.stream().collect(
+                    Collectors.toMap(counterCell -> COUNTER_MAP.inverse()
+                                             .get(counterCell.cellId().counterId()),
+                                     PiCounterCell::data)));
+        }
+        return readValues;
+    }
+
+    /**
+     * Reset the specified counters at a specific index.
+     *
+     * @param index    The index of the counter.
+     * @param counters The set of counters to reset.
+     */
+    private void resetCounters(long index, Set<BngCounterType> counters) throws BngProgrammableException {
+        checkIndex(index);
+        Set<PiCounterCellId> counterCellIds = counters.stream()
+                .filter(c -> !UNSUPPORTED_COUNTER.contains(c))
+                .map(c -> PiCounterCellId.ofIndirect(COUNTER_MAP.get(c), index))
+                .collect(Collectors.toSet());
+        if (counterCellIds.isEmpty()) {
+            // No counters to reset
+            log.info("No counters to reset.");
+            return;
+        }
+        Set<PiCounterCell> counterCellData = counterCellIds.stream()
+                .map(cId -> new PiCounterCell(cId, 0, 0))
+                .collect(Collectors.toSet());
+
+        // Query the device.
+        Collection<P4RuntimeWriteClient.EntityUpdateResponse> counterEntryResponse = client.write(
+                p4DeviceId, pipeconf)
+                .modify(counterCellData).submitSync()
+                .all();
+        counterEntryResponse.stream().filter(counterEntryResp -> !counterEntryResp.isSuccess())
+                .forEach(counterEntryResp -> log.warn("A counter was not reset correctly: {}",
+                                                      counterEntryResp.explanation()));
+    }
+
+    /**
+     * Check if the index is in the range of max number of supported
+     * attachments.
+     *
+     * @param index
+     * @throws BngProgrammableException
+     */
+    private void checkIndex(long index) throws BngProgrammableException {
+        if (index > MAX_SUPPORTED_ATTACHMENTS) {
+            throw new BngProgrammableException("Counter index too big. Value:" +
+                                                       index + ", MAX:" +
+                                                       MAX_SUPPORTED_ATTACHMENTS);
+        }
+    }
+
+    /**
+     * Set the punt to CPU rules of the BNG from a specific Application ID.
+     *
+     * @param appId Application ID asking to recive BNG control plane packets.
+     */
+    private void setupPuntToCpu(ApplicationId appId) {
+        for (Criterion c : PuntCpuCriterionFactory.getAllPuntCriterion()) {
+            FlowRule flPuntCpu = buildTPppoeCpFlowRule((PiCriterion) c, appId);
+            flowRuleService.applyFlowRules(flPuntCpu);
+        }
+    }
+
+    /**
+     * Build the Flow Rule for the table t_pppoe_term_v4 of the ingress
+     * upstream.
+     *
+     * @param attachment
+     * @return
+     */
+    private FlowRule buildTPppoeTermV4FlowRule(Attachment attachment) {
+        PiCriterion criterion = PiCriterion.builder()
+                .matchExact(FabricConstants.HDR_LINE_ID,
+                            attachment.attachmentId().id())
+                .matchExact(FabricConstants.HDR_IPV4_SRC,
+                            attachment.ipAddress().toOctets())
+                .matchExact(FabricConstants.HDR_PPPOE_SESSION_ID,
+                            attachment.pppoeSessionId())
+                // TODO: match on MAC SRC address (antispoofing)
+//                    .matchExact(FabricConstants.HDR_ETH_SRC,
+//                                attachment.macAddress.toBytes())
+                .build();
+        TrafficSelector trafficSelector = DefaultTrafficSelector.builder()
+                .matchPi(criterion)
+                .build();
+        PiAction action = PiAction.builder()
+                .withId(attachment.lineActive() ?
+                                FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_TERM_ENABLED_V4 :
+                                FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_TERM_DISABLED)
+                .build();
+        TrafficTreatment instTreatment = DefaultTrafficTreatment.builder()
+                .piTableAction(action)
+                .build();
+        return buildFlowRule(trafficSelector,
+                             instTreatment,
+                             FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_T_PPPOE_TERM_V4,
+                             attachment.appId());
+    }
+
+    /**
+     * Build the Flow Rule for the table t_line_session_map of the ingress
+     * downstream.
+     *
+     * @param attachment
+     * @return
+     */
+    private FlowRule buildTLineSessionMapFlowRule(Attachment attachment) {
+        PiCriterion criterion = PiCriterion.builder()
+                .matchExact(FabricConstants.HDR_LINE_ID,
+                            attachment.attachmentId().id())
+                .build();
+        TrafficSelector trafficSelector = DefaultTrafficSelector.builder()
+                .matchPi(criterion)
+                .build();
+        PiAction action;
+        if (attachment.lineActive()) {
+            action = PiAction.builder()
+                    .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_SET_SESSION)
+                    .withParameter(new PiActionParam(FabricConstants.PPPOE_SESSION_ID,
+                                                     attachment.pppoeSessionId()))
+                    .build();
+        } else {
+            action = PiAction.builder()
+                    .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_DROP)
+                    .build();
+        }
+        TrafficTreatment instTreatment = DefaultTrafficTreatment.builder()
+                .piTableAction(action)
+                .build();
+        return buildFlowRule(trafficSelector,
+                             instTreatment,
+                             FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_T_LINE_SESSION_MAP,
+                             attachment.appId());
+    }
+
+    /**
+     * Build the flow rule for the table t_line_map of the BNG-U (common to both
+     * upstream and downstream).
+     *
+     * @param attachment
+     * @return
+     */
+    private FlowRule buildTLineMapFlowRule(Attachment attachment) {
+        PiCriterion criterion = PiCriterion.builder()
+                .matchExact(FabricConstants.HDR_S_TAG,
+                            attachment.sTag().toShort())
+                .matchExact(FabricConstants.HDR_C_TAG,
+                            attachment.cTag().toShort())
+                .build();
+        TrafficSelector trafficSelector = DefaultTrafficSelector.builder()
+                .matchPi(criterion)
+                .build();
+        PiAction action = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_SET_LINE)
+                .withParameter(new PiActionParam(FabricConstants.LINE_ID,
+                                                 attachment.attachmentId().id()))
+                .build();
+        TrafficTreatment instTreatment = DefaultTrafficTreatment.builder()
+                .piTableAction(action)
+                .build();
+        return buildFlowRule(trafficSelector,
+                             instTreatment,
+                             FabricConstants.FABRIC_INGRESS_BNG_INGRESS_T_LINE_MAP,
+                             attachment.appId());
+    }
+
+    /**
+     * Build the flow rule for the table t_pppoe_cp of the ingress upstream.
+     *
+     * @param criterion Criterion to build the flow rule.
+     * @return The built flow rule.
+     */
+    private FlowRule buildTPppoeCpFlowRule(PiCriterion criterion, ApplicationId appId) {
+        TrafficSelector trafficSelector = DefaultTrafficSelector.builder()
+                .matchPi(criterion)
+                .build();
+        TrafficTreatment instTreatment = DefaultTrafficTreatment.builder()
+                .piTableAction(PiAction.builder()
+                                       .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_PUNT_TO_CPU)
+                                       .build()
+                )
+                .build();
+        return buildFlowRule(trafficSelector,
+                             instTreatment,
+                             FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_T_PPPOE_CP,
+                             appId);
+    }
+
+    private FlowRule buildFlowRule(TrafficSelector trafficSelector,
+                                   TrafficTreatment trafficTreatment,
+                                   TableId tableId,
+                                   ApplicationId appId) {
+        return DefaultFlowRule.builder()
+                .forDevice(data().deviceId())
+                .withSelector(trafficSelector)
+                .withTreatment(trafficTreatment)
+                .withPriority(DEFAULT_PRIORITY)
+                .forTable(tableId)
+                .fromApp(appId)
+                .makePermanent()
+                .build();
+    }
+}
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/bng/PuntCpuCriterionFactory.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/bng/PuntCpuCriterionFactory.java
new file mode 100644
index 0000000..ff40563
--- /dev/null
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/bng/PuntCpuCriterionFactory.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.pipelines.fabric.impl.behaviour.bng;
+
+import com.google.common.collect.ImmutableList;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.PiCriterion;
+import org.onosproject.pipelines.fabric.impl.behaviour.FabricConstants;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Factory to build criteria to punt packet to the CPU from the BNG pipeline.
+ */
+final class PuntCpuCriterionFactory {
+
+    private static final ImmutableList<Byte> PPPOE_CODE =
+            ImmutableList.<Byte>builder()
+                    .add((byte) 0x09) // PADI
+                    .add((byte) 0x07) // PADO
+                    .add((byte) 0x19) // PADR
+                    .add((byte) 0x65) // PADS
+                    .add((byte) 0xa7) // PADT
+                    .build();
+
+    private static final ImmutableList<Short> PPP_PROTOCOL =
+            ImmutableList.<Short>builder()
+                    .add((short) 0xc021) // LCP
+                    .add((short) 0x8021) // IPCP
+                    .add((short) 0xc023) // PAP
+                    .add((short) 0xc223) // CHAP
+                    .build();
+
+    private static final byte PPP_PROTOCOL_DEFAULT_MASK = (byte) 0xFFFF;
+
+    private PuntCpuCriterionFactory() {
+        // Hide constructor
+    }
+
+    /**
+     * Build all the Protocol Independent criteria starting from all the PPPoE
+     * codes.
+     *
+     * @return The list of Protocol Independent criteria.
+     */
+    static Set<Criterion> getAllPuntCriterion() {
+        Set<Criterion> criteria = PPPOE_CODE.stream()
+                .map(PuntCpuCriterionFactory::getPppoePiCriterion).collect(Collectors.toSet());
+
+        criteria.addAll(PPP_PROTOCOL.stream()
+                                .map(PuntCpuCriterionFactory::getPppPiCriterion)
+                                .collect(Collectors.toSet()));
+        return criteria;
+    }
+
+    /**
+     * Build the Protocol Independent criterion related to the specific PPPoE
+     * code.
+     *
+     * @param pppoeCode PPPoE code field.
+     * @return The built criterion.
+     */
+    private static PiCriterion getPppoePiCriterion(byte pppoeCode) {
+        return PiCriterion.builder()
+                .matchExact(FabricConstants.HDR_PPPOE_CODE, new byte[]{pppoeCode})
+                .build();
+    }
+
+    /**
+     * Build the Protocol Independent criterion related to the specified PPPoE
+     * code and PPP protocol matching.
+     * <p>
+     * Match on PPPoE Protocol will be done with 0xFFFF as mask.
+     *
+     * @param pppProtocol PPP protocol field.
+     * @return The built criterion.
+     */
+    private static PiCriterion getPppPiCriterion(short pppProtocol) {
+        return PiCriterion.builder()
+                .matchExact(FabricConstants.HDR_PPPOE_CODE, 0)
+                .matchTernary(FabricConstants.HDR_PPPOE_PROTOCOL,
+                              pppProtocol,
+                              PPP_PROTOCOL_DEFAULT_MASK)
+                .build();
+    }
+}
+
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/bng/package-info.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/bng/package-info.java
new file mode 100644
index 0000000..803c2b2
--- /dev/null
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/bng/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation classes for BngProgrammable behaviour.
+ */
+package org.onosproject.pipelines.fabric.impl.behaviour.bng;
\ No newline at end of file