VOL-3464: DHCP packets can be trapped to ONOS without Authentication
- ACLs can be sometimes have very broad classifiers to work around the limited number
of ACLs that can be installed on the BAL. This would mean that unwanted
packets can sometimes be trapped to host and could be used by the end user
for DDoS attacks by sending spurious packets that match broad classifier BAL ACLs.
This patch introduces some very low level checks on the trapped packets
at the openolt agent application. The packets could be trapped at the application
as a result of broad level BAL ACL classifiers, but the application
does more low checks derived from VOLTHA flow and only then allows the packets to
VOLTHA system if the low checks pass.
- Update BUILDING.md with details of installing PcapPlusPlus library.
- Bump version to 3.1.0
Change-Id: I53eee968c51659c31353eeb5d8d4e144a443c293
diff --git a/BUILDING.md b/BUILDING.md
index 4454822..ac1a2fa 100644
--- a/BUILDING.md
+++ b/BUILDING.md
@@ -42,7 +42,7 @@
Install the following packages
- `apt-get install -y git pkg-config build-essential autoconf libgflags-dev clang libc++-dev unzip libssl-dev gawk debhelper debhelper dh-systemd init-system-helpers curl cmake ccache g++-4.9 wget ca-certificates lcov libgoogle-glog-dev`
+ `apt-get install -y git pkg-config build-essential autoconf libgflags-dev clang libc++-dev unzip libssl-dev gawk debhelper debhelper dh-systemd init-system-helpers curl cmake ccache g++-4.9 wget ca-certificates lcov libgoogle-glog-dev libpcap-dev`
Follow the instructions [here](https://docs.docker.com/engine/install/debian/) to install `docker-ce`. It is not necessary to install `docker-ce-cli` and `containerd.io`.
@@ -81,7 +81,16 @@
cp `find . -name grpc_cpp_plugin` /usr/local/bin/
```
-6. Install latest version of Git (optional)
+6. Install PcapPlusPlus library
+
+```shell
+git clone -b v20.08 https://github.com/seladb/PcapPlusPlus.git
+./configure-linux.sh —default
+make all
+make install
+```
+
+7. Install latest version of Git (optional)
By default the apt-get install an older version of git (2.1.4) on debian jessie 8.11.1. This older version does not support some of the latest git options. You may want to install a later version of git using procedure below.
diff --git a/Makefile b/Makefile
index b89f58e..7b8deb9 100644
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,7 @@
DOCKER_REGISTRY ?=
DOCKER_REPOSITORY ?= voltha/
DOCKER_EXTRA_ARGS ?=
-DOCKER_TAG ?= 2.0.1
+DOCKER_TAG ?= 2.1.0
IMAGENAME = ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}openolt-test:${DOCKER_TAG}
DOCKER_BUILD_ARGS ?= \
diff --git a/VERSION b/VERSION
index 05d78bc..fd2a018 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.0.1-dev
+3.1.0
diff --git a/agent/Makefile.in b/agent/Makefile.in
index ad4c641..7512d1e 100644
--- a/agent/Makefile.in
+++ b/agent/Makefile.in
@@ -81,9 +81,13 @@
LIBZ_PATH=$(shell pkg-config --libs-only-L zlib | sed s/-L// | sed s/\ //g)
LIBPROTOBUF_PATH=$(shell PKG_CONFIG_ALLOW_SYSTEM_LIBS=true pkg-config --libs-only-L protobuf | sed s/-L// | sed s/\ //g)
+PCAPPLUSPLUS_INCLUDE_PATH=-I/usr/local/include/pcapplusplus/
+PCAPPLUSPLUS_LIB_PATH=-lPcap++ -lPacket++ -lCommon++
+
CXX = @CXX@-4.9
CXXFLAGS += @CXXFLAGS@
CXXFLAGS += $(shell pkg-config --cflags-only-I grpc++)
+CXXFLAGS += $(PCAPPLUSPLUS_INCLUDE_PATH)
CPPFLAGS += @CPPFLAGS@
CPPFLAGS += `pkg-config --cflags protobuf grpc`
@@ -247,7 +251,7 @@
.DEFAULT_GOAL := all
all: $(BUILD_DIR)/openolt
$(BUILD_DIR)/openolt: sdk protos $(OBJS)
- $(CXX) $(LDFLAGS) -L$(BALLIBDIR) $(OBJS) $(OPENOLT_API_LIB) $(LIBPROTOBUF_PATH)/libprotobuf.a -o $@ -l$(BALLIBNAME) $(shell pkg-config --libs protobuf grpc++ grpc)
+ $(CXX) $(LDFLAGS) -L$(BALLIBDIR) $(OBJS) $(OPENOLT_API_LIB) $(LIBPROTOBUF_PATH)/libprotobuf.a -o $@ -l$(BALLIBNAME) $(shell pkg-config --libs protobuf grpc++ grpc) $(PCAPPLUSPLUS_LIB_PATH)
ln -sf $(TOP_DIR)/$(BAL_DIR)/build/fs/$(OPENOLTDEVICE)/release/release_$(OPENOLTDEVICE)_V$(BAL_VER).$(DEV_VER).tar.gz $(BUILD_DIR)/.
ln -sf $(TOP_DIR)/$(BAL_DIR)/build/host_reference/host_api/strip/libbal_host_api.so $(BUILD_DIR)/.
ln -sf $(TOP_DIR)/$(BAL_DIR)/build/host_driver/dev_mgmt_daemon/dev_mgmt_daemon $(BUILD_DIR)/.
diff --git a/agent/src/core_api_handler.cc b/agent/src/core_api_handler.cc
index 32bcfcc..8c2df06 100644
--- a/agent/src/core_api_handler.cc
+++ b/agent/src/core_api_handler.cc
@@ -342,6 +342,8 @@
bcmos_fastlock_init(&voltha_flow_to_device_flow_lock, 0);
bcmos_fastlock_init(&alloc_cfg_wait_lock, 0);
bcmos_fastlock_init(&onu_deactivate_wait_lock, 0);
+ bcmos_fastlock_init(&acl_packet_trap_handler_lock, 0);
+
OPENOLT_LOG(INFO, openolt_log_id, "Enable OLT - %s-%s\n", VENDOR_ID, MODEL_ID);
//check BCM daemon is connected or not
@@ -1497,11 +1499,6 @@
return ::Status(grpc::StatusCode::ALREADY_EXISTS, "voltha-flow-already-installed");
}
- // Trap-to-host voltha flows need not be replicated as they are installed as ACLs, not BAL flows.
- if (action.cmd().trap_to_host()) {
- replicate_flow = false;
- }
-
// This is the case of symmetric_voltha_flow_id
// If symmetric_voltha_flow_id is available and valid in the Flow message,
// check if it is installed, and use the corresponding device_flow_id
@@ -1677,7 +1674,7 @@
BCMOLT_MSG_FIELD_SET(&cfg, cookie, cookie);
if (action.cmd().trap_to_host()) {
- Status resp = handle_acl_rule_install(onu_id, flow_id, flow_type, access_intf_id,
+ Status resp = handle_acl_rule_install(onu_id, flow_id, gemport_id, flow_type, access_intf_id,
network_intf_id, classifier);
return resp;
}
@@ -2055,7 +2052,7 @@
OPENOLT_LOG(INFO, openolt_log_id, "flow remove received for flow_id=%u, flow_type=%s\n",
flow_id, flow_type.c_str());
- bcmos_fastlock_lock(&data_lock);
+ bcmos_fastlock_lock(&acl_packet_trap_handler_lock);
flow_id_flow_direction fl_id_fl_dir(flow_id, flow_type);
int32_t gemport_id = -1;
int32_t intf_id = -1;
@@ -2068,17 +2065,41 @@
// cleanup acl only if it is a valid acl. If not valid acl, it may be datapath flow.
if (acl_id >= 0) {
Status resp = handle_acl_rule_cleanup(acl_id, intf_id, flow_type);
- bcmos_fastlock_unlock(&data_lock, 0);
if (resp.ok()) {
OPENOLT_LOG(INFO, openolt_log_id, "acl removed ok for flow_id = %u with acl_id = %d\n", flow_id, acl_id);
flow_to_acl_map.erase(fl_id_fl_dir);
+
+ // When flow is being removed, extract the value corresponding to flow_id from trap_to_host_pkt_info_with_vlan_for_flow_id if it exists
+ if (trap_to_host_pkt_info_with_vlan_for_flow_id.count(flow_id) > 0) {
+ trap_to_host_pkt_info_with_vlan pkt_info_with_vlan = trap_to_host_pkt_info_with_vlan_for_flow_id[flow_id];
+ // Formulate the trap_to_host_pkt_info tuple key
+ trap_to_host_pkt_info pkt_info(std::get<0>(pkt_info_with_vlan),
+ std::get<1>(pkt_info_with_vlan),
+ std::get<2>(pkt_info_with_vlan),
+ std::get<3>(pkt_info_with_vlan));
+ // Extract the value corresponding to trap_to_host_pkt_info key from trap_to_host_vlan_ids_for_trap_to_host_pkt_info
+ // The value is a list of vlan_ids for the given trap_to_host_pkt_info key
+ // Remove the vlan_id from the list that corresponded to the flow being removed.
+ if (trap_to_host_vlan_ids_for_trap_to_host_pkt_info.count(pkt_info) > 0) {
+ trap_to_host_vlan_ids_for_trap_to_host_pkt_info[pkt_info].remove(std::get<4>(pkt_info_with_vlan));
+ } else {
+ OPENOLT_LOG(ERROR, openolt_log_id, "trap-to-host with intf_type = %d, intf_id = %d, pkt_type = %d gemport_id = %d not found in trap_to_host_vlan_ids_for_trap_to_host_pkt_info map",
+ std::get<0>(pkt_info_with_vlan), std::get<1>(pkt_info_with_vlan), std::get<2>(pkt_info_with_vlan), std::get<3>(pkt_info_with_vlan));
+ }
+
+ } else {
+ OPENOLT_LOG(ERROR, openolt_log_id, "flow id = %u not found in trap_to_host_pkt_info_with_vlan_for_flow_id map", flow_id);
+ }
} else {
OPENOLT_LOG(ERROR, openolt_log_id, "acl remove error for flow_id = %u with acl_id = %d\n", flow_id, acl_id);
}
+ bcmos_fastlock_unlock(&acl_packet_trap_handler_lock, 0);
return resp;
}
}
+ bcmos_fastlock_unlock(&acl_packet_trap_handler_lock, 0);
+ bcmos_fastlock_lock(&data_lock);
uint32_t port_no = flowid_to_port[key.flow_id];
if (key.flow_type == BCMOLT_FLOW_TYPE_DOWNSTREAM) {
flowid_to_gemport.erase(key.flow_id);
diff --git a/agent/src/core_data.cc b/agent/src/core_data.cc
index 1836263..ad66ae3 100644
--- a/agent/src/core_data.cc
+++ b/agent/src/core_data.cc
@@ -142,8 +142,8 @@
bool operator<(const acl_classifier_key& a1, const acl_classifier_key& a2)
{
- return ((a1.ether_type + 2*a1.ip_proto + 3*a1.src_port + 4*a1.dst_port) <
- (a2.ether_type + 2*a2.ip_proto + 3*a2.src_port + 4*a2.dst_port));
+ return ((a1.ether_type + 2*a1.ip_proto + 3*a1.src_port + 4*a1.dst_port + 5*a1.o_vid) <
+ (a2.ether_type + 2*a2.ip_proto + 3*a2.src_port + 4*a2.dst_port + 5*a2.o_vid));
}
typedef std::tuple<uint64_t, std::string> flow_id_flow_direction;
@@ -164,6 +164,19 @@
typedef std::tuple<uint16_t, uint8_t, std::string> acl_id_intf_id_intf_type;
std::map<acl_id_intf_id_intf_type, uint16_t> intf_acl_registration_ref_cnt;
+// Data structures to work around ACL limits on BAL -- start --
+
+// This flag is set as soon as ACL count reaches MAX_ACL_WITH_VLAN_CLASSIFIER is hit.
+// It is not reset when ACL count comes below MAX_ACL_WITH_VLAN_CLASSIFIER again
+bool max_acls_with_vlan_classifiers_hit = false;
+// Map of flow_id -> trap_to_host_pkt_info_with_vlan
+std::map<uint64_t, trap_to_host_pkt_info_with_vlan> trap_to_host_pkt_info_with_vlan_for_flow_id;
+// Map of trap_to_host_pkt_info -> cvid_list
+std::map<trap_to_host_pkt_info, std::list<uint16_t> > trap_to_host_vlan_ids_for_trap_to_host_pkt_info;
+
+// Data structures to work around ACL limits on BAL -- end --
+
+
std::bitset<MAX_ACL_ID> acl_id_bitset;
bcmos_fastlock acl_id_bitset_lock;
@@ -185,6 +198,8 @@
std::map<uint64_t, device_flow> voltha_flow_to_device_flow;
bcmos_fastlock voltha_flow_to_device_flow_lock;
+// Lock to protect critical section around handling data associated with ACL trap packet handling
+bcmos_fastlock acl_packet_trap_handler_lock;
// General purpose lock used to gaurd critical section during various API handling at the core_api_handler
bcmos_fastlock data_lock;
diff --git a/agent/src/core_data.h b/agent/src/core_data.h
index 322c834..7d76555 100644
--- a/agent/src/core_data.h
+++ b/agent/src/core_data.h
@@ -22,6 +22,16 @@
#include "Queue.h"
#include "device.h"
+// pcapplusplus packet decoder include files
+#include "Packet.h"
+#include "EthLayer.h"
+#include "IPv4Layer.h"
+#include "UdpLayer.h"
+#include "VlanLayer.h"
+
+#include <time.h>
+#include <arpa/inet.h>
+
extern "C"
{
#include <bcmolt_api.h>
@@ -46,6 +56,9 @@
#define MAX_ACL_ID 33
+#define MAX_ACL_WITH_VLAN_CLASSIFIER 10
+
+#define ANY_VLAN 4095
// **************************************//
// Enums and structures used by the core //
@@ -126,6 +139,7 @@
int16_t ip_proto;
int32_t src_port;
int32_t dst_port;
+ int16_t o_vid;
// Add more classifiers elements as needed here
// For now, ACLs will be classified only based on
// above elements.
@@ -248,6 +262,46 @@
typedef std::tuple<uint16_t, uint8_t, std::string> acl_id_intf_id_intf_type;
extern std::map<acl_id_intf_id_intf_type, uint16_t> intf_acl_registration_ref_cnt;
+// Data structures to work around ACL limits on BAL -- start --
+
+enum trap_to_host_packet_type {
+ dhcpv4 = 0,
+ lldp = 1,
+ eap = 2,
+ igmpv4 = 3,
+ unsupported_trap_to_host_pkt_type = 0xff
+};
+
+// Constants useful during trap-to-host packet header field classification
+#define EAP_ETH_TYPE 0x888e
+#define LLDP_ETH_TYPE 0x88cc
+#define IPV4_ETH_TYPE 0x0800
+#define VLAN_ETH_TYPE 0x8100
+
+#define IGMPv4_PROTOCOL 2
+#define UDP_PROTOCOL 17
+
+#define DHCP_SERVER_SRC_PORT 67
+#define DHCP_CLIENT_SRC_PORT 68
+
+// This flag is set as soon as ACL count reaches MAX_ACL_WITH_VLAN_CLASSIFIER is hit.
+// It is not reset when ACL count comes below MAX_ACL_WITH_VLAN_CLASSIFIER again
+extern bool max_acls_with_vlan_classifiers_hit;
+
+// Tuple of -> {bcmolt_flow_interface_type, intf-id, trap_to_host_packet_type, gemport-id, c-vid}
+typedef std::tuple<int32_t, uint32_t, int32_t, int32_t, uint16_t> trap_to_host_pkt_info_with_vlan;
+
+// Tuple of -> {bcmolt_flow_interface_type, intf-id, trap_to_host_packet_type, gemport-id}
+typedef std::tuple<int32_t, uint32_t, int32_t, int32_t> trap_to_host_pkt_info;
+
+// Map of flow_id -> trap_to_host_pkt_info_with_vlan
+extern std::map<uint64_t, trap_to_host_pkt_info_with_vlan> trap_to_host_pkt_info_with_vlan_for_flow_id;
+
+// Map of trap_to_host_pkt_info -> cvid_list
+extern std::map<trap_to_host_pkt_info, std::list<uint16_t> > trap_to_host_vlan_ids_for_trap_to_host_pkt_info;
+
+// Data structures to work around ACL limits on BAL -- end --
+
extern std::bitset<MAX_ACL_ID> acl_id_bitset;
extern bcmos_fastlock acl_id_bitset_lock;
@@ -265,6 +319,9 @@
extern std::map<uint64_t, device_flow> voltha_flow_to_device_flow;
extern bcmos_fastlock voltha_flow_to_device_flow_lock;
+// Lock to protect critical section around handling data associated with ACL trap packet handling
+extern bcmos_fastlock acl_packet_trap_handler_lock;
+
extern Queue<openolt::Indication> oltIndQ;
/*** ACL Handling related data end ***/
diff --git a/agent/src/core_utils.cc b/agent/src/core_utils.cc
index 6e1b17c..554e88d 100644
--- a/agent/src/core_utils.cc
+++ b/agent/src/core_utils.cc
@@ -1015,8 +1015,8 @@
bcmolt_access_control_fwd_action_type action_type = BCMOLT_ACCESS_CONTROL_FWD_ACTION_TYPE_TRAP_TO_HOST;
int acl_id = get_acl_id();
if (acl_id < 0) {
- OPENOLT_LOG(ERROR, openolt_log_id, "exhausted acl_id for eth_type = %d, ip_proto = %d, src_port = %d, dst_port = %d\n",
- acl_key.ether_type, acl_key.ip_proto, acl_key.src_port, acl_key.dst_port);
+ OPENOLT_LOG(ERROR, openolt_log_id, "exhausted acl_id for eth_type = %d, ip_proto = %d, src_port = %d, dst_port = %d o_vid = %d, max_acl_hit=%d\n",
+ acl_key.ether_type, acl_key.ip_proto, acl_key.src_port, acl_key.dst_port, acl_key.o_vid, max_acls_with_vlan_classifiers_hit);
return bcm_to_grpc_err(BCM_ERR_INTERNAL, "exhausted acl id");
}
@@ -1043,6 +1043,12 @@
BCMOLT_FIELD_SET(&c_val, classifier, src_port, acl_key.src_port);
}
+ // Make sure that max_acls_with_vlan_classifiers_hit is not true to consider o_vid for ACL classification.
+ if (acl_key.o_vid > 0 && acl_key.o_vid != ANY_VLAN && !max_acls_with_vlan_classifiers_hit) {
+ OPENOLT_LOG(DEBUG, openolt_log_id, "Access_Control classify o_vid %d\n", acl_key.o_vid);
+ BCMOLT_FIELD_SET(&c_val, classifier, o_vid, acl_key.o_vid);
+ }
+
BCMOLT_MSG_FIELD_SET(&cfg, classifier, c_val);
BCMOLT_MSG_FIELD_SET(&cfg, priority, 10000);
BCMOLT_MSG_FIELD_SET(&cfg, statistics_control, BCMOLT_CONTROL_STATE_ENABLE);
@@ -1061,6 +1067,11 @@
// Update the map that we have installed an acl for the given classfier.
acl_classifier_to_acl_id_map[acl_key] = acl_id;
+ // If there was a valid vlan classifier in the ACL and the ACL ID hit the ceiling, set max_acls_with_vlan_classifiers_hit to true
+ // After max_acls_with_vlan_classifiers_hit is set to true no more ACLs can have vlan as an ACL classifier.
+ if (acl_key.o_vid > 0 && acl_key.o_vid != ANY_VLAN && acl_id >= MAX_ACL_WITH_VLAN_CLASSIFIER) {
+ max_acls_with_vlan_classifiers_hit = true;
+ }
return Status::OK;
}
@@ -1131,18 +1142,30 @@
OPENOLT_LOG(DEBUG, openolt_log_id, "classify dst_port %d\n", classifier.dst_port());
key->dst_port = classifier.dst_port();
} else key->dst_port = -1;
+
+ // We should also check the max_acls_with_vlan_classifiers_hit flag is not false to consider the vlan for flow classifier key
+ if (classifier.o_vid() && !max_acls_with_vlan_classifiers_hit) {
+ OPENOLT_LOG(DEBUG, openolt_log_id, "classify o_vid %d\n", classifier.o_vid());
+ key->o_vid = classifier.o_vid();
+ } else key->o_vid = ANY_VLAN;
+
}
-Status handle_acl_rule_install(int32_t onu_id, uint64_t flow_id,
+Status handle_acl_rule_install(int32_t onu_id, uint64_t flow_id, int32_t gemport_id,
const std::string flow_type, int32_t access_intf_id,
int32_t network_intf_id,
const ::openolt::Classifier& classifier) {
int acl_id;
- int32_t intf_id = flow_type.compare(upstream) == 0? access_intf_id: network_intf_id;
+ uint32_t intf_id = flow_type.compare(upstream) == 0? access_intf_id: network_intf_id;
const std::string intf_type = flow_type.compare(upstream) == 0? "pon": "nni";
bcmolt_interface_type olt_if_type = intf_type == "pon"? BCMOLT_INTERFACE_TYPE_PON: BCMOLT_INTERFACE_TYPE_NNI;
Status resp;
+ trap_to_host_packet_type pkt_type = get_trap_to_host_packet_type(classifier);
+ if (pkt_type == unsupported_trap_to_host_pkt_type) {
+ OPENOLT_LOG(ERROR, openolt_log_id, "unsupported pkt trap type");
+ return Status(grpc::StatusCode::UNIMPLEMENTED, "unsupported pkt trap type");
+ }
// few map keys we are going to use later.
flow_id_flow_direction fl_id_fl_dir(flow_id, flow_type);
@@ -1150,8 +1173,8 @@
acl_classifier_key acl_key;
formulate_acl_classifier_key(&acl_key, classifier);
const acl_classifier_key acl_key_const = {.ether_type=acl_key.ether_type, .ip_proto=acl_key.ip_proto,
- .src_port=acl_key.src_port, .dst_port=acl_key.dst_port};
- bcmos_fastlock_lock(&data_lock);
+ .src_port=acl_key.src_port, .dst_port=acl_key.dst_port, .o_vid=acl_key.o_vid};
+ bcmos_fastlock_lock(&acl_packet_trap_handler_lock);
// Check if the acl is already installed
if (acl_classifier_to_acl_id_map.count(acl_key_const) > 0) {
@@ -1160,14 +1183,14 @@
if (flow_to_acl_map.count(fl_id_fl_dir)) {
- // coult happen if same trap flow is received again
+ // could happen if same trap flow is received again
OPENOLT_LOG(INFO, openolt_log_id, "flow and related acl already handled, nothing more to do\n");
- bcmos_fastlock_unlock(&data_lock, 0);
+ bcmos_fastlock_unlock(&acl_packet_trap_handler_lock, 0);
return Status::OK;
}
- OPENOLT_LOG(INFO, openolt_log_id, "Acl for flow_id=%lu with eth_type = %d, ip_proto = %d, src_port = %d, dst_port = %d already installed with acl id = %u\n",
- flow_id, acl_key.ether_type, acl_key.ip_proto, acl_key.src_port, acl_key.dst_port, acl_id);
+ OPENOLT_LOG(INFO, openolt_log_id, "Acl for flow_id=%lu with eth_type = %d, ip_proto = %d, src_port = %d, dst_port = %d o_vid = %d already installed with acl id = %u\n",
+ flow_id, acl_key.ether_type, acl_key.ip_proto, acl_key.src_port, acl_key.dst_port, acl_key.o_vid, acl_id);
// The acl_ref_cnt is needed to know how many flows refer an ACL.
// When the flow is removed, we decrement the reference count.
@@ -1183,9 +1206,9 @@
} else {
resp = install_acl(acl_key_const);
if (!resp.ok()) {
- OPENOLT_LOG(ERROR, openolt_log_id, "Acl for flow_id=%lu with eth_type = %d, ip_proto = %d, src_port = %d, dst_port = %d failed\n",
- flow_id, acl_key_const.ether_type, acl_key_const.ip_proto, acl_key_const.src_port, acl_key_const.dst_port);
- bcmos_fastlock_unlock(&data_lock, 0);
+ OPENOLT_LOG(ERROR, openolt_log_id, "Acl for flow_id=%lu with eth_type = %d, ip_proto = %d, src_port = %d, dst_port = %d o_vid = %d failed\n",
+ flow_id, acl_key_const.ether_type, acl_key_const.ip_proto, acl_key_const.src_port, acl_key_const.dst_port, acl_key_const.o_vid);
+ bcmos_fastlock_unlock(&acl_packet_trap_handler_lock, 0);
return resp;
}
@@ -1217,12 +1240,33 @@
acl_id_intf_id ac_id_if_id(acl_id, intf_id);
flow_to_acl_map[fl_id_fl_dir] = ac_id_if_id;
- bcmos_fastlock_unlock(&data_lock, 0);
+ // Populate the trap_to_host_pkt_info_with_vlan corresponding to the trap-to-host voltha flow_id key.
+ // When the trap-to-host voltha flow-id is being removed, this entry is cleared too from the map.
+ trap_to_host_pkt_info_with_vlan pkt_info_with_vlan((int32_t)olt_if_type, intf_id, (int32_t)pkt_type, gemport_id, (short unsigned int)classifier.o_vid());
+ trap_to_host_pkt_info_with_vlan_for_flow_id[flow_id] = pkt_info_with_vlan;
+ trap_to_host_pkt_info pkt_info((int32_t)olt_if_type, intf_id, (int32_t)pkt_type, gemport_id);
+ bool duplicate = false;
+ // Check if the vlan_id corresponding to the trap_to_host_pkt_info key is found. Set the 'duplicate' flag accordingly.
+ if (trap_to_host_vlan_ids_for_trap_to_host_pkt_info.count(pkt_info) > 0) {
+ auto& vlan_id_list = trap_to_host_vlan_ids_for_trap_to_host_pkt_info[pkt_info];
+ auto it = std::find(vlan_id_list.begin(), vlan_id_list.end(), acl_key.o_vid);
+ if (it != vlan_id_list.end()) {
+ OPENOLT_LOG(DEBUG, openolt_log_id, "cvid = %d exists already in list", acl_key.o_vid);
+ duplicate = true;
+ }
+ }
+ // If the vlan_id is not found corresponding to the trap_to_host_pkt_info key, update it.
+ // This will be used to validate the vlan_id in the trapped packet. If vlan_id in the
+ // trapped packet is not match with the stored value, packet is dropped.
+ if (!duplicate) {
+ trap_to_host_vlan_ids_for_trap_to_host_pkt_info[pkt_info].push_back(acl_key.o_vid);
+ }
+
+ bcmos_fastlock_unlock(&acl_packet_trap_handler_lock, 0);
return Status::OK;
}
-//Status handle_acl_rule_cleanup(int16_t acl_id, int32_t gemport_id, int32_t intf_id, const std::string flow_type) {
Status handle_acl_rule_cleanup(int16_t acl_id, int32_t intf_id, const std::string flow_type) {
const std::string intf_type= flow_type.compare(upstream) == 0 ? "pon": "nni";
acl_id_intf_id_intf_type ac_id_inf_id_inf_type(acl_id, intf_id, intf_type);
@@ -1448,3 +1492,153 @@
return NULL;
}
+
+trap_to_host_packet_type get_trap_to_host_packet_type(const ::openolt::Classifier& classifier) {
+ trap_to_host_packet_type type = unsupported_trap_to_host_pkt_type;
+ if (classifier.eth_type() == EAP_ETH_TYPE) {
+ type = eap;
+ } else if (classifier.src_port() == DHCP_SERVER_SRC_PORT || classifier.src_port() == DHCP_CLIENT_SRC_PORT) {
+ type = dhcpv4;
+ } else if (classifier.eth_type() == LLDP_ETH_TYPE) {
+ type = lldp;
+ } else if (classifier.ip_proto() == IGMPv4_PROTOCOL) {
+ type = igmpv4;
+ }
+
+ return type;
+}
+
+// is_packet_allowed extracts the VLAN, packet-type, interface-type, interface-id from incoming trap-to-host packet.
+// Then it verifies if this packet can be allowed upstream to host. It does this by checking if the vlan in the incoming packet
+//exists in trap_to_host_vlan_ids_for_trap_to_host_pkt_info map for (interface-type, interface-id, packet-type) key.
+bool is_packet_allowed(bcmolt_access_control_receive_eth_packet_data *data, int32_t gemport_id) {
+ bcmolt_interface_type intf_type = data->interface_ref.intf_type;
+ uint32_t intf_id = data->interface_ref.intf_id;
+ trap_to_host_packet_type pkt_type = unsupported_trap_to_host_pkt_type;
+ uint16_t vlan_id = 0;
+ int ethType;
+
+ struct timeval dummy_tv = {0, 0};
+ bool free_memory_of_raw_packet = false; // This indicates the pcap library to not free the message buffer. It will freed by the caller.
+
+ pcpp::RawPacket rawPacket(data->buffer.arr, data->buffer.len, dummy_tv, free_memory_of_raw_packet, pcpp::LINKTYPE_ETHERNET);
+ pcpp::Packet parsedPacket(&rawPacket);
+ pcpp::EthLayer* ethernetLayer = parsedPacket.getLayerOfType<pcpp::EthLayer>();
+ if (ethernetLayer == NULL)
+ {
+ OPENOLT_LOG(ERROR, openolt_log_id, "Something went wrong, couldn't find Ethernet layer\n");
+ return false;
+ }
+
+ // Getting Vlan layer
+ pcpp::VlanLayer* vlanLayer = parsedPacket.getLayerOfType<pcpp::VlanLayer>();
+ if (vlanLayer == NULL)
+ {
+ // Allow Untagged LLDP Ether type packet to trap from NNI
+ if (ntohs(ethernetLayer->getEthHeader()->etherType) == LLDP_ETH_TYPE && intf_type == BCMOLT_INTERFACE_TYPE_NNI) {
+ return true;
+ } else {
+ OPENOLT_LOG(WARNING, openolt_log_id, "untagged packets other than lldp packets are dropped. ethertype=%d, intftype=%d, intf_id=%d\n",
+ ntohs(ethernetLayer->getEthHeader()->etherType), intf_type, intf_id);
+ return false;
+ }
+ } else {
+ ethType = ntohs(vlanLayer->getVlanHeader()->etherType);
+ if (ethType == EAP_ETH_TYPE) { // single tagged packet with EAPoL payload
+ vlan_id = vlanLayer->getVlanID();
+ pkt_type = eap;
+ } else if (ethType == IPV4_ETH_TYPE) { // single tagged packet with IPv4 payload
+ vlan_id = vlanLayer->getVlanID();
+ vlanLayer->parseNextLayer();
+ pcpp::IPv4Layer *ipv4Layer = (pcpp::IPv4Layer*)vlanLayer->getNextLayer();
+ if(ipv4Layer->getIPv4Header()->protocol == UDP_PROTOCOL) { // UDP payload
+ // Check the UDP Ports to see if it is a DHCPv4 packet
+ ipv4Layer->parseNextLayer();
+ pcpp::UdpLayer *udpLayer = (pcpp::UdpLayer*)ipv4Layer->getNextLayer();
+ if (ntohs(udpLayer->getUdpHeader()->portSrc) == DHCP_SERVER_SRC_PORT|| ntohs(udpLayer->getUdpHeader()->portSrc) == DHCP_CLIENT_SRC_PORT) {
+ pkt_type = dhcpv4;
+ } else {
+ OPENOLT_LOG(ERROR, openolt_log_id, "unsupported udp source port = %d\n", ntohs(udpLayer->getUdpHeader()->portSrc));
+ return false;
+ }
+ } else if (ipv4Layer->getIPv4Header()->protocol == IGMPv4_PROTOCOL) { // Igmpv4 payload
+ pkt_type = igmpv4;
+ } else {
+ OPENOLT_LOG(ERROR, openolt_log_id, "unsupported ip protocol = %d\n", ipv4Layer->getIPv4Header()->protocol);
+ return false;
+ }
+ } else if (ethType == VLAN_ETH_TYPE) { // double tagged packet
+
+ // Trap-to-host from NNI flows do not specify the VLANs, so no vlan validation is necessary.
+ if (intf_type == BCMOLT_INTERFACE_TYPE_NNI) {
+ return true;
+ }
+
+ // Here we parse the inner vlan payload and currently support only IPv4 packets
+
+ // Extract the vlan_id for trap-to-host packets arriving from the PON
+ // trap-to-host ACLs from the NNI do not care about VLAN.
+ if (intf_type == BCMOLT_INTERFACE_TYPE_PON) {
+ vlan_id = vlanLayer->getVlanID(); // This is the outer vlan id
+ }
+ vlanLayer->parseNextLayer();
+ vlanLayer = (pcpp::VlanLayer*)vlanLayer->getNextLayer(); // Here we extract the inner vlan layer
+ ethType = ntohs(vlanLayer->getVlanHeader()->etherType);
+ if (ethType == IPV4_ETH_TYPE) { // IPv4
+ uint16_t _inner_vlan_id = vlanLayer->getVlanID();
+ vlanLayer->parseNextLayer();
+ pcpp::IPv4Layer *ipv4Layer = (pcpp::IPv4Layer*)vlanLayer->getNextLayer(); // here we extract the inner vlan IPv4 payload
+ if(ipv4Layer->getIPv4Header()->protocol == UDP_PROTOCOL) { // UDP payload
+ // Check the UDP Ports to see if it is a DHCPv4 packet
+ ipv4Layer->parseNextLayer();
+ pcpp::UdpLayer *udpLayer = (pcpp::UdpLayer*)ipv4Layer->getNextLayer();
+ if (ntohs(udpLayer->getUdpHeader()->portSrc) == DHCP_SERVER_SRC_PORT || ntohs(udpLayer->getUdpHeader()->portSrc) == DHCP_CLIENT_SRC_PORT) {
+ pkt_type = dhcpv4;
+ } else {
+ OPENOLT_LOG(ERROR, openolt_log_id, "unsupported udp source port = %d\n", ntohs(udpLayer->getUdpHeader()->portSrc));
+ return false;
+ }
+ } else if (ipv4Layer->getIPv4Header()->protocol == IGMPv4_PROTOCOL) { // Igmpv4 payload
+ pkt_type = igmpv4;
+ } else {
+ OPENOLT_LOG(ERROR, openolt_log_id, "unsupported ip protocol = %d\n", ipv4Layer->getIPv4Header()->protocol)
+ return false;
+ }
+ }
+ } else {
+ OPENOLT_LOG(ERROR, openolt_log_id, "unsupported ether type = 0x%x\n", ntohs((vlanLayer->getVlanHeader()->etherType)));
+ return false;
+ }
+ }
+
+#if 0 // Debug logs for test purpose only
+ std::cout << "vlan of received packet " << vlan_id << " intf_type " << intf_type << " intf_id " <<intf_id << " pkt_type " <<pkt_type << " gem_port_id" << gemport_id << "\n";
+ for(std::map<trap_to_host_pkt_info, std::list<uint16_t> >::const_iterator it = trap_to_host_vlan_ids_for_trap_to_host_pkt_info.begin();
+ it != trap_to_host_vlan_ids_for_trap_to_host_pkt_info.end(); ++it)
+ {
+ std::cout << "value entries" << " " << std::get<0>(it->first) << " "<< std::get<1>(it->first) << " "<< std::get<2>(it->first) << " "<< std::get<3>(it->first) << "\n\n";
+ std::cout << "vlans for the above key are => ";
+ for (std::list<uint16_t>::const_iterator _it=it->second.begin();
+ _it != it->second.end();
+ ++_it) {
+ std::cout << *_it << " ";
+ }
+ std::cout << "\n\n";
+ }
+#endif
+
+ trap_to_host_pkt_info pkt_info(intf_type, intf_id, pkt_type, gemport_id);
+ // Check for matching vlan only if the trap_to_host_pkt_info exists in the trap_to_host_vlan_ids_for_trap_to_host_pkt_info map
+ if (trap_to_host_vlan_ids_for_trap_to_host_pkt_info.count(pkt_info) > 0) {
+ // Iterate throught the vlan list to find matching vlan
+ auto& vlan_id_list = trap_to_host_vlan_ids_for_trap_to_host_pkt_info[pkt_info];
+ for (auto allowed_vlan_id : vlan_id_list) {
+ // Found exact matching vlan in the allowed list of vlans for the trap_to_host_pkt_info key or
+ // there is generic match ANY_VLAN in the list in the allowed vlan list.
+ if (allowed_vlan_id == vlan_id || allowed_vlan_id == ANY_VLAN) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
diff --git a/agent/src/core_utils.h b/agent/src/core_utils.h
index e4eec7f..8404912 100644
--- a/agent/src/core_utils.h
+++ b/agent/src/core_utils.h
@@ -98,7 +98,7 @@
Status install_acl(const acl_classifier_key acl_key);
Status remove_acl(int acl_id);
void formulate_acl_classifier_key(acl_classifier_key *key, const ::openolt::Classifier& classifier);
-Status handle_acl_rule_install(int32_t onu_id, uint64_t flow_id,
+Status handle_acl_rule_install(int32_t onu_id, uint64_t flow_id, int32_t gemport_id,
const std::string flow_type, int32_t access_intf_id,
int32_t network_intf_id,
const ::openolt::Classifier& classifier);
@@ -114,4 +114,6 @@
bool is_voltha_flow_installed(uint64_t voltha_flow_id );
const device_flow* get_device_flow(uint64_t voltha_flow_id);
const device_flow_params* get_device_flow_params(uint64_t voltha_flow_id);
+trap_to_host_packet_type get_trap_to_host_packet_type(const ::openolt::Classifier& classifier);
+bool is_packet_allowed(bcmolt_access_control_receive_eth_packet_data *data, int32_t gemport_id);
#endif // OPENOLT_CORE_UTILS_H_
diff --git a/agent/src/indications.cc b/agent/src/indications.cc
index 03f81fe..0583ddb 100644
--- a/agent/src/indications.cc
+++ b/agent/src/indications.cc
@@ -490,18 +490,28 @@
static void PacketIndication(bcmolt_devid olt, bcmolt_msg *msg) {
openolt::Indication ind;
- openolt::PacketIndication* pkt_ind = new openolt::PacketIndication;
switch (msg->obj_type) {
case BCMOLT_OBJ_ID_ACCESS_CONTROL:
switch (msg->subgroup) {
case BCMOLT_ACCESS_CONTROL_AUTO_SUBGROUP_RECEIVE_ETH_PACKET:
{
+ int32_t gemport_id;
bcmolt_access_control_receive_eth_packet *pkt =
(bcmolt_access_control_receive_eth_packet*)msg;
bcmolt_access_control_receive_eth_packet_data *pkt_data =
&((bcmolt_access_control_receive_eth_packet*)msg)->data;
+ // Set the gemport_id to be passed to is_packet_allowed function
+ gemport_id = pkt_data->svc_port_id == BCMOLT_SERVICE_PORT_ID_INVALID ? -1 : pkt_data->svc_port_id;
+
+ // Allow the packet to host only if "is_packet_allowed" routine returns true, else drop the packet.
+ if (! is_packet_allowed(pkt_data, gemport_id) ) {
+ OPENOLT_LOG(WARNING, openolt_log_id, "packet not allowed to host\n");
+ bcmolt_msg_free(msg);
+ return;
+ }
+ openolt::PacketIndication* pkt_ind = new openolt::PacketIndication;
pkt_ind->set_intf_type(bcmolt_to_grpc_interface_rf__intf_type(
(bcmolt_interface_type)pkt_data->interface_ref.intf_type));
pkt_ind->set_intf_id((bcmolt_interface_id)pkt_data->interface_ref.intf_id);
@@ -513,8 +523,8 @@
OPENOLT_LOG(INFO, openolt_log_id, "packet indication, ingress intf_type %s, ingress intf_id %d, gem_port %d\n",
pkt_ind->intf_type().c_str(), pkt_ind->intf_id(), pkt_data->svc_port_id);
} else if (pkt_data->interface_ref.intf_type == BCMOLT_INTERFACE_TYPE_NNI ) {
- OPENOLT_LOG(INFO, openolt_log_id, "packet indication, ingress intf_type %s, ingress intf_id %d\n",
- pkt_ind->intf_type().c_str(), pkt_ind->intf_id());
+ OPENOLT_LOG(INFO, openolt_log_id, "packet indication, ingress intf_type %s, ingress intf_id %d, gem_port %d \n",
+ pkt_ind->intf_type().c_str(), pkt_ind->intf_id(), pkt_data->svc_port_id);
}
}
}
diff --git a/agent/test/Makefile b/agent/test/Makefile
index e54da02..56bcce7 100644
--- a/agent/test/Makefile
+++ b/agent/test/Makefile
@@ -32,11 +32,11 @@
CXX = g++
CXXFLAGS += -g -O2
-CXXFLAGS += `pkg-config --cflags-only-I grpc++`
+CXXFLAGS += `pkg-config --cflags-only-I grpc++` -I/usr/local/include/pcapplusplus/
CPPFLAGS += `pkg-config --cflags protobuf grpc`
CXXFLAGS += -std=c++11 -fpermissive -Wno-literal-suffix
CXXFLAGS += -DTEST_MODE -DENABLE_LOG
-LDFLAGS += -lpthread -lm `pkg-config --libs protobuf grpc++ grpc` -ldl -lgpr
+LDFLAGS += -lpthread -lm `pkg-config --libs protobuf grpc++ grpc` -ldl -lgpr -lPcap++ -lPacket++ -lCommon++
CXXFLAGSDEVICE = -I../device -I../device/$(OPENOLTDEVICE)
export CXX CXXFLAGS OPENOLT_PROTO_VER