void SwitchStateBase::process_packet_for_fdb_event()

in vslib/SwitchStateBaseFdb.cpp [387:585]


void SwitchStateBase::process_packet_for_fdb_event(
        _In_ sai_object_id_t portId,
        _In_ const std::string& name,
        _In_ const uint8_t *buffer,
        _In_ size_t size)
{
    SWSS_LOG_ENTER();

    // we would need hostif info here and maybe interface index, then we can
    // find host info from index

    uint32_t frametime = (uint32_t)time(NULL);

    /*
     * We add +2 in case if frame contains 1Q VLAN tag.
     */

    if (size < (sizeof(ethhdr) + 2))
    {
        SWSS_LOG_WARN("ethernet frame is too small: %zu", size);
        return;
    }

    const ethhdr *eh = (const ethhdr*)buffer;

    uint16_t proto = htons(eh->h_proto);

    uint16_t vlan_id = DEFAULT_VLAN_NUMBER;

    bool tagged = (proto == ETH_P_8021Q);

    if (tagged)
    {
        // this is tagged frame, get vlan id from frame

        uint16_t tci = htons(((const uint16_t*)&eh->h_proto)[1]); // tag is after h_proto field

        vlan_id = tci & 0xfff;

        if (vlan_id == 0xfff)
        {
            SWSS_LOG_WARN("invalid vlan id %u in ethernet frame on %s", vlan_id, name.c_str());
            return;
        }

        if (vlan_id == 0)
        {
            // priority packet, frame should be treated as non tagged
            tagged = false;
        }
    }

    if (tagged == false)
    {
        // untagged ethernet frame

        sai_attribute_t attr;

#ifdef SAI_LAG_ATTR_PORT_VLAN_ID

        sai_object_id_t lag_id;

        if (getLagFromPort(portid, lag_id))
        {
            // if port belongs to lag we need to get SAI_LAG_ATTR_PORT_VLAN_ID

            attr.id = SAI_LAG_ATTR_PORT_VLAN_ID

                sai_status_t status = get(SAI_OBJECT_TYPE_LAG, lag_id, 1, &attr);

            if (status != SAI_STATUS_SUCCESS)
            {
                SWSS_LOG_WARN("failed to get lag vlan id from lag %s",
                        sai_serialize_object_id(lag_id).c_str());
                return;
            }

            vlan_id = attr.value.u16;

            if (isLagOrPortRifBased(lag_id))
            {
                // this lag is router interface based, skip mac learning
                return;
            }
        }
        else
#endif
        {
            attr.id = SAI_PORT_ATTR_PORT_VLAN_ID;

            sai_status_t status = get(SAI_OBJECT_TYPE_PORT, portId, 1, &attr);

            if (status != SAI_STATUS_SUCCESS)
            {
                SWSS_LOG_WARN("failed to get port vlan id from port %s",
                        sai_serialize_object_id(portId).c_str());
                return;
            }

            // untagged port vlan (default is 1, but may change setting port attr)
            vlan_id = attr.value.u16;
        }
    }

    sai_object_id_t lag_id;
    if (getLagFromPort(portId, lag_id) && isLagOrPortRifBased(lag_id))
    {
        SWSS_LOG_DEBUG("lag %s is rif based, skip mac learning for port %s",
                sai_serialize_object_id(lag_id).c_str(),
                sai_serialize_object_id(portId).c_str());
        return;
    }

    if (isLagOrPortRifBased(portId))
    {
        SWSS_LOG_DEBUG("port %s is rif based, skip mac learning",
                sai_serialize_object_id(portId).c_str());
        return;
    }

    // we have vlan and mac address which is KEY, so just see if that is already defined

    FdbInfo fi;

    fi.setPortId((lag_id != SAI_NULL_OBJECT_ID) ? lag_id : portId);

    fi.setVlanId(vlan_id);

    memcpy(fi.m_fdbEntry.mac_address, eh->h_source, sizeof(sai_mac_t));

    std::set<FdbInfo>::iterator it = m_fdb_info_set.find(fi);

    if (it != m_fdb_info_set.end())
    {
        // this key was found, update timestamp
        // and since iterator is const we need to reinsert

        fi = *it;

        fi.setTimestamp(frametime);

        m_fdb_info_set.insert(fi);

        return;
    }

    // key was not found, get additional information

    fi.setTimestamp(frametime);

    fi.m_fdbEntry.switch_id = m_switch_id;

    findBridgeVlanForPortVlan(portId, vlan_id, fi.m_fdbEntry.bv_id, fi.m_bridgePortId);

    if (fi.getFdbEntry().bv_id == SAI_NULL_OBJECT_ID)
    {
        SWSS_LOG_WARN("skipping mac learn for %s, since BV_ID was not found for mac",
                sai_serialize_fdb_entry(fi.getFdbEntry()).c_str());

        // bridge was not found, skip mac learning
        return;
    }

    sai_attribute_t attr;

    attr.id = SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE;

    sai_status_t status = get(SAI_OBJECT_TYPE_BRIDGE_PORT, fi.getBridgePortId(), 1, &attr);

    if (status == SAI_STATUS_SUCCESS)
    {
        if (attr.value.s32 == SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW)
        {
            SWSS_LOG_INFO("inserting to fdb_info set: %s, vlan id: %d",
                    sai_serialize_fdb_entry(fi.getFdbEntry()).c_str(),
                    fi.getVlanId());

            m_fdb_info_set.insert(fi);

            processFdbInfo(fi, SAI_FDB_EVENT_LEARNED);
        }
        else if (attr.value.s32 == SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE)
        {
            // do not learn, actually linux kernel will learn that MAC
        }
        else
        {
            SWSS_LOG_WARN("not supported SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE: %d, for %s",
                    attr.value.s32,
                    sai_serialize_fdb_entry(fi.getFdbEntry()).c_str());
        }
    }
    else
    {
        SWSS_LOG_ERROR("failed to get SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE for %s: %s",
                sai_serialize_object_id(fi.getBridgePortId()).c_str(),
                sai_serialize_status(status).c_str());
    }
}