<commit_before>/*
 * Copyright 2015 Applied Research Center for Computer Networks
 *
 * 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.
 */

// TODO: security: add cookie to LLDP messages
// TODO: test on unstable links (broken by timeout)

#include "LinkDiscovery.hh"

#include <cstdint>
#include <boost/endian/arithmetic.hpp>
#include <boost/endian/conversion.hpp>

#include "LLDP.hh"

#include "api/PacketMissHandler.hh"
#include "api/Packet.hh"
#include "api/SerializablePacket.hh"
#include "api/TraceablePacket.hh"
#include "oxm/openflow_basic.hh"
#include "SwitchConnection.hh"
#include "Flow.hh"
#include "Maple.hh"

using namespace boost::endian;
using namespace runos;

REGISTER_APPLICATION(LinkDiscovery, {"switch-manager", "controller", "maple", ""})

big_uint16_t lldp_tlv_header(big_uint16_t type, big_uint16_t length)
{
    return (type << big_uint16_t(9)) | length;
}

struct lldp_packet {
    big_uint48_t dst_mac {0x0180c200000eULL};
    big_uint48_t src_mac;
    big_uint16_t eth_type {LLDP_ETH_TYPE};

    big_uint16_t chassis_id_header {lldp_tlv_header(LLDP_CHASSIS_ID_TLV, 7)};
    big_uint8_t chassis_id_sub {LLDP_CHASSIS_ID_SUB_MAC};
    big_uint48_t chassis_id_sub_mac;

    big_uint16_t port_id_header {lldp_tlv_header(LLDP_PORT_ID_TLV, 5)};
    big_uint8_t port_id_sub {LLDP_PORT_ID_SUB_COMPONENT};
    big_uint32_t port_id_sub_component;

    big_uint16_t ttl_header {lldp_tlv_header(LLDP_TTL_TLV, 2)};
    big_uint16_t ttl_seconds;

    big_uint16_t dpid_header {lldp_tlv_header(127, 12)};
    big_uint24_t dpid_oui {0x0026e1}; // OpenFlow
    big_uint8_t  dpid_sub {0};
    big_uint64_t dpid_data;

    big_uint16_t end {0};
};
static_assert(sizeof(lldp_packet) == 50, "Unexpected alignment");

void LinkDiscovery::init(Loader *loader, const Config &rootConfig)
{
    qRegisterMetaType<switch_and_port>();

    /* Read configuration */
    auto config = config_cd(rootConfig, "link-discovery");
    c_poll_interval = config_get(config, "poll-interval", 120);

    /* Get dependencies */
    ctrl  = Controller::get(loader);
    m_switch_manager     = SwitchManager::get(loader);

    QObject::connect(m_switch_manager, &SwitchManager::switchDiscovered,
         [this](Switch* dp) {
             QObject::connect(dp, &Switch::portUp, this, &LinkDiscovery::portUp);
             QObject::connect(dp, &Switch::portDown, this, &LinkDiscovery::portDown);
             QObject::connect(dp, &Switch::portModified, this, &LinkDiscovery::portModified);
         });

    /* Do logging */
    QObject::connect(this, &LinkDiscovery::linkDiscovered,
         [](switch_and_port from, switch_and_port to) {
             LOG(INFO) << "Link discovered: "
                     << from.dpid << ':' << from.port << " -> "
                     << to.dpid << ':' << to.port;
         });
    QObject::connect(this, &LinkDiscovery::linkBroken,
         [](switch_and_port from, switch_and_port to) {
             LOG(INFO) << "Link broken: "
                     << from.dpid << ':' << from.port << " -> "
                     << to.dpid << ':' << to.port;

        });

    /* Connect with other applications */
    auto maple = Maple::get(loader);
    maple->registerHandler("link-discovery",
        [this](SwitchConnectionPtr connection) {
            const auto ofb_in_port = oxm::in_port();
            const auto ofb_eth_type = oxm::eth_type();

            return [=](Packet& pkt, FlowPtr, Decision decision) {
                if (not pkt.test(ofb_eth_type == LLDP_ETH_TYPE))
                    return decision;

                lldp_packet lldp;
                auto written = packet_cast<SerializablePacket&>(pkt)
                              //.ethernet()
                              .serialize_to(sizeof lldp, &lldp);
                if (written < sizeof lldp) {
                    LOG(ERROR) << "LLDP packet is too small";
                    return decision.idle_timeout(std::chrono::seconds::zero())
                        .drop()// TODO : inspect(sizeof(lldp_packet))
                                   .return_();
                }

                switch_and_port source
                    = { lldp.dpid_data, lldp.port_id_sub_component };
                switch_and_port target
                    = { target.dpid = connection->dpid(),
                        packet_cast<TraceablePacket>(pkt).watch(ofb_in_port) };

                DVLOG(5) << "LLDP packet received on "
                    << target.dpid << ':' << target.port;

                if (source > target)
                    std::swap(source, target);

                QMetaObject::invokeMethod(this, "handleBeacon",
                                          Qt::QueuedConnection,
                                          Q_ARG(switch_and_port, source),
                                          Q_ARG(switch_and_port, target));
                return decision.idle_timeout(std::chrono::seconds::zero())
                    .drop()// TODO : inspect(sizeof(lldp_packet))
                                   .return_();
            };
        });
}

void LinkDiscovery::startUp(Loader *)
{
    // Start LLDP polling
    startTimer(c_poll_interval * 1000);<commit_msg>change link discovery by Inspect Flow<commit_after>/*
 * Copyright 2015 Applied Research Center for Computer Networks
 *
 * 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.
 */

// TODO: security: add cookie to LLDP messages
// TODO: test on unstable links (broken by timeout)

#include "LinkDiscovery.hh"

#include <cstdint>
#include <boost/endian/arithmetic.hpp>
#include <boost/endian/conversion.hpp>

#include "LLDP.hh"

#include "api/PacketMissHandler.hh"
#include "api/Packet.hh"
#include "api/SerializablePacket.hh"
#include "api/TraceablePacket.hh"
#include "oxm/openflow_basic.hh"
#include "SwitchConnection.hh"
#include "Flow.hh"
#include "Maple.hh"

using namespace boost::endian;
using namespace runos;

REGISTER_APPLICATION(LinkDiscovery, {"switch-manager", "controller", "maple", ""})

big_uint16_t lldp_tlv_header(big_uint16_t type, big_uint16_t length)
{
    return (type << big_uint16_t(9)) | length;
}

struct lldp_packet {
    big_uint48_t dst_mac {0x0180c200000eULL};
    big_uint48_t src_mac;
    big_uint16_t eth_type {LLDP_ETH_TYPE};

    big_uint16_t chassis_id_header {lldp_tlv_header(LLDP_CHASSIS_ID_TLV, 7)};
    big_uint8_t chassis_id_sub {LLDP_CHASSIS_ID_SUB_MAC};
    big_uint48_t chassis_id_sub_mac;

    big_uint16_t port_id_header {lldp_tlv_header(LLDP_PORT_ID_TLV, 5)};
    big_uint8_t port_id_sub {LLDP_PORT_ID_SUB_COMPONENT};
    big_uint32_t port_id_sub_component;

    big_uint16_t ttl_header {lldp_tlv_header(LLDP_TTL_TLV, 2)};
    big_uint16_t ttl_seconds;

    big_uint16_t dpid_header {lldp_tlv_header(127, 12)};
    big_uint24_t dpid_oui {0x0026e1}; // OpenFlow
    big_uint8_t  dpid_sub {0};
    big_uint64_t dpid_data;

    big_uint16_t end {0};
};
static_assert(sizeof(lldp_packet) == 50, "Unexpected alignment");

void LinkDiscovery::init(Loader *loader, const Config &rootConfig)
{
    qRegisterMetaType<switch_and_port>();

    /* Read configuration */
    auto config = config_cd(rootConfig, "link-discovery");
    c_poll_interval = config_get(config, "poll-interval", 120);

    /* Get dependencies */
    ctrl  = Controller::get(loader);
    m_switch_manager     = SwitchManager::get(loader);

    QObject::connect(m_switch_manager, &SwitchManager::switchDiscovered,
         [this](Switch* dp) {
             QObject::connect(dp, &Switch::portUp, this, &LinkDiscovery::portUp);
             QObject::connect(dp, &Switch::portDown, this, &LinkDiscovery::portDown);
             QObject::connect(dp, &Switch::portModified, this, &LinkDiscovery::portModified);
         });

    /* Do logging */
    QObject::connect(this, &LinkDiscovery::linkDiscovered,
         [](switch_and_port from, switch_and_port to) {
             LOG(INFO) << "Link discovered: "
                     << from.dpid << ':' << from.port << " -> "
                     << to.dpid << ':' << to.port;
         });
    QObject::connect(this, &LinkDiscovery::linkBroken,
         [](switch_and_port from, switch_and_port to) {
             LOG(INFO) << "Link broken: "
                     << from.dpid << ':' << from.port << " -> "
                     << to.dpid << ':' << to.port;

        });

    /* Connect with other applications */
    auto maple = Maple::get(loader);
    maple->registerHandler("link-discovery",
        [this](SwitchConnectionPtr connection) {
            const auto ofb_in_port = oxm::in_port();
            const auto ofb_eth_type = oxm::eth_type();

            auto handler = [=](Packet& pkt, FlowPtr){
                lldp_packet lldp;
                auto written = packet_cast<SerializablePacket&>(pkt)
                              //.ethernet()
                              .serialize_to(sizeof lldp, &lldp);
                if (written < sizeof lldp) {
                    LOG(ERROR) << "LLDP packet is too small";
               }

                switch_and_port source
                    = { lldp.dpid_data, lldp.port_id_sub_component };
                switch_and_port target
                    = { target.dpid = connection->dpid(),
                        packet_cast<TraceablePacket>(pkt).watch(ofb_in_port) };

                DVLOG(5) << "LLDP packet received on "
                    << target.dpid << ':' << target.port;

                if (source > target)
                    std::swap(source, target);

                QMetaObject::invokeMethod(this, "handleBeacon",
                                          Qt::QueuedConnection,
                                          Q_ARG(switch_and_port, source),
                                          Q_ARG(switch_and_port, target));

                return true;
                };


            return [=](Packet& pkt, FlowPtr, Decision decision) {
                if (not pkt.test(ofb_eth_type == LLDP_ETH_TYPE))
                    return decision;

                VLOG(30) << "installing lldp rule";
                return decision
                    .inspect(sizeof(lldp_packet), handler)
                    .return_();
           };
        });
}

void LinkDiscovery::startUp(Loader *)
{
    // Start LLDP polling
    startTimer(c_poll_interval * 1000);
{'fraction_non_alphanumeric': 0.11392742796157951, 'fraction_numerical': 0.014941302027748132, 'mean_word_length': 2.453624078624079, 'pattern_counts': {'":': 0, '<': 107, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2445028', 'n_tokens_mistral': 3615, 'n_tokens_neox': 3402, 'n_words': 736}