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

Change-Id: Ia020d19f305d8819eef7f70453b14cb00fd31af8
diff --git a/core/api/src/main/java/org/onosproject/net/behaviour/BngProgrammable.java b/core/api/src/main/java/org/onosproject/net/behaviour/BngProgrammable.java
new file mode 100644
index 0000000..a35377e
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/behaviour/BngProgrammable.java
@@ -0,0 +1,301 @@
+/*
+ * 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.net.behaviour;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.Identifier;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.driver.HandlerBehaviour;
+import org.onosproject.net.pi.runtime.PiCounterCellData;
+
+import java.util.Map;
+
+/**
+ * BNG programmable behavior. Provides means to update device forwarding state
+ * to perform BNG user plane functions (e.g., tunnel termination, accounting,
+ * etc.). An implementation of this API should not write state directly to the
+ * device, but instead, always rely on core ONOS subsystems (e.g.,
+ * FlowRuleService, GroupService, etc).
+ */
+public interface BngProgrammable extends HandlerBehaviour {
+
+    /**
+     * Provision rules to punt BNG control packets to ONOS. Control packets
+     * might include Session Discovery, Authentication, Address Assignment etc.
+     * Apps are expected to call this method as the first one when they are
+     * ready to process attachment connection requests.
+     *
+     * @param appId Application ID of the caller of this API.
+     * @return True if initialized, false otherwise.
+     */
+    boolean init(ApplicationId appId);
+
+    /**
+     * Remove any state previously created by this API for the given application
+     * ID.
+     *
+     * @param appId Application ID of the application using the
+     *              BngProgrammable.
+     * @throws BngProgrammableException while writing on BNG device.
+     */
+    void cleanUp(ApplicationId appId) throws BngProgrammableException;
+
+    /**
+     * Set up the necessary state to enable termination of the attachment
+     * traffic. Make also sure that the state of the given attachment is cleaned
+     * up. If the attachment is active, packets will be forwarded/terminated
+     * after calling this method, if not they will be dropped.
+     *
+     * @param attachmentInfo Attachment information to configure the line
+     *                       termination.
+     * @throws BngProgrammableException while writing on BNG device.
+     */
+    void setupAttachment(Attachment attachmentInfo)
+            throws BngProgrammableException;
+
+    /**
+     * Remove any state associated with the given attachment, including
+     * termination flow rules. Calling this method while an attachment is
+     * generating/receiving traffic, will eventually cause all packets to be
+     * dropped for that attachment.
+     *
+     * @param attachmentInfo Attachment information to remove the line
+     *                       termination.
+     */
+    void removeAttachment(Attachment attachmentInfo);
+
+    /**
+     * Read all counters for a given attachment and returns a map with keys
+     * BngCounterType and values the ones obtained from the device. If a
+     * specific BngCounterType is not found in the map, it means the device does
+     * not support it.
+     *
+     * @param attachmentInfo Attachment information.
+     * @return The counter values of the attachment.
+     * @throws BngProgrammableException while reading from BNG device.
+     */
+    Map<BngCounterType, PiCounterCellData> readCounters(Attachment attachmentInfo)
+            throws BngProgrammableException;
+
+    /**
+     * Read a specific counter value of a specific attachment. If the given
+     * BngCounterType is not supported by the device, returns null.
+     *
+     * @param attachmentInfo Attachment information.
+     * @param counter        The counter to be read.
+     * @return The value of the specific counter.
+     * @throws BngProgrammableException while reading from BNG device.
+     */
+    PiCounterCellData readCounter(Attachment attachmentInfo, BngCounterType counter)
+            throws BngProgrammableException;
+
+    /**
+     * Read the control plane traffic counter of packets punted before
+     * attachment creation (e.g., when an attachment is not created yet). If
+     * unsupported, return null value.
+     *
+     * @return The value of the control traffic counter.
+     * @throws BngProgrammableException while reading from BNG device.
+     */
+    PiCounterCellData readControlTrafficCounter()
+            throws BngProgrammableException;
+
+    /**
+     * Reset the given counter of a specific attachment.
+     *
+     * @param attachmentInfo Attachment information.
+     * @param counter        The counter to be reset.
+     * @throws BngProgrammableException while writing on BNG device.
+     */
+    void resetCounter(Attachment attachmentInfo, BngCounterType counter)
+            throws BngProgrammableException;
+
+    /**
+     * Reset the all the counters of a specific attachment.
+     *
+     * @param attachmentInfo Attachment information.
+     * @throws BngProgrammableException while writing on BNG device.
+     */
+    void resetCounters(Attachment attachmentInfo)
+            throws BngProgrammableException;
+
+    /**
+     * Reset the control plane traffic counter of packets punted before
+     * attachment creation (e.g., when an attachment is not created yet).
+     *
+     * @throws BngProgrammableException while writing on BNG device.
+     */
+    void resetControlTrafficCounter()
+            throws BngProgrammableException;
+
+    /**
+     * Counters to implement BNG accounting. Some of these counters can be
+     * unsupported by the implementations.
+     */
+    enum BngCounterType {
+        /**
+         * Count the received packets in the downstream direction.
+         */
+        DOWNSTREAM_RX,
+
+        /**
+         * Count the transmitted packets in the downstream direction.
+         */
+        DOWNSTREAM_TX,
+
+        /**
+         * Count the dropped packets in the downstream direction.
+         */
+        DOWNSTREAM_DROPPED,
+
+        /**
+         * Count the received packets in the upstream direction.
+         */
+        UPSTREAM_RX,
+
+        /**
+         * Count the transmitted packets in the upstream direction.
+         */
+        UPSTREAM_TX,
+
+        /**
+         * Count the dropped packets in the upstream direction.
+         */
+        UPSTREAM_DROPPED,
+
+        /**
+         * Count the received control plane packets.
+         */
+        CONTROL_PLANE
+    }
+
+    /**
+     * Immutable representation of an attachment in the BNG context. It
+     * identifies a L2/L2.5 tunnel line between the RG and the BNG.
+     */
+    interface Attachment {
+        /**
+         * Get the identifier of the attachment.
+         *
+         * @return The identifier of the attachment.
+         */
+        AttachmentId attachmentId();
+
+        /**
+         * Get the application ID that generated the attachment.
+         *
+         * @return The application ID.
+         */
+        ApplicationId appId();
+
+        /**
+         * Get the VLAN S-tag of the attachment.
+         *
+         * @return The VLAN S-tag of the attachment.
+         */
+        VlanId sTag();
+
+        /**
+         * Get the VLAN C-tag of the attachment.
+         *
+         * @return The VLAN C-tag of the attachment.
+         */
+        VlanId cTag();
+
+        /**
+         * Get the MAC address of the attachment.
+         *
+         * @return The MAC address of the attachment.
+         */
+        MacAddress macAddress();
+
+        /**
+         * Get the IP address of the attachment.
+         *
+         * @return The IP address of the attachment.
+         */
+        IpAddress ipAddress();
+
+        /**
+         * Defines if the line related to the attachment is active.
+         *
+         * @return True if the line is active, False otherwise.
+         */
+        boolean lineActive();
+
+        /**
+         * Get the type of attachment.
+         *
+         * @return type of attachment
+         */
+        AttachmentType type();
+
+        /**
+         * Get the PPPoE session ID of the attachment. This method is meaningful
+         * only if the attachment type is PPPoE.
+         *
+         * @return The PPPoE session ID.
+         */
+        short pppoeSessionId();
+
+        enum AttachmentType {
+            /**
+             * Implemented as PPPoE attachment.
+             */
+            PPPoE,
+
+            /**
+             * Implemented as IPoE attachment.
+             */
+            // TODO: unsupported.
+            IPoE
+        }
+    }
+
+    /**
+     * The identifier of the attachment.
+     */
+    class AttachmentId extends Identifier<Long> {
+        /**
+         * Identifies the attachment at network level.
+         *
+         * @param id long value
+         */
+        public AttachmentId(long id) {
+            super(id);
+        }
+    }
+
+    /**
+     * An exception indicating a an error happened in the BNG programmable
+     * behaviour.
+     */
+    class BngProgrammableException extends Exception {
+        /**
+         * Creates a new exception for the given message.
+         *
+         * @param message message
+         */
+        public BngProgrammableException(String message) {
+            super(message);
+        }
+    }
+}
+
+
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/AbstractP4RuntimeHandlerBehaviour.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/AbstractP4RuntimeHandlerBehaviour.java
index 222cdaa..49f79f9 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/AbstractP4RuntimeHandlerBehaviour.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/AbstractP4RuntimeHandlerBehaviour.java
@@ -38,7 +38,7 @@
     PiTranslationService translationService;
 
 
-    AbstractP4RuntimeHandlerBehaviour() {
+    public AbstractP4RuntimeHandlerBehaviour() {
         super(P4RuntimeController.class);
     }
 
diff --git a/pipelines/fabric/impl/BUILD b/pipelines/fabric/impl/BUILD
index e4b427e..3e8824d 100644
--- a/pipelines/fabric/impl/BUILD
+++ b/pipelines/fabric/impl/BUILD
@@ -6,6 +6,7 @@
     "//pipelines/basic:onos-pipelines-basic",
     "//core/store/serializers:onos-core-serializers",
     "//apps/inbandtelemetry/api:onos-apps-inbandtelemetry-api",
+    "//drivers/p4runtime:onos-drivers-p4runtime",
 ]
 
 osgi_jar_with_tests(
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