diff --git a/dash-pipeline/SAI/src/dashsai.cpp b/dash-pipeline/SAI/src/dashsai.cpp index 8db034fc4..e61a1914d 100644 --- a/dash-pipeline/SAI/src/dashsai.cpp +++ b/dash-pipeline/SAI/src/dashsai.cpp @@ -7,6 +7,7 @@ extern "C" { #include using namespace dash; +using namespace dash::utils; #define DASH_BMV2_CPU_QOS_NUMBER_OF_QUEUES 0 @@ -607,6 +608,52 @@ grpc::StatusCode DashSai::mutateTableEntry( return status.error_code(); } +grpc::StatusCode DashSai::readTableEntry( + _Inout_ std::shared_ptr entry) +{ + DASH_LOG_ENTER(); + + if (!m_apiInitialized) + { + DASH_LOG_ERROR("api not initialized"); + + return grpc::StatusCode::CANCELLED; + } + + p4::v1::ReadRequest request; + + request.set_device_id(m_cfg->m_deviceId); + + auto entity = request.add_entities(); + + entity->set_allocated_table_entry(entry.get()); + + grpc::ClientContext context; + + auto client_reader = m_stub->Read(&context, request); + assert(client_reader); + + p4::v1::ReadResponse rep; + if (client_reader->Read(&rep)) { + assert(rep.entities_size() == 1); + entity->release_table_entry(); + entity = rep.mutable_entities(0); + entry->CopyFrom(entity->table_entry()); + } + + auto status = client_reader->Finish(); + +exit: + if (status.ok()) { + DASH_LOG_NOTICE("GRPC call Read OK %s", entry->ShortDebugString().c_str()); + } + else { + DASH_LOG_ERROR("GRPC ERROR[%d]: %s, %s", status.error_code(), status.error_message().c_str(), status.error_details().c_str()); + } + + return status.error_code(); +} + bool DashSai::insertInTable( _In_ std::shared_ptr entry, _In_ sai_object_id_t objId) @@ -705,6 +752,40 @@ bool DashSai::removeFromTable( return retCode == grpc::StatusCode::OK; } +bool DashSai::getFromTable( + _In_ sai_object_id_t id, + _Out_ std::shared_ptr &entry) +{ + DASH_LOG_ENTER(); + + if (!m_apiInitialized) + { + DASH_LOG_ERROR("api not initialized"); + + return false; + } + + MUTEX; + + auto range = m_tableEntryMap.equal_range(id); + + if (range.first == range.second) + { + DASH_LOG_ERROR("id: 0x%lx not present in the table for deletion!", id); + + return false; + } + + for (auto itr = range.first; itr != range.second; ++itr) + { + entry = itr->second; + return true; + + } + + return false; +} + // QUAD generic api implementation sai_status_t DashSai::create( @@ -960,4 +1041,182 @@ sai_status_t DashSai::bulk_remove_objects( } return agg_status; -} \ No newline at end of file +} + +sai_status_t DashSai::set( + _In_ const P4MetaTable &meta_table, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr) +{ + DASH_LOG_ENTER(); + DASH_CHECK_API_INITIALIZED(); + + std::shared_ptr matchActionEntry = nullptr; + if (!getFromTable(objectId, matchActionEntry)) { + return SAI_STATUS_FAILURE; + } + + // Search attr in table action params + auto action = matchActionEntry->mutable_action()->mutable_action(); + pi_p4_id_t action_id = action->action_id(); + auto meta_param = meta_table.get_meta_action_param(action_id, attr->id); + if (meta_param) { + auto pair_param = get_action_param_pair_from_p4_table_entry(meta_param, matchActionEntry); + if (pair_param.second) { + set_attr_ipaddr_family_to_p4(attr->value, pair_param.second); + } + + assert(pair_param.first); + set_attr_value_to_p4(meta_param->field, meta_param->bitwidth, attr->value, pair_param.first); + + auto ret = mutateTableEntry(matchActionEntry, p4::v1::Update_Type_MODIFY); + return ret == grpc::StatusCode::OK ? SAI_STATUS_SUCCESS : SAI_STATUS_FAILURE; + } + + // Search attr in table match fields + auto meta_key = meta_table.get_meta_key(attr->id); + if (meta_key) { + std::shared_ptr new_entry = std::make_shared(); + new_entry->CopyFrom(*matchActionEntry); + + auto pair_key = get_match_pair_from_p4_table_entry(meta_key, new_entry); + if (pair_key.second) { + set_attr_ipaddr_family_to_p4(attr->value, pair_key.second->mutable_exact()); + } + + assert(pair_key.first); + if (meta_key->match_type == "ternary" && string_has_suffix(meta_key->name, "_MASK")) { + set_attr_value_mask_to_p4_ternary(meta_key->field, meta_key->bitwidth, attr->value, + pair_key.first->mutable_ternary()); + } + else { + set_attr_value_to_p4_match(*meta_key, attr->value, pair_key.first); + } + + removeFromTable(objectId); + auto ret = insertInTable(new_entry, objectId); + return ret ? SAI_STATUS_SUCCESS : SAI_STATUS_FAILURE; + } + + return SAI_STATUS_FAILURE; +} + +sai_status_t DashSai::set( + _In_ const P4MetaTable &meta_table, + _Inout_ std::shared_ptr matchActionEntry, + _In_ const sai_attribute_t *attr) +{ + DASH_LOG_ENTER(); + DASH_CHECK_API_INITIALIZED(); + + if (grpc::StatusCode::OK != readTableEntry(matchActionEntry)) { + return SAI_STATUS_FAILURE; + } + + // Search attr in table action params + auto action = matchActionEntry->mutable_action()->mutable_action(); + pi_p4_id_t action_id = action->action_id(); + auto meta_param = meta_table.get_meta_action_param(action_id, attr->id); + if (meta_param) { + auto pair_param = get_action_param_pair_from_p4_table_entry(meta_param, matchActionEntry); + if (pair_param.second) { + set_attr_ipaddr_family_to_p4(attr->value, pair_param.second); + } + + assert(pair_param.first); + set_attr_value_to_p4(meta_param->field, meta_param->bitwidth, attr->value, pair_param.first); + + auto ret = mutateTableEntry(matchActionEntry, p4::v1::Update_Type_MODIFY); + return ret == grpc::StatusCode::OK ? SAI_STATUS_SUCCESS : SAI_STATUS_FAILURE; + } + + return SAI_STATUS_FAILURE; +} + +sai_status_t DashSai::get( + _In_ const P4MetaTable &meta_table, + _In_ sai_object_id_t objectId, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) +{ + DASH_LOG_ENTER(); + DASH_CHECK_API_INITIALIZED(); + + std::shared_ptr matchActionEntry = nullptr; + if (!getFromTable(objectId, matchActionEntry)) { + return SAI_STATUS_FAILURE; + } + + auto action = matchActionEntry->mutable_action()->mutable_action(); + pi_p4_id_t action_id = action->action_id(); + + for (uint32_t i = 0; i < attr_count; i++) { + if (auto meta_param = meta_table.get_meta_action_param(action_id, attr_list[i].id)) { + // attr in table action params + auto pair_param = get_action_param_pair_from_p4_table_entry(meta_param, matchActionEntry); + if (pair_param.second) { + get_attr_ipaddr_family_from_p4(pair_param.second, attr_list[i].value); + } + + assert(pair_param.first); + get_attr_value_from_p4(meta_param->field, meta_param->bitwidth, pair_param.first, attr_list[i].value); + } + else if (auto meta_key = meta_table.get_meta_key(attr_list[i].id)) { + // attr in table keys + auto pair_key = get_match_pair_from_p4_table_entry(meta_key, matchActionEntry); + if (pair_key.second) { + get_attr_ipaddr_family_from_p4(pair_key.second->mutable_exact(), attr_list[i].value); + } + + assert(pair_key.first); + if (meta_key->match_type == "ternary" && string_has_suffix(meta_key->name, "_MASK")) { + get_attr_value_mask_from_p4_ternary(meta_key->field, meta_key->bitwidth, + pair_key.first->mutable_ternary(), attr_list[i].value); + } + else { + get_attr_value_from_p4_match(*meta_key, pair_key.first, attr_list[i].value); + } + } + else { + DASH_LOG_ERROR("failed to get value for attr %d", attr_list[i].id); + } + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t DashSai::get( + _In_ const P4MetaTable &meta_table, + _Inout_ std::shared_ptr matchActionEntry, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) +{ + DASH_LOG_ENTER(); + DASH_CHECK_API_INITIALIZED(); + + if (grpc::StatusCode::OK != readTableEntry(matchActionEntry)) { + return SAI_STATUS_FAILURE; + } + + auto action = matchActionEntry->mutable_action()->mutable_action(); + pi_p4_id_t action_id = action->action_id(); + + for (uint32_t i = 0; i < attr_count; i++) { + if (auto meta_param = meta_table.get_meta_action_param(action_id, attr_list[i].id)) { + // attr in table action params + auto pair_param = get_action_param_pair_from_p4_table_entry(meta_param, matchActionEntry); + if (pair_param.second) { + get_attr_ipaddr_family_from_p4(pair_param.second, attr_list[i].value); + } + + assert(pair_param.first); + get_attr_value_from_p4(meta_param->field, meta_param->bitwidth, pair_param.first, attr_list[i].value); + } + else { + DASH_LOG_ERROR("failed to get value for attr %d", attr_list[i].id); + } + } + + return SAI_STATUS_SUCCESS; +} + diff --git a/dash-pipeline/SAI/src/dashsai.h b/dash-pipeline/SAI/src/dashsai.h index 1abccf8a5..1d1517872 100644 --- a/dash-pipeline/SAI/src/dashsai.h +++ b/dash-pipeline/SAI/src/dashsai.h @@ -3,6 +3,7 @@ #include "utils.h" #include "config.h" #include "objectidmanager.h" +#include "p4meta.h" namespace dash { @@ -51,6 +52,29 @@ namespace dash _In_ uint32_t attr_count, _Inout_ sai_attribute_t *attr_list); + // QUAD api implementation, using p4 meta table + sai_status_t set( + _In_ const P4MetaTable &meta_table, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr); + + sai_status_t set( + _In_ const P4MetaTable &meta_table, + _Inout_ std::shared_ptr matchActionEntry, + _In_ const sai_attribute_t *attr); + + sai_status_t get( + _In_ const P4MetaTable &meta_table, + _In_ sai_object_id_t objectId, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list); + + sai_status_t get( + _In_ const P4MetaTable &meta_table, + _Inout_ std::shared_ptr matchActionEntry, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list); + private: // QUAD api implementation // switch @@ -85,6 +109,9 @@ namespace dash _In_ std::shared_ptr, _In_ p4::v1::Update_Type updateType); + grpc::StatusCode readTableEntry( + _Inout_ std::shared_ptr); + sai_object_id_t getNextObjectId( _In_ sai_object_type_t objectType); @@ -95,6 +122,10 @@ namespace dash bool removeFromTable( _In_ sai_object_id_t id); + bool getFromTable( + _In_ sai_object_id_t id, + _Out_ std::shared_ptr &entry); + public: // default attributes helper static std::vector populateDefaultAttributes( diff --git a/dash-pipeline/SAI/src/p4meta.cpp b/dash-pipeline/SAI/src/p4meta.cpp new file mode 100644 index 000000000..7c9b068e3 --- /dev/null +++ b/dash-pipeline/SAI/src/p4meta.cpp @@ -0,0 +1,361 @@ +extern "C" { +#include "saimetadata.h" +} + +#include "utils.h" +#include "p4meta.h" + +using namespace dash; +using namespace dash::utils; + +namespace dash +{ + const P4MetaKey* P4MetaTable::get_meta_key( + _In_ sai_attr_id_t attr_id) const + { + for (auto i=0u; isecond.params; + for (auto i=0u; ifirst; + } + + auto itr = extra_fields.find("ACTION"); + assert(itr != extra_fields.end()); + + for (uint32_t i = 0; i < attr_count; i++) { + if (attr_list[i].id == itr->second) { + uint32_t action_enum_id = attr_list[i].value.u32; + for (auto &action: actions) { + if (action.second.enum_id == action_enum_id) { + return action.first; + } + } + break; + } + } + + return 0; // invalid p4 action id + } + + // + // helper functions, set/get attr to/from p4 match|action + // + + void set_attr_value_to_p4( + _In_ const std::string &field, + _In_ uint32_t bitwidth, + _In_ const sai_attribute_value_t &value, + _Inout_ p4::v1::FieldMatch_LPM *mf_lpm) + { + assert (field == "ipPrefix"); + return ipPrefixSetVal(value, mf_lpm, bitwidth); + } + + void get_attr_value_from_p4( + _In_ const std::string &field, + _In_ uint32_t bitwidth, + _In_ const p4::v1::FieldMatch_LPM *mf_lpm, + _Out_ sai_attribute_value_t &value) + { + assert (field == "ipPrefix"); + const char *v = mf_lpm->value().c_str(); + auto prefix_len = mf_lpm->prefix_len(); + + if (value.ipprefix.addr_family == SAI_IP_ADDR_FAMILY_IPV4) { + uint32_t val = *(const uint32_t*)v; + prefix_len -= 96; + assert (prefix_len <= 32); + value.ipprefix.addr.ip4 = val; + value.ipprefix.mask.ip4 = 0xffffffff << (32 - prefix_len); + } + else { + assert (prefix_len <= 128); + uint8_t netmask[16] = { 0 }; + int i; + + for (i = 0; i < prefix_len/8; i++) + netmask[i] = 0xff; + if (prefix_len % 8 != 0) + netmask[i] = (uint8_t)(0xff << (8 - prefix_len%8)); + memcpy(value.ipprefix.mask.ip6, netmask, 16); + } + } + + + void set_attr_value_mask_to_p4_ternary( + _In_ const std::string &field, + _In_ uint32_t bitwidth, + _In_ const sai_attribute_value_t &value, + _Inout_ p4::v1::FieldMatch_Ternary *mf_ternary) + { + if (field == "ipaddr") + return ipaddrSetMask(value, mf_ternary, bitwidth); + if (field == "u32") + return u32SetMask(value, mf_ternary, bitwidth); + if (field == "u64") + return u64SetMask(value, mf_ternary, bitwidth); + + assert(0); + } + + void get_attr_value_mask_from_p4_ternary( + _In_ const std::string &field, + _In_ uint32_t bitwidth, + _In_ const p4::v1::FieldMatch_Ternary *mf_ternary, + _Out_ sai_attribute_value_t &value) + + { + const char *v = mf_ternary->mask().c_str(); + + if (field == "ipaddr") { + if (value.ipaddr.addr_family == SAI_IP_ADDR_FAMILY_IPV4) { + uint32_t val = *(const uint32_t*)v; + value.ipaddr.addr.ip4 = val; + } + else { + memcpy(value.ipaddr.addr.ip6, v, 16); + } + } + else if (field == "u32") { + uint32_t val = *(const uint32_t*)v; + value.u32 = ntohl(val) >> (32 - bitwidth); + } + else if (field == "u64") { + uint64_t val = *(const uint64_t*)v; + if (*reinterpret_cast("\0\x01") == 0) { // Little Endian + value.u64 = be64toh(val) >> (64 - bitwidth); + } + else { + value.u64 = val & ((1ul<mutable_exact(); + set_attr_value_to_p4(key.field, key.bitwidth, value, mf_exact); + } + else if (key.match_type == "lpm") { + auto mf_lpm = mf->mutable_lpm(); + if (getPrefixLength(value) == 0) + { + // https://github.com/p4lang/PI/blob/24e0a3c08c964e36d235973556b90e0ae922b894/proto/frontend/src/device_mgr.cpp#L2242-L2246 + DASH_LOG_WARN("Invalid reprsentation of 'don't care' LPM match, omit match field instead of using a prefix length of 0"); + return; + } + set_attr_value_to_p4(key.field, key.bitwidth, value, mf_lpm); + } + else if (key.match_type == "ternary") { + auto mf_ternary = mf->mutable_ternary(); + set_attr_value_to_p4(key.field, key.bitwidth, value, mf_ternary); + } + else if (key.match_type == "optional") { + auto mf_optional = mf->mutable_optional(); + set_attr_value_to_p4(key.field, key.bitwidth, value, mf_optional); + } + else if (key.match_type == "list") { + // BMv2 doesn't support "list" match type, and we are using "optional" match in v1model as our implementation. + // Hence, here we only take the first item from the list and program it as optional match. + auto mf_optional = mf->mutable_optional(); + if (key.field == "ipprefixlist") { + sai_attribute_value_t val; + val.ipaddr.addr_family = value.ipprefixlist.list[0].addr_family; + val.ipaddr.addr = value.ipprefixlist.list[0].addr; + set_attr_value_to_p4("ipaddr", key.bitwidth, val, mf_optional); + } + else { + set_attr_value_to_p4(key.field, key.bitwidth, value, mf_optional); + } + } + else if (key.match_type == "range_list") { + // BMv2 doesn't support "range_list" match type, and we are using "optional" match in v1model as our implementation. + // Hence, here we only take the first item from the list and program the range start as optional match. + auto mf_optional = mf->mutable_optional(); + // FIXME only u16rangelist in sai_attribute_value_t + u16SetVal(value.u16rangelist.list[0].min, mf_optional, key.bitwidth); + } + else { + assert(0); + } + } + + void get_attr_value_from_p4_match( + _In_ const P4MetaKey &key, + _In_ p4::v1::FieldMatch *mf, + _Out_ sai_attribute_value_t &value) + { + if (key.match_type == "exact") { + auto mf_exact = mf->mutable_exact(); + get_attr_value_from_p4(key.field, key.bitwidth, mf_exact, value); + } + else if (key.match_type == "lpm") { + auto mf_lpm = mf->mutable_lpm(); + get_attr_value_from_p4(key.field, key.bitwidth, mf_lpm, value); + } + else if (key.match_type == "ternary") { + auto mf_ternary = mf->mutable_ternary(); + get_attr_value_from_p4(key.field, key.bitwidth, mf_ternary, value); + } + else if (key.match_type == "optional") { + auto mf_optional = mf->mutable_optional(); + get_attr_value_from_p4(key.field, key.bitwidth, mf_optional, value); + } + else if (key.match_type == "list") { + auto mf_optional = mf->mutable_optional(); + get_attr_value_from_p4(key.field, key.bitwidth, mf_optional, value); + } + else if (key.match_type == "range_list") { + auto mf_optional = mf->mutable_optional(); + get_attr_value_from_p4(key.field, key.bitwidth, mf_optional, value); + } + else { + assert(0); + } + } + + void set_attr_to_p4_match( + _In_ const P4MetaKey *meta_key, + _In_ const sai_attribute_t *attr, + _Inout_ std::shared_ptr matchActionEntry) + { + if (meta_key->ip_is_v6_field_id) { + auto mf = matchActionEntry->add_match(); + mf->set_field_id(meta_key->ip_is_v6_field_id); + set_attr_ipaddr_family_to_p4(attr->value, mf->mutable_exact()); + } + + auto mf = matchActionEntry->add_match(); + mf->set_field_id(meta_key->id); + if (meta_key->match_type == "ternary" && string_has_suffix(meta_key->name, "_MASK")) { + set_attr_value_mask_to_p4_ternary(meta_key->field, meta_key->bitwidth, attr->value, + mf->mutable_ternary()); + } + else { + set_attr_value_to_p4_match(*meta_key, attr->value, mf); + } + } + + void set_attr_to_p4_action( + _In_ const P4MetaActionParam *meta_param, + _In_ const sai_attribute_t *attr, + _Out_ p4::v1::Action *action) + { + if (meta_param->ip_is_v6_field_id) { + auto param = action->add_params(); + param->set_param_id(meta_param->ip_is_v6_field_id); + set_attr_ipaddr_family_to_p4(attr->value, param); + } + + auto param = action->add_params(); + param->set_param_id(meta_param->id); + set_attr_value_to_p4(meta_param->field, meta_param->bitwidth, attr->value, param); + } + + std::pair get_match_pair_from_p4_table_entry( + _In_ const P4MetaKey *meta_key, + _In_ std::shared_ptr matchActionEntry) + { + std::pair pair_key = {nullptr, nullptr}; + + for (int i = 0; i < matchActionEntry->match_size(); i++) { + auto mf = matchActionEntry->mutable_match(i); + if (mf->field_id() == meta_key->id) { + pair_key.first = mf; + } + else if (mf->field_id() == meta_key->ip_is_v6_field_id) { + pair_key.second = mf; + } + } + + return pair_key; + } + + std::pair get_action_param_pair_from_p4_table_entry( + _In_ const P4MetaActionParam *meta_param, + _In_ std::shared_ptr matchActionEntry) + { + auto action = matchActionEntry->mutable_action()->mutable_action(); + std::pair pair_param = {nullptr, nullptr}; + + for (int i = 0; i < action->params_size(); i++) { + auto param = action->mutable_params(i); + if (param->param_id() == meta_param->id) { + pair_param.first = param; + } + else if (param->param_id() == meta_param->ip_is_v6_field_id) { + pair_param.second = param; + } + } + + return pair_param; + } + + bool string_has_suffix(const std::string &str, const std::string &suffix) + { + if (str.length() < suffix.length()) + return false; + + return !str.compare(str.length() - suffix.length(), suffix.length(), suffix); + } +} + diff --git a/dash-pipeline/SAI/src/p4meta.h b/dash-pipeline/SAI/src/p4meta.h new file mode 100644 index 000000000..219aab202 --- /dev/null +++ b/dash-pipeline/SAI/src/p4meta.h @@ -0,0 +1,228 @@ +#pragma once + +#include +#include +#include +#include + +#include "utils.h" + +using namespace dash::utils; + +namespace dash +{ + struct P4MetaKey { + sai_attr_id_t attr_id; + std::string name; + uint32_t id; + std::string match_type; + std::string field; + uint32_t bitwidth; + uint32_t ip_is_v6_field_id; + }; + + struct P4MetaActionParam { + sai_attr_id_t attr_id; + uint32_t id; + std::string field; + uint32_t bitwidth; + uint32_t ip_is_v6_field_id; + }; + + struct P4MetaAction { + uint32_t enum_id; + std::vector params; + }; + + struct P4MetaTable { + uint32_t id; + std::vector keys; + std::map actions; + std::map extra_fields; + + P4MetaTable( + uint32_t table_id, + std::initializer_list init_keys, + std::initializer_list::value_type> init_actions, + std::initializer_list::value_type> extras + ) : id(table_id), keys(init_keys), actions(init_actions), extra_fields(extras) + + {} + + const P4MetaKey* get_meta_key( + _In_ sai_attr_id_t attr_id) const; + + const P4MetaKey* get_meta_key( + _In_ const std::string &key_name) const; + + const P4MetaKey* get_meta_object_key() const; + + const P4MetaActionParam* get_meta_action_param( + _In_ uint32_t action_id, + _In_ sai_attr_id_t attr_id) const; + + pi_p4_id_t find_action_id( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) const; + }; + + + template + static inline + void set_attr_value_to_p4( + _In_ const std::string &field, + _In_ uint32_t bitwidth, + _In_ const sai_attribute_value_t &value, + _Inout_ T *p4_key_or_param) + { + if (field == "booldata") + return booldataSetVal(value, p4_key_or_param, bitwidth); + if (field == "u8") + return u8SetVal(value, p4_key_or_param, bitwidth); + if (field == "u16") + return u16SetVal(value, p4_key_or_param, bitwidth); + if (field == "u32") + return u32SetVal(value, p4_key_or_param, bitwidth); + if (field == "u64") + return u64SetVal(value, p4_key_or_param, bitwidth); + if (field == "ipaddr") + return ipaddrSetVal(value, p4_key_or_param, bitwidth); + if (field == "mac") + return macSetVal(value, p4_key_or_param, bitwidth); + if (field == "u8list") + return u8listSetVal(value, p4_key_or_param, bitwidth); + + assert(0); + } + + void set_attr_value_to_p4( + _In_ const std::string &field, + _In_ uint32_t bitwidth, + _In_ const sai_attribute_value_t &value, + _Inout_ p4::v1::FieldMatch_LPM *mf_lpm); + + template + static inline + void get_attr_value_from_p4( + _In_ const std::string &field, + _In_ uint32_t bitwidth, + _In_ const T *p4_key_or_param, + _Out_ sai_attribute_value_t &value) + { + const char *v = p4_key_or_param->value().c_str(); + + if (field == "booldata") { + value.booldata = *(const bool*)v; + } + else if (field == "u8") { + value.u8 = *(const uint8_t*)v; + } + else if (field == "u16") { + uint16_t val = *(const uint16_t*)v; + value.u16 = ntohs(val); + } + else if (field == "u32") { + uint32_t val = *(const uint32_t*)v; + value.u32 = ntohl(val) >> (32 - bitwidth); + } + else if (field == "u64") { + uint64_t val = *(const uint64_t*)v; + if (*reinterpret_cast("\0\x01") == 0) { // Little Endian + value.u64 = be64toh(val) >> (64 - bitwidth); + } + else { + value.u64 = val & ((1ul<value().size()); + } + else { + assert(0); + } + } + + void get_attr_value_from_p4( + _In_ const std::string &field, + _In_ uint32_t bitwidth, + _In_ const p4::v1::FieldMatch_LPM *mf_lpm, + _Out_ sai_attribute_value_t &value); + + // set/get only for value.ipaddr.addr_family + template + static inline + void set_attr_ipaddr_family_to_p4( + _In_ const sai_attribute_value_t &value, + _Out_ T *t) + { + booldataSetVal(value.ipaddr.addr_family == SAI_IP_ADDR_FAMILY_IPV4 ? 0 : 1, t, 1); + } + + template + static inline + void get_attr_ipaddr_family_from_p4( + _In_ const T *t, + _Out_ sai_attribute_value_t &value) + { + const char *v = t->value().c_str(); + + if (*(const bool*)v) // is_ipv6 + value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + else + value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + } + + void set_attr_value_mask_to_p4_ternary( + _In_ const std::string &field, + _In_ uint32_t bitwidth, + _In_ const sai_attribute_value_t &value, + _Inout_ p4::v1::FieldMatch_Ternary *mf_ternary); + + void get_attr_value_mask_from_p4_ternary( + _In_ const std::string &field, + _In_ uint32_t bitwidth, + _In_ const p4::v1::FieldMatch_Ternary *mf_ternary, + _Out_ sai_attribute_value_t &value); + + void set_attr_value_to_p4_match( + _In_ const P4MetaKey &key, + _In_ const sai_attribute_value_t &value, + _Inout_ p4::v1::FieldMatch *mf); + + void get_attr_value_from_p4_match( + _In_ const P4MetaKey &key, + _In_ p4::v1::FieldMatch *mf, + _Out_ sai_attribute_value_t &value); + + void set_attr_to_p4_match( + _In_ const P4MetaKey *meta_key, + _In_ const sai_attribute_t *attr, + _Inout_ std::shared_ptr matchActionEntry); + + void set_attr_to_p4_action( + _In_ const P4MetaActionParam *meta_param, + _In_ const sai_attribute_t *attr, + _Out_ p4::v1::Action *action); + + std::pair get_match_pair_from_p4_table_entry( + _In_ const P4MetaKey *meta_key, + _In_ std::shared_ptr matchActionEntry); + + std::pair get_action_param_pair_from_p4_table_entry( + _In_ const P4MetaActionParam *meta_param, + _In_ std::shared_ptr matchActionEntry); + + bool string_has_suffix(const std::string &str, const std::string &suffix); +} diff --git a/dash-pipeline/SAI/src/utils.h b/dash-pipeline/SAI/src/utils.h index e468911d8..0ffe068f1 100644 --- a/dash-pipeline/SAI/src/utils.h +++ b/dash-pipeline/SAI/src/utils.h @@ -242,12 +242,6 @@ namespace dash int getPrefixLength(const sai_attribute_value_t &value); - template - void ipPrefixSetVal(const sai_attribute_value_t &value, T &t, int bits = -1) - { - ipPrefixSetVal(value.ipprefix, t); - } - template void ipPrefixSetVal(const sai_ip_prefix_t &value, T &t, int bits = -1) { @@ -282,6 +276,12 @@ namespace dash } } + template + void ipPrefixSetVal(const sai_attribute_value_t &value, T &t, int bits = -1) + { + ipPrefixSetVal(value.ipprefix, t); + } + template void u8listSetVal(const sai_attribute_value_t &value, T &t, int bits = -1) { diff --git a/dash-pipeline/SAI/templates/impls/sai_api_func_quad.cpp.j2 b/dash-pipeline/SAI/templates/impls/sai_api_func_quad.cpp.j2 index 249d0209b..6903f5b68 100644 --- a/dash-pipeline/SAI/templates/impls/sai_api_func_quad.cpp.j2 +++ b/dash-pipeline/SAI/templates/impls/sai_api_func_quad.cpp.j2 @@ -1,3 +1,15 @@ +{% if not api.is_object %} +static sai_status_t table_{{api.name}}_add_keys( + _In_ const sai_{{ api.name }}_t *{{ api.name }}, + _Inout_ std::shared_ptr matchActionEntry) +{ + auto tableEntry = {{ api.name }}; + {% include 'templates/impls/p4_table_entry_match.cpp.j2' %} + + return SAI_STATUS_SUCCESS; +} + +{% endif %} static sai_status_t dash_sai_create_{{ api.name }}( {% if api.is_object %} _Out_ sai_object_id_t *{{ api.name }}_id, @@ -144,9 +156,15 @@ static sai_status_t dash_sai_set_{{ api.name }}_attribute( {% include 'templates/headers/sai_api_param_object_id.j2' %}, _In_ const sai_attribute_t *attr) { - DASH_LOG_ENTER(); - assert(0 && "sai_set_{{ api.name }}_attribute NYI"); - return SAI_STATUS_FAILURE; +{% if api.is_object %} + return dashSai->set({{meta_table}}, {{ api.name }}_id, attr); +{% else %} + std::shared_ptr matchActionEntry = std::make_shared(); + matchActionEntry->set_table_id({{meta_table}}.id); + + table_{{api.name}}_add_keys({{ api.name }}, matchActionEntry); + return dashSai->set({{meta_table}}, matchActionEntry, attr); +{% endif %} } static sai_status_t dash_sai_get_{{ api.name }}_attribute( @@ -154,8 +172,14 @@ static sai_status_t dash_sai_get_{{ api.name }}_attribute( _In_ uint32_t attr_count, _Inout_ sai_attribute_t *attr_list) { - DASH_LOG_ENTER(); - assert(0 && "sai_get_{{ api.name }}_attribute NYI"); - return SAI_STATUS_FAILURE; +{% if api.is_object %} + return dashSai->get({{meta_table}}, {{ api.name }}_id, attr_count, attr_list); +{% else %} + std::shared_ptr matchActionEntry = std::make_shared(); + matchActionEntry->set_table_id({{meta_table}}.id); + + table_{{api.name}}_add_keys({{ api.name }}, matchActionEntry); + return dashSai->get({{meta_table}}, matchActionEntry, attr_count, attr_list); +{% endif %} } diff --git a/dash-pipeline/SAI/templates/impls/sai_api_group.cpp.j2 b/dash-pipeline/SAI/templates/impls/sai_api_group.cpp.j2 index 42ab5ba24..8308ecc49 100644 --- a/dash-pipeline/SAI/templates/impls/sai_api_group.cpp.j2 +++ b/dash-pipeline/SAI/templates/impls/sai_api_group.cpp.j2 @@ -10,6 +10,88 @@ using namespace dash::utils; {% for api in api_group.sai_apis %} {% set table = api.p4_meta.tables[0] -%} +{% set meta_table = api.name ~ '_meta_table' -%} +static dash::P4MetaTable {{meta_table}} ( + {{table.id}}, // p4 table id + { // meta table keys + {% for key in table['keys'] %} + { + {% if api.is_object and not key.is_object_key %} + SAI_{{ api.name | upper }}_ATTR_{{ key.name | upper }}, + {% else %} + ~0u, // key is not ATTR + {% endif%} + {% if key.is_object_key %} + "{{key.name}}_OBJECT_KEY", // p4 match name + {% else %} + "{{key.name}}", // p4 match name + {% endif%} + {{key.id}}, // p4 match field id + "{{key.match_type}}", // p4 match type + "{{key.field}}", // field + {{key.bitwidth}}, // bitwidth + {{key.ip_is_v6_field_id}} // ip_is_v6_field_id + }, + {% if api.is_object and key.match_type == 'ternary'%} + { // extra added for set_mask of ternary key + SAI_{{ api.name | upper }}_ATTR_{{ key.name | upper }}_MASK, + "{{key.name}}_MASK", // p4 match name + {{key.id}}, // p4 match field id + "{{key.match_type}}", // p4 match type + "{{key.field}}", // field + {{key.bitwidth}}, // bitwidth + {{key.ip_is_v6_field_id}} // ip_is_v6_field_id + }, + {% endif%} + {% endfor %} + }, + { // meta table actions + {% for name, action in table.actions.items() %} + { + {{action.id}}, // p4 action id + { + {% if api.is_object and table.actions|length == 1 %} + ~0u, // Have no ATTR ACTION value for oid API with 1 action + {% elif api_group.api_type == "underlay" %} + ~0u, // Have no ATTR ACTION value for underlay api type + {% else %} + {{name}}, // attr action enum id + {% endif %} + { + {% for param_name, param in action.attr_params.items() %} + {% if param.skipattr != 'true' %} + { + {{param_name}}, // attr id + {{param.id}}, // p4 action param id + "{{param.field}}", // field + {{param.bitwidth}}, // bitwidth + {{param.ip_is_v6_field_id}} // ip_is_v6_field_id + }, + {% endif%} + {% endfor %} + } + + }, + }, + {% endfor %} + }, + { // extra fields for SAI object attributes + {% if api_group.api_type != "underlay" %} + {% for attr in api.attributes %} + {% if attr.name.endswith("ATTR_ACTION") %} + {"ACTION", {{attr.name}}}, + {% elif attr.name.endswith("ATTR_PRIORITY") %} + {"PRIORITY", {{attr.name}}}, + {% elif attr.name.endswith("ATTR_COUNTER_ID") %} + {"COUNTER_ID", {{attr.name}}}, + {% elif attr.name.endswith("ATTR_IP_ADDR_FAMILY") %} + {"IP_ADDR_FAMILY", {{attr.name}}}, + {% endif%} + {% endfor %} + {% endif%} + } +); + {% include 'templates/impls/sai_api_func_quad.cpp.j2' %} {% if api.stats | length > 0 %} diff --git a/dash-pipeline/tests/libsai/vnet_out/vnet_out.cpp b/dash-pipeline/tests/libsai/vnet_out/vnet_out.cpp index e79fbca66..111a8a721 100644 --- a/dash-pipeline/tests/libsai/vnet_out/vnet_out.cpp +++ b/dash-pipeline/tests/libsai/vnet_out/vnet_out.cpp @@ -2,6 +2,7 @@ #include #include #include +#include extern "C" { #include @@ -114,6 +115,15 @@ int main(int argc, char **argv) return 1; } + attr.value.u32 = 10; + status = dash_vnet_api->set_vnet_attribute(vnet_id, &attr); + assert(status == SAI_STATUS_SUCCESS); + + attr.value.u32 = 0; + status = dash_vnet_api->get_vnet_attribute(vnet_id, 1, &attr); + assert(status == SAI_STATUS_SUCCESS); + assert(attr.value.u32 == 10); + attrs.clear(); attr.id = SAI_ENI_ATTR_CPS; @@ -231,6 +241,12 @@ int main(int argc, char **argv) return 1; } + attr.id = SAI_ENI_ATTR_VM_UNDERLAY_DIP; + attr.value.ipaddr.addr = {0}; + status = dash_eni_api->get_eni_attribute(eni_id, 1, &attr); + assert(status == SAI_STATUS_SUCCESS); + assert(attr.value.ipaddr.addr.ip4 == u_dip_addr.ip4); + attrs.clear(); sai_eni_ether_address_map_entry_t eam; @@ -247,7 +263,7 @@ int main(int argc, char **argv) attrs.push_back(attr); attr.id = SAI_ENI_ETHER_ADDRESS_MAP_ENTRY_ATTR_ENI_ID; - attr.value.u16 = eni_id; + attr.value.u16 = (uint16_t)eni_id; attrs.push_back(attr); status = dash_eni_api->create_eni_ether_address_map_entry(&eam, attrs.size(), attrs.data()); @@ -257,6 +273,12 @@ int main(int argc, char **argv) return 1; } + attr.id = SAI_ENI_ETHER_ADDRESS_MAP_ENTRY_ATTR_ENI_ID; + attr.value.u16 -= 1; + status = dash_eni_api->get_eni_ether_address_map_entry_attribute(&eam, 1, &attr); + assert(status == SAI_STATUS_SUCCESS); + assert(attr.value.u16 == (uint16_t)eni_id); + // Delete everything in reverse order status = dash_eni_api->remove_eni_ether_address_map_entry(&eam); if (status != SAI_STATUS_SUCCESS)