diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a77bd6e..90407dd6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,92 +2,38 @@ name: Release on: push: - tags: - - 'v[0-9]+.[0-9]+' + branch: + - 'release/*' jobs: build-debian-package-jammy: - name: build (Ubuntu 22.04) - runs-on: builder - container: - image: yanetplatform/builder-lite + name: build-image + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + id-token: write steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v4 with: submodules: recursive - - run: | - export YANET_VERSION=${{github.ref_name}} - export YANET_VERSION=${YANET_VERSION#v} - export YANET_VERSION_MAJOR=${YANET_VERSION%.*} - export YANET_VERSION_MINOR=${YANET_VERSION#*.} - export YANET_VERSION_REVISION=${{github.run_number}} - export YANET_VERSION_HASH=${{github.sha}} - export YANET_VERSION_CUSTOM=stable - meson setup --prefix=/target \ - -Dtarget=release \ - -Darch=corei7,broadwell,knl \ - -Dversion_major=$YANET_VERSION_MAJOR \ - -Dversion_minor=$YANET_VERSION_MINOR \ - -Dversion_revision=$YANET_VERSION_REVISION \ - -Dversion_hash=$YANET_VERSION_HASH \ - -Dversion_custom=$YANET_VERSION_CUSTOM \ - build - meson compile -C build - - run: meson install -C build - - run: | - export YANET_VERSION=${{github.ref_name}} - export YANET_VERSION=${YANET_VERSION#v} - cp -r debian /target/DEBIAN - sed -i "s/__VERSION__/${YANET_VERSION}/g" /target/DEBIAN/control - - run: | - export YANET_VERSION=${{github.ref_name}} - export YANET_VERSION=${YANET_VERSION#v} - mkdir /export - dpkg-deb -b "/target" /export/yanet_${YANET_VERSION}_ubuntu22.04.deb - - uses: actions/upload-artifact@v3 + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 with: - name: target_debian - path: /export/yanet*.deb - - build-debian-package-bionic: - name: build (Ubuntu 18.04) - runs-on: builder - container: - image: yanetplatform/builder_ubuntu18.04-lite - steps: - - uses: actions/checkout@v1 + images: yanetplatform/yanet + - name: Log in to Docker Hub + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a with: - submodules: recursive - - run: | - export YANET_VERSION=${{github.ref_name}} - export YANET_VERSION=${YANET_VERSION#v} - export YANET_VERSION_MAJOR=${YANET_VERSION%.*} - export YANET_VERSION_MINOR=${YANET_VERSION#*.} - export YANET_VERSION_REVISION=${{github.run_number}} - export YANET_VERSION_HASH=${{github.sha}} - export YANET_VERSION_CUSTOM=stable - meson setup --prefix=/target \ - -Dtarget=release \ - -Darch=corei7,broadwell,knl \ - -Dversion_major=$YANET_VERSION_MAJOR \ - -Dversion_minor=$YANET_VERSION_MINOR \ - -Dversion_revision=$YANET_VERSION_REVISION \ - -Dversion_hash=$YANET_VERSION_HASH \ - -Dversion_custom=$YANET_VERSION_CUSTOM \ - build - meson compile -C build - - run: meson install -C build - - run: | - export YANET_VERSION=${{github.ref_name}} - export YANET_VERSION=${YANET_VERSION#v} - cp -r debian /target/DEBIAN - sed -i "s/__VERSION__/${YANET_VERSION}/g" /target/DEBIAN/control - - run: | - export YANET_VERSION=${{github.ref_name}} - export YANET_VERSION=${YANET_VERSION#v} - mkdir /export - dpkg-deb -b "/target" /export/yanet_${YANET_VERSION}_ubuntu18.04.deb - - uses: actions/upload-artifact@v3 + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Build and push Docker image + id: push + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 with: - name: target_debian - path: /export/yanet*.deb + context: . + file: ./build/Dockerfile.image + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/autotest/autotest.cpp b/autotest/autotest.cpp index 1930d955..9268892b 100644 --- a/autotest/autotest.cpp +++ b/autotest/autotest.cpp @@ -1487,6 +1487,16 @@ void tAutotest::mainThread() result = step_dumpPackets(yamlStep["dumpPackets"], configFilePath); } + else if (yamlStep["bird"]) + { + YANET_LOG_DEBUG("step: bird\n"); + + result = step_bird(yamlStep["bird"], configFilePath); + } + else if (yamlStep["birdc"]) + { + YANET_LOG_DEBUG("step: birdc\n"); + } else { YANET_LOG_ERROR("unknown step\n"); @@ -2006,6 +2016,42 @@ bool tAutotest::step_dumpPackets(const YAML::Node& yamlStep, return true; } +bool tAutotest::step_bird(const YAML::Node& yamlStep, + const std::string& path) +{ + std::string confPath; + for (const auto& yamlIter : yamlStep) + { + confPath = yamlIter.as(); + } + + if (!confPath.empty()) + { + const std::string execCommand = "bird -c " + path + confPath; + + std::string resExec = exec(execCommand.c_str()); + + printf("%s", resExec.data()); + + return true; + } + return false; +} + +bool tAutotest::step_birdc(const YAML::Node& yamlStep) +{ + std::string command = "birdc "; + for (const auto& yamlIter : yamlStep) + { + command += yamlIter.as() + " "; + } + + auto res_birdc = exec(command.c_str()); + + printf("birdc:\n %s\n", res_birdc.data()); + return true; +} + void tAutotest::fflushSharedMemory() { size_t size = std::get<0>(rawShmInfo); diff --git a/autotest/autotest.h b/autotest/autotest.h index edd2c840..f25fe715 100644 --- a/autotest/autotest.h +++ b/autotest/autotest.h @@ -69,6 +69,8 @@ class tAutotest bool step_reload_async(const YAML::Node& yamlStep); bool step_echo(const YAML::Node& yamlStep); bool step_dumpPackets(const YAML::Node& yamlStep, const std::string& path); + bool step_bird(const YAML::Node& yamlStep, const std::string& path); + bool step_birdc(const YAML::Node& yamlStep); eResult initSockets(); eResult initSharedMemory(); diff --git a/autotest/units/001_one_port/077_bird/autotest.yaml b/autotest/units/001_one_port/077_bird/autotest.yaml new file mode 100644 index 00000000..4caa1fa0 --- /dev/null +++ b/autotest/units/001_one_port/077_bird/autotest.yaml @@ -0,0 +1,8 @@ +steps: +- bird: + - bird/bird.conf +- birdc: + - show route +- sleep: 20 +- cli: + - rib prefixes diff --git a/autotest/units/001_one_port/077_bird/bird/bird.conf b/autotest/units/001_one_port/077_bird/bird/bird.conf new file mode 100644 index 00000000..e152e7b2 --- /dev/null +++ b/autotest/units/001_one_port/077_bird/bird/bird.conf @@ -0,0 +1,61 @@ +/* + * This is a simple example configuration file with no aim for completeness. + * See documentation for full description. + */ + +# Router ID in IPv4 format +router id 62.168.0.1; + +# Load device information from kernel. +protocol device {} + +# Generate direct routes for interfaces. Useful on BSD. +protocol direct { + ipv4; + disabled; +} + +protocol direct { + ipv6; + disabled; +} + +# Feed routes to kernel FIB +protocol kernel { + ipv4 { export all; import all; }; + learn; # Learn all routes from the kernel +# scan time 10; # Scan kernel tables every 10 seconds +} + +protocol kernel { + ipv6 { import all; }; + learn; +} + +# Static route feed +protocol static { + ipv4 { export all; }; + route 10.0.0.0/24 via 192.168.1.32; + route 10.10.0.0/16 blackhole; + route 10.20.0.0/20 via 192.168.1.22; + route 10.30.50.0/28 prohibit; +} + +protocol static { + ipv6 { export all; }; + route 2001:db8:1::/48 via 5555::6666; + route 2001:db8:2::/48 blackhole; + route 2001:db8:3::/48 prohibit; + route 2001:db8:4::/48 unreachable; +} + +protocol export { + table master4; + socket "/var/tmp/bird-master4.sock"; +} + +protocol export { + table master6; + socket "/var/tmp/bird-master6.sock"; +} + diff --git a/autotest/units/001_one_port/077_bird/controlplane.conf b/autotest/units/001_one_port/077_bird/controlplane.conf new file mode 100644 index 00000000..6f37692d --- /dev/null +++ b/autotest/units/001_one_port/077_bird/controlplane.conf @@ -0,0 +1,42 @@ +{ + "modules": { + "lp0.100": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "100", + "macAddress": "00:11:22:33:44:55", + "nextModule": "vrf0" + }, + "lp0.200": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "200", + "macAddress": "00:11:22:33:44:55", + "nextModule": "vrf0" + }, + "vrf0": { + "type": "route", + "interfaces": { + "kni0.100": { + "nextModule": "lp0.100" + }, + "kni0.200": { + "ipv4Prefix": "200.0.0.2/24", + "neighborIPv4Address": "200.0.0.1", + "neighborMacAddress": "00:00:00:11:11:11", + "nextModule": "lp0.200" + } + }, + "birdImport": [ + { + "socket": "/var/tmp/bird-master4.sock", + "vrf": "master4" + }, + { + "socket": "/var/tmp/bird-master6.sock", + "vrf": "master6" + } + ] + } + } +} diff --git a/build/Dockerfile.image b/build/Dockerfile.image index 72e3c3aa..2380b47a 100644 --- a/build/Dockerfile.image +++ b/build/Dockerfile.image @@ -1,5 +1,5 @@ ARG RELEASE=22.04 -FROM --platform=linux/amd64 ubuntu:${RELEASE} as environment +FROM --platform=linux/amd64 ubuntu:${RELEASE} AS environment RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ @@ -28,7 +28,8 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ RUN python3 -m pip install meson==0.61.2 -FROM environment as builder + +FROM environment AS builder ARG YANET_VERSION_MAJOR=0 ARG YANET_VERSION_MINOR=0 @@ -50,6 +51,7 @@ RUN meson setup --prefix=/target \ RUN meson compile -C build + FROM --platform=linux/amd64 ubuntu:${RELEASE} RUN apt-get update @@ -61,8 +63,10 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ libmlx5-1 \ libnuma1 \ libpcap0.8 \ - netbase + netbase \ + python3 COPY --from=builder /opt/yanet/build/controlplane/yanet-controlplane /usr/bin/ COPY --from=builder /opt/yanet/build/dataplane/yanet-dataplane /usr/bin/ COPY --from=builder /opt/yanet/build/cli/yanet-cli /usr/bin/ +COPY --from=builder /opt/yanet/yanet-announcer.py /usr/bin/yanet-announcer diff --git a/cli/telegraf.h b/cli/telegraf.h index ae0e4ee9..401d45ae 100644 --- a/cli/telegraf.h +++ b/cli/telegraf.h @@ -106,10 +106,13 @@ void unsafe() const auto& [memory_groups, memory_objects] = memory_stats; const auto durations = controlplane.controlplane_durations(); + uint64_t total_acl_ingress_dropPackets = 0, total_acl_egress_dropPackets = 0; for (const auto& [coreId, worker] : responseWorkers) { const auto& [iterations, stats, ports_stats] = worker; + total_acl_ingress_dropPackets += stats.acl_ingress_dropPackets; + total_acl_egress_dropPackets += stats.acl_egress_dropPackets; printf("worker,coreId=%u " "iterations=%luu," @@ -174,6 +177,12 @@ void unsafe() stats.logs_drops, stats.logs_packets); + printf("worker,coreId=all " + "acl_ingress_dropPackets=%luu," + "acl_egress_dropPackets=%luu\n", + total_acl_ingress_dropPackets, + total_acl_egress_dropPackets); + for (const auto& [physicalPortName, stats] : ports_stats) { printf("worker,coreId=%u,physicalPortName=%s " diff --git a/common/config.release.h b/common/config.release.h index e77beb6c..73b7f667 100644 --- a/common/config.release.h +++ b/common/config.release.h @@ -1,3 +1,5 @@ +#pragma once + #define CONFIG_YADECAP_MBUFS_COUNT (48 * 1024) #define CONFIG_YADECAP_MBUF_SIZE (10 * 1024) #define CONFIG_YADECAP_WORKER_PORTS_SIZE (8) diff --git a/common/icp.h b/common/icp.h index 3b2b7810..14febcf7 100644 --- a/common/icp.h +++ b/common/icp.h @@ -664,7 +664,9 @@ using eor = std::tuple; ///< table_name -using request = std::vector>; +using action = std::variant; + +using request = std::vector; } namespace rib_summary diff --git a/controlplane/libbird.cpp b/controlplane/libbird.cpp new file mode 100644 index 00000000..79939bb0 --- /dev/null +++ b/controlplane/libbird.cpp @@ -0,0 +1,609 @@ +#include "libbird.h" + +#include +#include "common/type.h" +#include "common/icp.h" +#include "controlplane/rib.h" + +static inline bool +decode_u8(uintptr_t *ppos, uintptr_t end, uint8_t *pvalue) +{ + if (*ppos + sizeof(uint8_t) > end) + return false; + *pvalue = *(uint8_t *)*ppos; + *ppos += sizeof(uint8_t); + return true; +} + +static inline bool +decode_u16(uintptr_t *ppos, uintptr_t end, uint16_t *pvalue) +{ + if (*ppos + sizeof(uint16_t) > end) + return false; + *pvalue = *(uint16_t *)*ppos; + *ppos += sizeof(uint16_t); + return true; +} + +static inline bool +decode_u32(uintptr_t *ppos, uintptr_t end, uint32_t *pvalue) +{ + if (*ppos + sizeof(uint32_t) > end) + return false; + *pvalue = *(uint32_t *)*ppos; + *ppos += sizeof(uint32_t); + return true; +} + +static inline bool +decode_chunk(uintptr_t *ppos, uintptr_t end, uintptr_t *pchunk_end) +{ + uint32_t chunk_size; + if (!decode_u32(ppos, end, &chunk_size)) + return false; + *pchunk_end = *ppos + chunk_size; + return *pchunk_end <= end; +} + +static inline bool +decode_u32_array(uintptr_t *ppos, uintptr_t end, uintptr_t *parray_end) +{ + uint32_t array_count; + if (!decode_u32(ppos, end, &array_count)) + return false; + *parray_end = *ppos + sizeof(uint32_t) * array_count; + return (*parray_end <= end); +} + +static inline bool +decode_ip_addr(uintptr_t *ppos, uintptr_t end, ip_addr *paddr) +{ + if (*ppos + sizeof(ip_addr) > end) + return false; + *paddr = *(ip_addr *)*ppos; + *ppos += sizeof(ip_addr); + return true; +} + +static inline bool +decode_net_addr(uintptr_t *ppos, uintptr_t end, net_addr_union *paddr) +{ + if (*ppos + sizeof(net_addr_union) > end) + return false; + *paddr = *(net_addr_union *)*ppos; + // Some members of net_addr_union are variable length structures so + // check the length attribute. + if (*ppos + paddr->n.length > end) + return false; + if (paddr->n.length <= sizeof(net_addr_union)) { + *ppos += sizeof(net_addr_union); + return true; + } + return false; + /* + * FIXME: this is not supported yet + memcpy( + paddr + 1, (void *)(*ppos + sizeof(net_addr_union)), + paddr->n.length - sizeof(net_addr_union) + ); + *ppos += paddr->n.length; + return true; + */ +} + +static inline bool +decode_u16_array(uintptr_t *ppos, uintptr_t end, std::vector *values) +{ + size_t attr_end; + if (!decode_chunk(ppos, end, &attr_end)) { + return false; + } + + while (*ppos < attr_end) { + uint16_t value; + if (!decode_u16(ppos, attr_end, &value)) { + return false; + } + + values->emplace_back(value); + } + + return true; +} + +static inline bool +decode_u32_array(uintptr_t *ppos, uintptr_t end, std::vector *values) +{ + size_t attr_end; + if (!decode_chunk(ppos, end, &attr_end)) { + return false; + } + + while (*ppos < attr_end) { + uint32_t value; + if (!decode_u32(ppos, attr_end, &value)) { + return false; + } + + values->emplace_back(value); + } + + return true; +} + +static inline bool +decode_as_path(uintptr_t *ppos, uintptr_t end, std::vector *as_path) +{ + size_t attr_end; + if (!decode_chunk(ppos, end, &attr_end)) { + return false; + } + + while (*ppos < attr_end) { + uint8_t segment_type; + if (!decode_u8(ppos, attr_end, &segment_type)) { + return false; + } + + if (segment_type != 2 && segment_type != 3) { + return false; + } + + uint8_t segment_len; + if (!decode_u8(ppos, attr_end, &segment_len)) { + return false; + } + + for (uint16_t idx = 0; idx < segment_len; ++idx) { + uint32_t as; + if (!decode_u32(ppos, attr_end, &as)) { + return false; + } + as = ntohl(as); + as_path->emplace_back(as); + } + } + + return true; +} + +static inline bool +ipa_is_ip4(ip_addr addr) +{ + return addr.addr[0] == 0 && addr.addr[1] == 0 && addr.addr[2] == 0xffff; +} + +static inline common::ipv4_address_t +ipa_to_address_4(ip_addr addr) +{ + return common::ipv4_address_t(addr.addr[3]); +} + +static inline common::ipv6_address_t +ipa_to_address_6(ip_addr addr) +{ + addr.addr[0] = ntohl(addr.addr[0]); + addr.addr[1] = ntohl(addr.addr[1]); + addr.addr[2] = ntohl(addr.addr[2]); + addr.addr[3] = ntohl(addr.addr[3]); + return common::ipv6_address_t((uint8_t *)&addr); +} + +static inline common::ip_address_t +ipa_to_address(ip_addr addr) +{ + if (ipa_is_ip4(addr)) + return common::ip_address_t(ipa_to_address_4(addr)); + return common::ip_address_t(ipa_to_address_6(addr)); +} + +static inline bool +recover_prefix_info(net_addr_union *paddr, common::ip_prefix_t *prefix, + common::ip_address_t *vpnDST, uint32_t *vpnRD) +{ + + switch (paddr->n.type) { + case NET_IP4: { + *prefix = common::ip_prefix_t(common::ipv4_address_t(paddr->ip4.prefix), paddr->ip4.pxlen); + break; + } + + case NET_IP6: { + *prefix = common::ip_prefix_t(ipa_to_address_6(paddr->ip6.prefix), paddr->ip6.pxlen); + break; + } + + case NET_VPN4: { + if ((paddr->vpn4.rd >> 48) != 1) { + return false; + } + + *prefix = common::ip_prefix_t(common::ipv4_address_t(paddr->vpn4.prefix), paddr->vpn4.pxlen); + *vpnDST = common::ip_address_t(common::ipv4_address_t(paddr->vpn4.rd >> 16)); + *vpnRD = paddr->vpn4.rd & 0xffff; + break; + } + + case NET_VPN6: { + if ((paddr->vpn6.rd >> 48) != 1) { + return false; + } + + *prefix = common::ip_prefix_t(ipa_to_address_6(paddr->vpn6.prefix), paddr->vpn6.pxlen); + *vpnDST = common::ip_address_t(common::ipv4_address_t(paddr->vpn4.rd >> 16)); + *vpnRD = paddr->vpn6.rd & 0xffff; + + break; + } + + default: + return false; + } + + return true; +} + +static inline bool +recover_next_hop(uintptr_t *ppos, uintptr_t end, common::ip_address_t *next_hop) +{ + if (end - *ppos != sizeof(ip_addr) && + end - *ppos != 2 * sizeof(ip_addr)) { + return false; + } + + if (end - *ppos == sizeof(ip_addr)) { + ip_addr nh_addr; + if (!decode_ip_addr(ppos, end, &nh_addr)) { + return false; + } + *next_hop = ipa_to_address(nh_addr); + return true; + } + + ip_addr nh_addr1; + ip_addr nh_addr2; + if (!decode_ip_addr(ppos, end, &nh_addr1) || + !decode_ip_addr(ppos, end, &nh_addr2)) { + return false; + } + + if (nh_addr2.addr[0] == 0 && nh_addr2.addr[1] == 0 && + nh_addr2.addr[2] == 0 && nh_addr2.addr[3] == 0) { + *next_hop = ipa_to_address(nh_addr1); + } else { + *next_hop = ipa_to_address(nh_addr2); + } + + return true; +} + +static bool +parse_route_update(uintptr_t *ppos, uintptr_t end, const char *vrf, + common::icp::rib_update::action *paction) +{ + common::ip_address_t peer_address; + std::string attribute_origin; + std::string path_information; + std::vector attribute_aspath; + std::set attribute_communities; + std::set attribute_large_communities; + std::vector labels; + + uint32_t attribute_local_preference = 0; + uint32_t attribute_med = 0; + + common::ip_address_t next_hop; + common::ip_prefix_t prefix; + common::ip_address_t vpnDST; + uint32_t vpnRD = 0; + + /* Decode route prefix. */ + net_addr_union addr; + if (!decode_net_addr(ppos, end, &addr)) + return false; + + if (!recover_prefix_info(&addr, &prefix, &vpnDST, &vpnRD)) + return false; + + uint32_t type; + if (!decode_u32(ppos, end, &type)) + return false; + + ip_addr remote_addr; + if (!decode_ip_addr(ppos, end, &remote_addr)) + return false; + peer_address = ipa_to_address(remote_addr); + + size_t attrs_end; + if (!decode_chunk(ppos, end, &attrs_end)) + return false; + + if (attrs_end != end) + return false; + + /* Now decode all attributes one by one. */ + while (*ppos < attrs_end) { + uint32_t attr_id; + if (!decode_u32(ppos, attrs_end, &attr_id)) + return false; + + + switch (EA_ID(attr_id)) { + case BA_ORIGIN: { + uint32_t origin; + if (!decode_u32(ppos, attrs_end, &origin)) { + return false; + } + switch (origin) { + case 0: attribute_origin = "IGP"; break; + case 1: attribute_origin = "EGP"; break; + case 2: attribute_origin = "Incomplete"; break; + default: attribute_origin = "?"; + } + + break; + } + + case BA_MULTI_EXIT_DISC: { + uint32_t med; + if (!decode_u32(ppos, attrs_end, &med)) { + return false; + } + + attribute_med = med; + + break; + } + + case BA_LOCAL_PREF: { + uint32_t pref; + if (!decode_u32(ppos, attrs_end, &pref)) { + return false; + } + + attribute_local_preference = pref; + + break; + } + + case BA_ORIGINATOR_ID: { + uint32_t originator; + if (!decode_u32(ppos, attrs_end, &originator)) { + return false; + } + + break; + } + + case BA_AS_PATH: { + if (!decode_as_path(ppos, attrs_end, &attribute_aspath)) { + return false; + } + + break; + } + + case BA_NEXT_HOP: { + size_t attr_end; + if (!decode_chunk(ppos, attrs_end, &attr_end)) { + return false; + } + + if (!recover_next_hop(ppos, attr_end, &next_hop)) { + return false; + } + + break; + } + + case BA_COMMUNITY: { + std::vector communities; + if (!decode_u16_array(ppos, attrs_end, &communities)) { + return false; + } + + if (communities.size() % 2 != 0) { + return false; + } + + for (size_t idx = 0; idx < communities.size() / 2; ++idx) { + attribute_communities.emplace( + communities[idx * 2 + 0], + communities[idx * 2 + 1] + ); + } + + break; + } + + case BA_CLUSTER_LIST: { + std::vector clusters; + if (!decode_u32_array(ppos, attrs_end, &clusters)) { + return false; + } + + break; + } + + case BA_EXT_COMMUNITY: { + std::vector ext_communities; + if (!decode_u32_array(ppos, attrs_end, &ext_communities)) { + return false; + } + + break; + } + + case BA_LARGE_COMMUNITY: { + std::vector large_communities; + if (!decode_u32_array(ppos, attrs_end, &large_communities)) { + return false; + } + + if (large_communities.size() % 3 != 0) { + return false; + } + + for (size_t idx = 0; idx < large_communities.size() / 3; + ++idx) { + + attribute_large_communities.emplace( + large_communities[idx * 3 + 0], + large_communities[idx * 3 + 1], + large_communities[idx * 3 + 2] + ); + } + + break; + } + + case BA_MPLS_LABEL_STACK: { + if (!decode_u32_array(ppos, attrs_end, &labels)) { + return false; + } + + break; + } + + default: + return false; + + } + } + + std::string afi; + std::string safi; + + //FIXME: table name + if (prefix.is_ipv4()) { + afi = "ipv4"; + } else { + afi = "ipv6"; + } + + if (vpnRD == 0) { + safi = "unicast"; + } else { + safi = "mpls-vpn"; + } + + if (type == 1) { + common::icp::rib_update::insert insert = {"bgp", + std::string(vrf), + YANET_RIB_PRIORITY_DEFAULT, + {}}; + + auto& announce = std::get<3>(insert)[{peer_address, + attribute_origin, + attribute_med, + attribute_aspath, + attribute_communities, + attribute_large_communities, + attribute_local_preference}]; + + auto& announce_table = announce[afi + " " + safi]; + announce_table[next_hop].emplace_back(prefix, std::to_string(vpnRD), labels); + + *paction = insert; + } else { + common::icp::rib_update::remove remove = {"bgp", + std::string(vrf), + YANET_RIB_PRIORITY_DEFAULT, + {}}; + + auto& announce = std::get<3>(remove)[peer_address]; + + //FIXME: table name + auto& announce_table = announce[afi + " " + safi]; + announce_table.emplace_back(prefix, std::to_string(vpnRD), labels); + + *paction = remove; + } + + return true; +} + +void +read_bird_feed(const char *sock_name, const char *vrf, class rib_t *rib) +{ + /* Connect to bird export socket. */ + int bird_sock = socket(AF_UNIX, SOCK_STREAM, 0); + struct sockaddr_un server_addr; + server_addr.sun_family = AF_UNIX; + strncpy(server_addr.sun_path, sock_name, sizeof(server_addr.sun_path) - 1); + if (connect(bird_sock, + (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) { + return; + } + + /* + * Read buffer is used to accumulate multuple route updates and + * flush then at once. 1Mib looks good as the buffur size. + */ + const size_t buf_size = 1 << 20; + void *read_buf = malloc(buf_size); + if (read_buf == NULL) { + close(bird_sock); + return; + } + /* + * The variable is the offset to read data to and denotes the first + * byte which could nit be parsed yet. + */ + size_t read_pos = 0; + /* + * Parse position is the position of the next one route update to + * process. + */ + size_t parse_pos = 0; + + common::icp::rib_update::request requests; + + for (;;) + { + /* Read as mush as possible data */ + ssize_t readen = read(bird_sock, + (void *)((uintptr_t)read_buf + read_pos), + buf_size - read_pos); + if (readen <= 0) { + break; + } + + /* Adust read postion and try to recover next route update. */ + read_pos += readen; + while (parse_pos < read_pos) { + /* pos and end denote addresses of a memory chunk to parse. */ + uintptr_t pos = (uintptr_t)read_buf + parse_pos; + uintptr_t end = (uintptr_t)read_buf + read_pos; + + /* Determine boundaries of the next route update. */ + uintptr_t route_end; + if (!decode_chunk(&pos, end, &route_end)) + break; + + common::icp::rib_update::action action; + if (!parse_route_update(&pos, route_end, vrf, &action)) + break; + + requests.emplace_back(action); + + parse_pos = pos - (uintptr_t)read_buf; + } + + rib->rib_update(requests); + rib->rib_flush(); + + if (buf_size - read_pos < buf_size / 2) { + memmove(read_buf, + (void *)((uintptr_t)read_buf + parse_pos), + read_pos - parse_pos); + read_pos = read_pos - parse_pos; + parse_pos = 0; + } + } + + free(read_buf); + close(bird_sock); + + return; +} diff --git a/controlplane/libbird.h b/controlplane/libbird.h new file mode 100644 index 00000000..92646132 --- /dev/null +++ b/controlplane/libbird.h @@ -0,0 +1,175 @@ +#ifndef LIBBIRD_H +#define LIBBIRD_H + +#include + +#define NET_IP4 1 +#define NET_IP6 2 +#define NET_VPN4 3 +#define NET_VPN6 4 +#define NET_ROA4 5 +#define NET_ROA6 6 +#define NET_FLOW4 7 +#define NET_FLOW6 8 +#define NET_IP6_SADR 9 +#define NET_MPLS 10 +#define NET_MAX 11 + +#define NB_IP4 (1 << NET_IP4) +#define NB_IP6 (1 << NET_IP6) +#define NB_VPN4 (1 << NET_VPN4) +#define NB_VPN6 (1 << NET_VPN6) +#define NB_ROA4 (1 << NET_ROA4) +#define NB_ROA6 (1 << NET_ROA6) +#define NB_FLOW4 (1 << NET_FLOW4) +#define NB_FLOW6 (1 << NET_FLOW6) +#define NB_IP6_SADR (1 << NET_IP6_SADR) +#define NB_MPLS (1 << NET_MPLS) + +#define NB_IP (NB_IP4 | NB_IP6) +#define NB_VPN (NB_VPN4 | NB_VPN6) +#define NB_ROA (NB_ROA4 | NB_ROA6) +#define NB_FLOW (NB_FLOW4 | NB_FLOW6) +#define NB_DEST (NB_IP | NB_IP6_SADR | NB_VPN | NB_MPLS) +#define NB_ANY 0xffffffff + +#define BA_ORIGIN 0x01 /* RFC 4271 */ /* WM */ +#define BA_AS_PATH 0x02 /* WM */ +#define BA_NEXT_HOP 0x03 /* WM */ +#define BA_MULTI_EXIT_DISC 0x04 /* ON */ +#define BA_LOCAL_PREF 0x05 /* WD */ +#define BA_ATOMIC_AGGR 0x06 /* WD */ +#define BA_AGGREGATOR 0x07 /* OT */ +#define BA_COMMUNITY 0x08 /* RFC 1997 */ /* OT */ +#define BA_ORIGINATOR_ID 0x09 /* RFC 4456 */ /* ON */ +#define BA_CLUSTER_LIST 0x0a /* RFC 4456 */ /* ON */ +#define BA_MP_REACH_NLRI 0x0e /* RFC 4760 */ +#define BA_MP_UNREACH_NLRI 0x0f /* RFC 4760 */ +#define BA_EXT_COMMUNITY 0x10 /* RFC 4360 */ +#define BA_AS4_PATH 0x11 /* RFC 6793 */ +#define BA_AS4_AGGREGATOR 0x12 /* RFC 6793 */ +#define BA_AIGP 0x1a /* RFC 7311 */ +#define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */ +#define BA_ONLY_TO_CUSTOMER 0x23 /* RFC 9234 */ + +/* Bird's private internal BGP attributes */ +#define BA_MPLS_LABEL_STACK 0xfe /* MPLS label stack transfer attribute */ + +#define EA_ID(ea) ((ea) & 0xff) +#define EA_PROTO(ea) ((ea) >> 8) + +typedef uint32_t ip4_addr; + +typedef struct ip6_addr { + uint32_t addr[4]; +} ip6_addr; + +typedef ip6_addr ip_addr; + +typedef struct net_addr { + uint8_t type; + uint8_t pxlen; + uint16_t length; + uint8_t data[20]; + uint64_t align[0]; +} net_addr; + +typedef struct net_addr_ip4 { + uint8_t type; + uint8_t pxlen; + uint16_t length; + ip4_addr prefix; +} net_addr_ip4; + +typedef struct net_addr_ip6 { + uint8_t type; + uint8_t pxlen; + uint16_t length; + ip6_addr prefix; +} net_addr_ip6; + +typedef struct net_addr_vpn4 { + uint8_t type; + uint8_t pxlen; + uint16_t length; + ip4_addr prefix; + uint64_t rd; +} net_addr_vpn4; + +typedef struct net_addr_vpn6 { + uint8_t type; + uint8_t pxlen; + uint16_t length; + ip6_addr prefix; + uint32_t padding; + uint64_t rd; +} net_addr_vpn6; + +typedef struct net_addr_roa4 { + uint8_t type; + uint8_t pxlen; + uint16_t length; + ip4_addr prefix; + uint32_t max_pxlen; + uint32_t asn; +} net_addr_roa4; + +typedef struct net_addr_roa6 { + uint8_t type; + uint8_t pxlen; + uint16_t length; + ip6_addr prefix; + uint32_t max_pxlen; + uint32_t asn; +} net_addr_roa6; + +typedef struct net_addr_flow4 { + uint8_t type; + uint8_t pxlen; + uint16_t length; + ip4_addr prefix; + uint8_t data[0]; +} net_addr_flow4; + +typedef struct net_addr_flow6 { + uint8_t type; + uint8_t pxlen; + uint16_t length; + ip6_addr prefix; + uint8_t data[0]; +} net_addr_flow6; + +typedef struct net_addr_mpls { + uint8_t type; + uint8_t pxlen; + uint16_t length; + uint32_t label; +} net_addr_mpls; + +typedef struct net_addr_ip6_sadr { + uint8_t type; + uint8_t dst_pxlen; + uint16_t length; + ip6_addr dst_prefix; + int32_t src_pxlen; /* s32 to avoid padding */ + ip6_addr src_prefix; +} net_addr_ip6_sadr; + +typedef union net_addr_union { + net_addr n; + net_addr_ip4 ip4; + net_addr_ip6 ip6; + net_addr_vpn4 vpn4; + net_addr_vpn6 vpn6; + net_addr_roa4 roa4; + net_addr_roa6 roa6; + net_addr_flow4 flow4; + net_addr_flow6 flow6; + net_addr_ip6_sadr ip6_sadr; + net_addr_mpls mpls; +} net_addr_union; + +void +read_bird_feed(const char *sock_name, const char *vrf, class rib_t *rib); + +#endif diff --git a/controlplane/main.cpp b/controlplane/main.cpp index d5288f79..2fb6debf 100644 --- a/controlplane/main.cpp +++ b/controlplane/main.cpp @@ -1,5 +1,5 @@ #include -#include +//#include #include @@ -58,7 +58,7 @@ int main(int argc, return 3; } - sd_notify(0, "READY=1"); +// sd_notify(0, "READY=1"); application.start(); application.join(); diff --git a/controlplane/meson.build b/controlplane/meson.build index f888b09c..6fe171fa 100644 --- a/controlplane/meson.build +++ b/controlplane/meson.build @@ -1,6 +1,6 @@ dependencies = [] dependencies += libjson.get_variable('nlohmann_json_dep') -dependencies += dependency('libsystemd') +#dependencies += dependency('libsystemd') dependencies += dependency('threads') dependencies += libyanet_fwparser_dep dependencies += libyanet_protobuf_dep @@ -31,7 +31,8 @@ sources = files('acl_compiler.cpp', 'rib.cpp', 'route.cpp', 'telegraf.cpp', - 'tun64.cpp') + 'tun64.cpp', + 'libbird.cpp') foreach arch : archs bin = 'yanet-controlplane' diff --git a/controlplane/rib.cpp b/controlplane/rib.cpp index c66e21ed..4d531545 100644 --- a/controlplane/rib.cpp +++ b/controlplane/rib.cpp @@ -1,6 +1,9 @@ #include "rib.h" +#include "libbird.h" #include "controlplane.h" +#include + using namespace controlplane::module; rib_t::rib_t() @@ -69,6 +72,10 @@ eResult rib_t::init() rib_thread(); }); + funcThreads.emplace_back([this]() { + bird_thread(); + }); + return eResult::success; } @@ -873,4 +880,22 @@ void rib_t::rib_thread() std::this_thread::sleep_for(std::chrono::milliseconds{200}); } + +} + +void rib_t::bird_thread() +{ + while (!flagStop) { + read_bird_feed("/tmp/export.sock", "default", this); + + common::icp::rib_update::clear request = {"bgp", std::nullopt}; +/* std::get<1>(request) = {peer_address, + {"default", ///< @todo: vrf + YANET_RIB_PRIORITY_DEFAULT}}; +*/ + rib_clear(request); + + std::this_thread::sleep_for(std::chrono::milliseconds{200}); + } + } diff --git a/controlplane/rib.h b/controlplane/rib.h index c436dc3b..a6f9d977 100644 --- a/controlplane/rib.h +++ b/controlplane/rib.h @@ -62,6 +62,7 @@ class rib_t : public cModule void rib_eor(const common::icp::rib_update::eor& request); void rib_thread(); + void bird_thread(); protected: mutable std::mutex rib_update_mutex; diff --git a/dataplane/main.cpp b/dataplane/main.cpp index e88d5ab3..aeec9503 100644 --- a/dataplane/main.cpp +++ b/dataplane/main.cpp @@ -1,5 +1,5 @@ #include -#include +//#include #include @@ -67,7 +67,7 @@ int main(int argc, return 3; } - sd_notify(0, "READY=1"); +// sd_notify(0, "READY=1"); dataPlane.start(); dataPlane.join(); diff --git a/dataplane/meson.build b/dataplane/meson.build index f3599582..af1ba5d0 100644 --- a/dataplane/meson.build +++ b/dataplane/meson.build @@ -1,7 +1,7 @@ dependencies = [] dependencies += libdpdk.get_variable('dpdk_dep') dependencies += libjson.get_variable('nlohmann_json_dep') -dependencies += dependency('libsystemd') +#dependencies += dependency('libsystemd') dependencies += dependency('threads') sources = files('bus.cpp', diff --git a/logger/main.cpp b/logger/main.cpp index 1492ee98..8510606d 100644 --- a/logger/main.cpp +++ b/logger/main.cpp @@ -318,7 +318,7 @@ int main(int argc, return ret; } - sd_notify(0, "READY=1"); + //sd_notify(0, "READY=1"); return runLogger(); } diff --git a/logger/meson.build b/logger/meson.build index c07d93eb..8ba9e5e4 100644 --- a/logger/meson.build +++ b/logger/meson.build @@ -1,7 +1,7 @@ dependencies = [] dependencies += libdpdk.get_variable('dpdk_dep') dependencies += libjson.get_variable('nlohmann_json_dep') -dependencies += dependency('libsystemd') +#dependencies += dependency('libsystemd') dependencies += dependency('threads') sources = files('main.cpp') diff --git a/meson.build b/meson.build index 970a7484..ccbee825 100644 --- a/meson.build +++ b/meson.build @@ -2,9 +2,7 @@ project('yanet', 'cpp', meson_version: '>= 0.61', default_options: ['cpp_std=c++17', 'buildtype=release', - 'warning_level=2', - 'werror=true', - 'b_lto=true']) + 'warning_level=2']) yanet_rootdir = include_directories('.')