summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/labeler.yml2
-rw-r--r--.github/workflows/labeler.yml19
-rw-r--r--.github/workflows/mergifyio_backport.yml21
-rw-r--r--bgpd/bgp_attr.h31
-rw-r--r--bgpd/bgp_route.c7
-rw-r--r--bgpd/bgp_routemap.c5
-rw-r--r--bgpd/bgp_snmp.c858
-rw-r--r--bgpd/bgp_snmp.h32
-rw-r--r--bgpd/bgp_snmp_bgp4.c814
-rw-r--r--bgpd/bgp_snmp_bgp4.h90
-rw-r--r--bgpd/bgp_snmp_bgp4v2.c455
-rw-r--r--bgpd/bgp_snmp_bgp4v2.h97
-rw-r--r--bgpd/bgp_updgrp.h2
-rw-r--r--bgpd/bgp_updgrp_packet.c26
-rw-r--r--bgpd/subdir.am5
-rwxr-xr-xdebian/rules6
-rw-r--r--doc/developer/packaging-debian.rst2
-rw-r--r--doc/developer/packaging-redhat.rst1
-rw-r--r--include/subdir.am1
-rw-r--r--lib/lib_vty.c12
-rw-r--r--lib/smux.h10
-rw-r--r--lib/subdir.am2
-rw-r--r--lib/tc.c88
-rw-r--r--lib/tc.h151
-rw-r--r--lib/zclient.c91
-rw-r--r--lib/zclient.h6
-rw-r--r--redhat/frr.spec.in2
-rw-r--r--sharpd/sharp_vty.c61
-rw-r--r--sharpd/sharp_zebra.c122
-rw-r--r--sharpd/sharp_zebra.h5
-rw-r--r--tests/topotests/bfd_topo1/test_bfd_topo1.py2
-rw-r--r--tests/topotests/bgp_features/test_bgp_features.py12
-rw-r--r--tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py7
-rw-r--r--tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py4
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/__init__.py0
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/cpe1/bgpd.conf9
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/cpe1/zebra.conf9
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/cpe2/bgpd.conf6
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/cpe2/zebra.conf6
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf38
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe1/ldpd.conf10
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe1/ospf6d.conf12
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe1/zebra.conf14
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf29
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe2/ldpd.conf10
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe2/ospf6d.conf12
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe2/zebra.conf14
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py138
-rw-r--r--tests/topotests/cspf_topo1/test_cspf_topo1.py2
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/bfdd.conf2
-rw-r--r--tests/topotests/isis_lfa_topo1/rt2/bfdd.conf2
-rwxr-xr-xtests/topotests/isis_lfa_topo1/test_isis_lfa_topo1.py134
-rwxr-xr-xtests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py14
-rwxr-xr-xtests/topotests/tc_basic/test_tc_basic.py133
-rw-r--r--vtysh/vtysh.c27
-rw-r--r--zebra/debug.c13
-rw-r--r--zebra/debug.h5
-rw-r--r--zebra/dplane_fpm_nl.c11
-rw-r--r--zebra/interface.c11
-rw-r--r--zebra/kernel_netlink.c43
-rw-r--r--zebra/kernel_socket.c11
-rw-r--r--zebra/rtread_netlink.c7
-rw-r--r--zebra/rtread_sysctl.c5
-rw-r--r--zebra/subdir.am2
-rw-r--r--zebra/tc_netlink.c735
-rw-r--r--zebra/tc_netlink.h50
-rw-r--r--zebra/tc_socket.c26
-rw-r--r--zebra/zapi_msg.c172
-rw-r--r--zebra/zebra_dplane.c450
-rw-r--r--zebra/zebra_dplane.h71
-rw-r--r--zebra/zebra_nhg.c11
-rw-r--r--zebra/zebra_ns.c2
-rw-r--r--zebra/zebra_rib.c11
-rw-r--r--zebra/zebra_router.c15
-rw-r--r--zebra/zebra_router.h4
-rw-r--r--zebra/zebra_tc.c444
-rw-r--r--zebra/zebra_tc.h79
77 files changed, 4565 insertions, 1283 deletions
diff --git a/.github/labeler.yml b/.github/labeler.yml
deleted file mode 100644
index af29289d33..0000000000
--- a/.github/labeler.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-backport:
- - '(Mergifyio backport )'
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
deleted file mode 100644
index 79bb73f414..0000000000
--- a/.github/workflows/labeler.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-name: Labeler
-
-on:
- pull_request_target:
- types:
- - opened
- - reopened
-
-jobs:
- labeler:
- runs-on: ubuntu-latest
- permissions:
- contents: read
- pull-requests: write
- steps:
- - uses: github/issue-labeler@v2.5
- with:
- repo-token: "${{ secrets.GITHUB_TOKEN }}"
- configuration-path: .github/labeler.yml
diff --git a/.github/workflows/mergifyio_backport.yml b/.github/workflows/mergifyio_backport.yml
new file mode 100644
index 0000000000..bca8f34fec
--- /dev/null
+++ b/.github/workflows/mergifyio_backport.yml
@@ -0,0 +1,21 @@
+name: Mergifyio backport
+
+on: [issue_comment]
+
+jobs:
+ mergifyio_backport:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+
+ - uses: actions-ecosystem/action-regex-match@v2
+ id: regex-match
+ with:
+ text: ${{ github.event.comment.body }}
+ regex: '[Mm]ergifyio backport '
+
+ - uses: actions-ecosystem/action-add-labels@v1
+ if: ${{ steps.regex-match.outputs.match != '' }}
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ labels: backport
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index bc82d0c6ed..a34da1a6de 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -344,6 +344,8 @@ struct attr {
#define BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED (1 << 6)
#define BATTR_RMAP_LINK_BW_SET (1 << 7)
#define BATTR_RMAP_L3VPN_ACCEPT_GRE (1 << 8)
+#define BATTR_RMAP_VPNV4_NHOP_CHANGED (1 << 9)
+#define BATTR_RMAP_VPNV6_GLOBAL_NHOP_CHANGED (1 << 10)
/* Router Reflector related structure. */
struct cluster_list {
@@ -473,20 +475,23 @@ extern void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt);
extern enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer,
struct attr *attr);
-static inline int bgp_rmap_nhop_changed(uint32_t out_rmap_flags,
- uint32_t in_rmap_flags)
+static inline bool bgp_rmap_nhop_changed(uint32_t out_rmap_flags,
+ uint32_t in_rmap_flags)
{
- return ((CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_PEER_ADDRESS)
- || CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED)
- || CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV4_NHOP_CHANGED)
- || CHECK_FLAG(out_rmap_flags,
- BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED)
- || CHECK_FLAG(out_rmap_flags,
- BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED)
- || CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV6_LL_NHOP_CHANGED)
- || CHECK_FLAG(in_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED))
- ? 1
- : 0);
+ return ((CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_PEER_ADDRESS) ||
+ CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED) ||
+ CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV4_NHOP_CHANGED) ||
+ CHECK_FLAG(out_rmap_flags, BATTR_RMAP_VPNV4_NHOP_CHANGED) ||
+ CHECK_FLAG(out_rmap_flags,
+ BATTR_RMAP_VPNV6_GLOBAL_NHOP_CHANGED) ||
+ CHECK_FLAG(out_rmap_flags,
+ BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED) ||
+ CHECK_FLAG(out_rmap_flags,
+ BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED) ||
+ CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV6_LL_NHOP_CHANGED) ||
+ CHECK_FLAG(in_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED))
+ ? true
+ : false);
}
static inline uint32_t mac_mobility_seqnum(struct attr *attr)
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 2ca9502545..d6e6fc952f 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -2583,8 +2583,8 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
zlog_debug(
- "%s: BGP_PATH_ANNC_NH_SELF, family=%s",
- __func__, family2str(family));
+ "%s: %pFX BGP_PATH_ANNC_NH_SELF, family=%s",
+ __func__, p, family2str(family));
subgroup_announce_reset_nhop(family, attr);
nh_reset = true;
}
@@ -11714,6 +11714,9 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi,
vty_out(vty,
"\nDisplayed %ld routes and %ld total paths\n",
output_cum, total_cum);
+ } else {
+ if (use_json && output_cum == 0)
+ vty_out(vty, "{}\n");
}
return CMD_SUCCESS;
}
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index b736e6c38a..9422469bca 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -3725,6 +3725,8 @@ route_set_vpnv4_nexthop(void *rule, const struct prefix *prefix, void *object)
path->attr->mp_nexthop_global_in = *address;
path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_VPNV4_NHOP_CHANGED);
+
return RMAP_OKAY;
}
@@ -3762,6 +3764,9 @@ route_set_vpnv6_nexthop(void *rule, const struct prefix *prefix, void *object)
sizeof(struct in6_addr));
path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_VPNV6_GLOBAL;
+ SET_FLAG(path->attr->rmap_change_flags,
+ BATTR_RMAP_VPNV6_GLOBAL_NHOP_CHANGED);
+
return RMAP_OKAY;
}
diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c
index 6bc313464a..b202ec2e17 100644
--- a/bgpd/bgp_snmp.c
+++ b/bgpd/bgp_snmp.c
@@ -40,864 +40,16 @@
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_snmp.h"
+#include "bgpd/bgp_snmp_bgp4.h"
+#include "bgpd/bgp_snmp_bgp4v2.h"
#include "bgpd/bgp_mplsvpn_snmp.h"
-/* BGP4-MIB described in RFC1657. */
-#define BGP4MIB 1,3,6,1,2,1,15
-
-/* BGP TRAP. */
-#define BGPESTABLISHED 1
-#define BGPBACKWARDTRANSITION 2
-
-/* BGP MIB bgpVersion. */
-#define BGPVERSION 0
-
-/* BGP MIB bgpLocalAs. */
-#define BGPLOCALAS 0
-
-/* BGP MIB bgpPeerTable. */
-#define BGPPEERIDENTIFIER 1
-#define BGPPEERSTATE 2
-#define BGPPEERADMINSTATUS 3
-#define BGPPEERNEGOTIATEDVERSION 4
-#define BGPPEERLOCALADDR 5
-#define BGPPEERLOCALPORT 6
-#define BGPPEERREMOTEADDR 7
-#define BGPPEERREMOTEPORT 8
-#define BGPPEERREMOTEAS 9
-#define BGPPEERINUPDATES 10
-#define BGPPEEROUTUPDATES 11
-#define BGPPEERINTOTALMESSAGES 12
-#define BGPPEEROUTTOTALMESSAGES 13
-#define BGPPEERLASTERROR 14
-#define BGPPEERFSMESTABLISHEDTRANSITIONS 15
-#define BGPPEERFSMESTABLISHEDTIME 16
-#define BGPPEERCONNECTRETRYINTERVAL 17
-#define BGPPEERHOLDTIME 18
-#define BGPPEERKEEPALIVE 19
-#define BGPPEERHOLDTIMECONFIGURED 20
-#define BGPPEERKEEPALIVECONFIGURED 21
-#define BGPPEERMINROUTEADVERTISEMENTINTERVAL 22
-#define BGPPEERINUPDATEELAPSEDTIME 23
-
-/* BGP MIB bgpIdentifier. */
-#define BGPIDENTIFIER 0
-
-/* BGP MIB bgpRcvdPathAttrTable */
-#define BGPPATHATTRPEER 1
-#define BGPPATHATTRDESTNETWORK 2
-#define BGPPATHATTRORIGIN 3
-#define BGPPATHATTRASPATH 4
-#define BGPPATHATTRNEXTHOP 5
-#define BGPPATHATTRINTERASMETRIC 6
-
-/* BGP MIB bgp4PathAttrTable. */
-#define BGP4PATHATTRPEER 1
-#define BGP4PATHATTRIPADDRPREFIXLEN 2
-#define BGP4PATHATTRIPADDRPREFIX 3
-#define BGP4PATHATTRORIGIN 4
-#define BGP4PATHATTRASPATHSEGMENT 5
-#define BGP4PATHATTRNEXTHOP 6
-#define BGP4PATHATTRMULTIEXITDISC 7
-#define BGP4PATHATTRLOCALPREF 8
-#define BGP4PATHATTRATOMICAGGREGATE 9
-#define BGP4PATHATTRAGGREGATORAS 10
-#define BGP4PATHATTRAGGREGATORADDR 11
-#define BGP4PATHATTRCALCLOCALPREF 12
-#define BGP4PATHATTRBEST 13
-#define BGP4PATHATTRUNKNOWN 14
-
-/* SNMP value hack. */
-#define INTEGER ASN_INTEGER
-#define INTEGER32 ASN_INTEGER
-#define COUNTER32 ASN_COUNTER
-#define OCTET_STRING ASN_OCTET_STR
-#define IPADDRESS ASN_IPADDRESS
-#define GAUGE32 ASN_UNSIGNED
-
-/* Declare static local variables for convenience. */
-SNMP_LOCAL_VARIABLES
-
-/* BGP-MIB instances. */
-static oid bgp_oid[] = {BGP4MIB};
-static oid bgp_trap_oid[] = {BGP4MIB, 0};
-
-/* IP address 0.0.0.0. */
-static struct in_addr bgp_empty_addr = {.s_addr = 0};
-
-/* Hook functions. */
-static uint8_t *bgpVersion(struct variable *, oid[], size_t *, int, size_t *,
- WriteMethod **);
-static uint8_t *bgpLocalAs(struct variable *, oid[], size_t *, int, size_t *,
- WriteMethod **);
-static uint8_t *bgpPeerTable(struct variable *, oid[], size_t *, int, size_t *,
- WriteMethod **);
-static uint8_t *bgpRcvdPathAttrTable(struct variable *, oid[], size_t *, int,
- size_t *, WriteMethod **);
-static uint8_t *bgpIdentifier(struct variable *, oid[], size_t *, int, size_t *,
- WriteMethod **);
-static uint8_t *bgp4PathAttrTable(struct variable *, oid[], size_t *, int,
- size_t *, WriteMethod **);
-/* static uint8_t *bgpTraps (); */
-
-static struct variable bgp_variables[] = {
- /* BGP version. */
- {BGPVERSION, OCTET_STRING, RONLY, bgpVersion, 1, {1}},
- /* BGP local AS. */
- {BGPLOCALAS, INTEGER, RONLY, bgpLocalAs, 1, {2}},
- /* BGP peer table. */
- {BGPPEERIDENTIFIER, IPADDRESS, RONLY, bgpPeerTable, 3, {3, 1, 1}},
- {BGPPEERSTATE, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 2}},
- {BGPPEERADMINSTATUS, INTEGER, RWRITE, bgpPeerTable, 3, {3, 1, 3}},
- {BGPPEERNEGOTIATEDVERSION,
- INTEGER32,
- RONLY,
- bgpPeerTable,
- 3,
- {3, 1, 4}},
- {BGPPEERLOCALADDR, IPADDRESS, RONLY, bgpPeerTable, 3, {3, 1, 5}},
- {BGPPEERLOCALPORT, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 6}},
- {BGPPEERREMOTEADDR, IPADDRESS, RONLY, bgpPeerTable, 3, {3, 1, 7}},
- {BGPPEERREMOTEPORT, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 8}},
- {BGPPEERREMOTEAS, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 9}},
- {BGPPEERINUPDATES, COUNTER32, RONLY, bgpPeerTable, 3, {3, 1, 10}},
- {BGPPEEROUTUPDATES, COUNTER32, RONLY, bgpPeerTable, 3, {3, 1, 11}},
- {BGPPEERINTOTALMESSAGES, COUNTER32, RONLY, bgpPeerTable, 3, {3, 1, 12}},
- {BGPPEEROUTTOTALMESSAGES,
- COUNTER32,
- RONLY,
- bgpPeerTable,
- 3,
- {3, 1, 13}},
- {BGPPEERLASTERROR, OCTET_STRING, RONLY, bgpPeerTable, 3, {3, 1, 14}},
- {BGPPEERFSMESTABLISHEDTRANSITIONS,
- COUNTER32,
- RONLY,
- bgpPeerTable,
- 3,
- {3, 1, 15}},
- {BGPPEERFSMESTABLISHEDTIME,
- GAUGE32,
- RONLY,
- bgpPeerTable,
- 3,
- {3, 1, 16}},
- {BGPPEERCONNECTRETRYINTERVAL,
- INTEGER,
- RWRITE,
- bgpPeerTable,
- 3,
- {3, 1, 17}},
- {BGPPEERHOLDTIME, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 18}},
- {BGPPEERKEEPALIVE, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 19}},
- {BGPPEERHOLDTIMECONFIGURED,
- INTEGER,
- RWRITE,
- bgpPeerTable,
- 3,
- {3, 1, 20}},
- {BGPPEERKEEPALIVECONFIGURED,
- INTEGER,
- RWRITE,
- bgpPeerTable,
- 3,
- {3, 1, 21}},
- {BGPPEERMINROUTEADVERTISEMENTINTERVAL,
- INTEGER,
- RWRITE,
- bgpPeerTable,
- 3,
- {3, 1, 23}},
- {BGPPEERINUPDATEELAPSEDTIME,
- GAUGE32,
- RONLY,
- bgpPeerTable,
- 3,
- {3, 1, 24}},
- /* BGP identifier. */
- {BGPIDENTIFIER, IPADDRESS, RONLY, bgpIdentifier, 1, {4}},
- /* BGP received path attribute table. */
- {BGPPATHATTRPEER, IPADDRESS, RONLY, bgpRcvdPathAttrTable, 3, {5, 1, 1}},
- {BGPPATHATTRDESTNETWORK,
- IPADDRESS,
- RONLY,
- bgpRcvdPathAttrTable,
- 3,
- {5, 1, 2}},
- {BGPPATHATTRORIGIN, INTEGER, RONLY, bgpRcvdPathAttrTable, 3, {5, 1, 3}},
- {BGPPATHATTRASPATH,
- OCTET_STRING,
- RONLY,
- bgpRcvdPathAttrTable,
- 3,
- {5, 1, 4}},
- {BGPPATHATTRNEXTHOP,
- IPADDRESS,
- RONLY,
- bgpRcvdPathAttrTable,
- 3,
- {5, 1, 5}},
- {BGPPATHATTRINTERASMETRIC,
- INTEGER32,
- RONLY,
- bgpRcvdPathAttrTable,
- 3,
- {5, 1, 6}},
- /* BGP-4 received path attribute table. */
- {BGP4PATHATTRPEER, IPADDRESS, RONLY, bgp4PathAttrTable, 3, {6, 1, 1}},
- {BGP4PATHATTRIPADDRPREFIXLEN,
- INTEGER,
- RONLY,
- bgp4PathAttrTable,
- 3,
- {6, 1, 2}},
- {BGP4PATHATTRIPADDRPREFIX,
- IPADDRESS,
- RONLY,
- bgp4PathAttrTable,
- 3,
- {6, 1, 3}},
- {BGP4PATHATTRORIGIN, INTEGER, RONLY, bgp4PathAttrTable, 3, {6, 1, 4}},
- {BGP4PATHATTRASPATHSEGMENT,
- OCTET_STRING,
- RONLY,
- bgp4PathAttrTable,
- 3,
- {6, 1, 5}},
- {BGP4PATHATTRNEXTHOP,
- IPADDRESS,
- RONLY,
- bgp4PathAttrTable,
- 3,
- {6, 1, 6}},
- {BGP4PATHATTRMULTIEXITDISC,
- INTEGER,
- RONLY,
- bgp4PathAttrTable,
- 3,
- {6, 1, 7}},
- {BGP4PATHATTRLOCALPREF,
- INTEGER,
- RONLY,
- bgp4PathAttrTable,
- 3,
- {6, 1, 8}},
- {BGP4PATHATTRATOMICAGGREGATE,
- INTEGER,
- RONLY,
- bgp4PathAttrTable,
- 3,
- {6, 1, 9}},
- {BGP4PATHATTRAGGREGATORAS,
- INTEGER,
- RONLY,
- bgp4PathAttrTable,
- 3,
- {6, 1, 10}},
- {BGP4PATHATTRAGGREGATORADDR,
- IPADDRESS,
- RONLY,
- bgp4PathAttrTable,
- 3,
- {6, 1, 11}},
- {BGP4PATHATTRCALCLOCALPREF,
- INTEGER,
- RONLY,
- bgp4PathAttrTable,
- 3,
- {6, 1, 12}},
- {BGP4PATHATTRBEST, INTEGER, RONLY, bgp4PathAttrTable, 3, {6, 1, 13}},
- {BGP4PATHATTRUNKNOWN,
- OCTET_STRING,
- RONLY,
- bgp4PathAttrTable,
- 3,
- {6, 1, 14}},
-};
-
-
-static uint8_t *bgpVersion(struct variable *v, oid name[], size_t *length,
- int exact, size_t *var_len,
- WriteMethod **write_method)
-{
- static uint8_t version;
-
- if (smux_header_generic(v, name, length, exact, var_len, write_method)
- == MATCH_FAILED)
- return NULL;
-
- /* Return BGP version. Zebra bgpd only support version 4. */
- version = (0x80 >> (BGP_VERSION_4 - 1));
-
- /* Return octet string length 1. */
- *var_len = 1;
- return &version;
-}
-
-static uint8_t *bgpLocalAs(struct variable *v, oid name[], size_t *length,
- int exact, size_t *var_len,
- WriteMethod **write_method)
-{
- struct bgp *bgp;
-
- if (smux_header_generic(v, name, length, exact, var_len, write_method)
- == MATCH_FAILED)
- return NULL;
-
- /* Get BGP structure. */
- bgp = bgp_get_default();
- if (!bgp)
- return NULL;
-
- return SNMP_INTEGER(bgp->as);
-}
-
-static struct peer *peer_lookup_addr_ipv4(struct in_addr *src)
-{
- struct bgp *bgp;
- struct peer *peer;
- struct listnode *node;
- struct listnode *bgpnode;
-
- for (ALL_LIST_ELEMENTS_RO(bm->bgp, bgpnode, bgp)) {
- for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
- if (sockunion_family(&peer->su) != AF_INET)
- continue;
-
- if (sockunion2ip(&peer->su) == src->s_addr)
- return peer;
- }
- }
-
- return NULL;
-}
-
-static struct peer *bgp_peer_lookup_next(struct in_addr *src)
-{
- struct bgp *bgp;
- struct peer *peer;
- struct peer *next_peer = NULL;
- struct listnode *node;
- struct listnode *bgpnode;
-
- for (ALL_LIST_ELEMENTS_RO(bm->bgp, bgpnode, bgp)) {
- for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
- if (sockunion_family(&peer->su) != AF_INET)
- continue;
- if (ntohl(sockunion2ip(&peer->su)) <= ntohl(src->s_addr))
- continue;
-
- if (!next_peer
- || ntohl(sockunion2ip(&next_peer->su))
- > ntohl(sockunion2ip(&peer->su))) {
- next_peer = peer;
- }
- }
- }
-
- if (next_peer) {
- src->s_addr = sockunion2ip(&next_peer->su);
- return next_peer;
- }
-
- return NULL;
-}
-
-/* 1.3.6.1.2.1.15.3.1.x = 10 */
-#define PEERTAB_NAMELEN 10
-
-static struct peer *bgpPeerTable_lookup(struct variable *v, oid name[],
- size_t *length, struct in_addr *addr,
- int exact)
-{
- struct peer *peer = NULL;
- size_t namelen = v ? v->namelen : PEERTAB_NAMELEN;
- int len;
-
- if (exact) {
- /* Check the length. */
- if (*length - namelen != sizeof(struct in_addr))
- return NULL;
-
- oid2in_addr(name + namelen, IN_ADDR_SIZE, addr);
-
- peer = peer_lookup_addr_ipv4(addr);
- return peer;
- } else {
- len = *length - namelen;
- if (len > 4)
- len = 4;
-
- oid2in_addr(name + namelen, len, addr);
-
- peer = bgp_peer_lookup_next(addr);
-
- if (peer == NULL)
- return NULL;
-
- oid_copy_in_addr(name + namelen, addr);
- *length = sizeof(struct in_addr) + namelen;
-
- return peer;
- }
- return NULL;
-}
-
-/* BGP write methods. */
-static int write_bgpPeerTable(int action, uint8_t *var_val,
- uint8_t var_val_type, size_t var_val_len,
- uint8_t *statP, oid *name, size_t length)
-{
- struct in_addr addr;
- struct peer *peer;
- long intval;
-
- if (var_val_type != ASN_INTEGER) {
- return SNMP_ERR_WRONGTYPE;
- }
- if (var_val_len != sizeof(long)) {
- return SNMP_ERR_WRONGLENGTH;
- }
-
- intval = *(long *)var_val;
-
- memset(&addr, 0, sizeof(addr));
-
- peer = bgpPeerTable_lookup(NULL, name, &length, &addr, 1);
- if (!peer)
- return SNMP_ERR_NOSUCHNAME;
-
- if (action != SNMP_MSG_INTERNAL_SET_COMMIT)
- return SNMP_ERR_NOERROR;
-
- zlog_info("%s: SNMP write .%ld = %ld", peer->host,
- (long)name[PEERTAB_NAMELEN - 1], intval);
-
- switch (name[PEERTAB_NAMELEN - 1]) {
- case BGPPEERADMINSTATUS:
-#define BGP_PeerAdmin_stop 1
-#define BGP_PeerAdmin_start 2
- /* When the peer is established, */
- if (intval == BGP_PeerAdmin_stop)
- BGP_EVENT_ADD(peer, BGP_Stop);
- else if (intval == BGP_PeerAdmin_start)
- ; /* Do nothing. */
- else
- return SNMP_ERR_NOSUCHNAME;
- break;
- case BGPPEERCONNECTRETRYINTERVAL:
- peer_flag_set(peer, PEER_FLAG_TIMER_CONNECT);
- peer->connect = intval;
- peer->v_connect = intval;
- break;
- case BGPPEERHOLDTIMECONFIGURED:
- peer_flag_set(peer, PEER_FLAG_TIMER);
- peer->holdtime = intval;
- peer->v_holdtime = intval;
- break;
- case BGPPEERKEEPALIVECONFIGURED:
- peer_flag_set(peer, PEER_FLAG_TIMER);
- peer->keepalive = intval;
- peer->v_keepalive = intval;
- break;
- case BGPPEERMINROUTEADVERTISEMENTINTERVAL:
- peer->v_routeadv = intval;
- break;
- }
- return SNMP_ERR_NOERROR;
-}
-
-static uint8_t *bgpPeerTable(struct variable *v, oid name[], size_t *length,
- int exact, size_t *var_len,
- WriteMethod **write_method)
-{
- static struct in_addr addr;
- struct peer *peer;
- uint32_t ui, uo;
-
- if (smux_header_table(v, name, length, exact, var_len, write_method)
- == MATCH_FAILED)
- return NULL;
- memset(&addr, 0, sizeof(addr));
-
- peer = bgpPeerTable_lookup(v, name, length, &addr, exact);
- if (!peer)
- return NULL;
-
- switch (v->magic) {
- case BGPPEERIDENTIFIER:
- return SNMP_IPADDRESS(peer->remote_id);
- case BGPPEERSTATE:
- return SNMP_INTEGER(peer->status);
- case BGPPEERADMINSTATUS:
- *write_method = write_bgpPeerTable;
-#define BGP_PeerAdmin_stop 1
-#define BGP_PeerAdmin_start 2
- if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN))
- return SNMP_INTEGER(BGP_PeerAdmin_stop);
- else
- return SNMP_INTEGER(BGP_PeerAdmin_start);
- case BGPPEERNEGOTIATEDVERSION:
- return SNMP_INTEGER(BGP_VERSION_4);
- case BGPPEERLOCALADDR:
- if (peer->su_local)
- return SNMP_IPADDRESS(peer->su_local->sin.sin_addr);
- else
- return SNMP_IPADDRESS(bgp_empty_addr);
- case BGPPEERLOCALPORT:
- if (peer->su_local)
- return SNMP_INTEGER(
- ntohs(peer->su_local->sin.sin_port));
- else
- return SNMP_INTEGER(0);
- case BGPPEERREMOTEADDR:
- if (peer->su_remote)
- return SNMP_IPADDRESS(peer->su_remote->sin.sin_addr);
- else
- return SNMP_IPADDRESS(bgp_empty_addr);
- case BGPPEERREMOTEPORT:
- if (peer->su_remote)
- return SNMP_INTEGER(
- ntohs(peer->su_remote->sin.sin_port));
- else
- return SNMP_INTEGER(0);
- case BGPPEERREMOTEAS:
- return SNMP_INTEGER(peer->as);
- case BGPPEERINUPDATES:
- ui = atomic_load_explicit(&peer->update_in,
- memory_order_relaxed);
- return SNMP_INTEGER(ui);
- case BGPPEEROUTUPDATES:
- uo = atomic_load_explicit(&peer->update_out,
- memory_order_relaxed);
- return SNMP_INTEGER(uo);
- case BGPPEERINTOTALMESSAGES:
- return SNMP_INTEGER(PEER_TOTAL_RX(peer));
- case BGPPEEROUTTOTALMESSAGES:
- return SNMP_INTEGER(PEER_TOTAL_TX(peer));
- case BGPPEERLASTERROR: {
- static uint8_t lasterror[2];
- lasterror[0] = peer->notify.code;
- lasterror[1] = peer->notify.subcode;
- *var_len = 2;
- return (uint8_t *)&lasterror;
- }
- case BGPPEERFSMESTABLISHEDTRANSITIONS:
- return SNMP_INTEGER(peer->established);
- case BGPPEERFSMESTABLISHEDTIME:
- if (peer->uptime == 0)
- return SNMP_INTEGER(0);
- else
- return SNMP_INTEGER(monotime(NULL) - peer->uptime);
- case BGPPEERCONNECTRETRYINTERVAL:
- *write_method = write_bgpPeerTable;
- return SNMP_INTEGER(peer->v_connect);
- case BGPPEERHOLDTIME:
- return SNMP_INTEGER(peer->v_holdtime);
- case BGPPEERKEEPALIVE:
- return SNMP_INTEGER(peer->v_keepalive);
- case BGPPEERHOLDTIMECONFIGURED:
- *write_method = write_bgpPeerTable;
- if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
- return SNMP_INTEGER(peer->holdtime);
- else
- return SNMP_INTEGER(peer->v_holdtime);
- case BGPPEERKEEPALIVECONFIGURED:
- *write_method = write_bgpPeerTable;
- if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
- return SNMP_INTEGER(peer->keepalive);
- else
- return SNMP_INTEGER(peer->v_keepalive);
- case BGPPEERMINROUTEADVERTISEMENTINTERVAL:
- *write_method = write_bgpPeerTable;
- return SNMP_INTEGER(peer->v_routeadv);
- case BGPPEERINUPDATEELAPSEDTIME:
- if (peer->update_time == 0)
- return SNMP_INTEGER(0);
- else
- return SNMP_INTEGER(monotime(NULL) - peer->update_time);
- default:
- return NULL;
- }
- return NULL;
-}
-
-static uint8_t *bgpIdentifier(struct variable *v, oid name[], size_t *length,
- int exact, size_t *var_len,
- WriteMethod **write_method)
-{
- struct bgp *bgp;
-
- if (smux_header_generic(v, name, length, exact, var_len, write_method)
- == MATCH_FAILED)
- return NULL;
-
- bgp = bgp_get_default();
- if (!bgp)
- return NULL;
-
- return SNMP_IPADDRESS(bgp->router_id);
-}
-
-static uint8_t *bgpRcvdPathAttrTable(struct variable *v, oid name[],
- size_t *length, int exact, size_t *var_len,
- WriteMethod **write_method)
-{
- /* Received Path Attribute Table. This table contains, one entry
- per path to a network, path attributes received from all peers
- running BGP version 3 or less. This table is obsolete, having
- been replaced in functionality with the bgp4PathAttrTable. */
- return NULL;
-}
-
-static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[],
- size_t *length, struct bgp *bgp,
- struct prefix_ipv4 *addr,
- int exact)
-{
- oid *offset;
- int offsetlen;
- struct bgp_path_info *path;
- struct bgp_path_info *min;
- struct bgp_dest *dest;
- union sockunion su;
- unsigned int len;
- struct in_addr paddr;
-
- sockunion_init(&su);
-
-#define BGP_PATHATTR_ENTRY_OFFSET (IN_ADDR_SIZE + 1 + IN_ADDR_SIZE)
-
- if (exact) {
- if (*length - v->namelen != BGP_PATHATTR_ENTRY_OFFSET)
- return NULL;
-
- /* Set OID offset for prefix. */
- offset = name + v->namelen;
- oid2in_addr(offset, IN_ADDR_SIZE, &addr->prefix);
- offset += IN_ADDR_SIZE;
-
- /* Prefix length. */
- addr->prefixlen = *offset;
- offset++;
-
- /* Peer address. */
- su.sin.sin_family = AF_INET;
- oid2in_addr(offset, IN_ADDR_SIZE, &su.sin.sin_addr);
-
- /* Lookup node. */
- dest = bgp_node_lookup(bgp->rib[AFI_IP][SAFI_UNICAST],
- (struct prefix *)addr);
- if (dest) {
- for (path = bgp_dest_get_bgp_path_info(dest); path;
- path = path->next)
- if (sockunion_same(&path->peer->su, &su))
- return path;
-
- bgp_dest_unlock_node(dest);
- }
- } else {
- offset = name + v->namelen;
- offsetlen = *length - v->namelen;
- len = offsetlen;
-
- if (offsetlen == 0)
- dest = bgp_table_top(bgp->rib[AFI_IP][SAFI_UNICAST]);
- else {
- if (len > IN_ADDR_SIZE)
- len = IN_ADDR_SIZE;
-
- oid2in_addr(offset, len, &addr->prefix);
-
- offset += IN_ADDR_SIZE;
- offsetlen -= IN_ADDR_SIZE;
-
- if (offsetlen > 0)
- addr->prefixlen = *offset;
- else
- addr->prefixlen = len * 8;
-
- dest = bgp_node_get(bgp->rib[AFI_IP][SAFI_UNICAST],
- (struct prefix *)addr);
-
- offset++;
- offsetlen--;
- }
-
- if (offsetlen > 0) {
- len = offsetlen;
- if (len > IN_ADDR_SIZE)
- len = IN_ADDR_SIZE;
-
- oid2in_addr(offset, len, &paddr);
- } else
- paddr.s_addr = INADDR_ANY;
-
- if (!dest)
- return NULL;
-
- do {
- min = NULL;
-
- for (path = bgp_dest_get_bgp_path_info(dest); path;
- path = path->next) {
- if (path->peer->su.sin.sin_family == AF_INET
- && ntohl(paddr.s_addr)
- < ntohl(path->peer->su.sin
- .sin_addr
- .s_addr)) {
- if (min) {
- if (ntohl(path->peer->su.sin
- .sin_addr
- .s_addr)
- < ntohl(min->peer->su.sin
- .sin_addr
- .s_addr))
- min = path;
- } else
- min = path;
- }
- }
-
- if (min) {
- const struct prefix *rn_p =
- bgp_dest_get_prefix(dest);
-
- *length =
- v->namelen + BGP_PATHATTR_ENTRY_OFFSET;
-
- offset = name + v->namelen;
- oid_copy_in_addr(offset, &rn_p->u.prefix4);
- offset += IN_ADDR_SIZE;
- *offset = rn_p->prefixlen;
- offset++;
- oid_copy_in_addr(offset,
- &min->peer->su.sin.sin_addr);
- addr->prefix = rn_p->u.prefix4;
- addr->prefixlen = rn_p->prefixlen;
-
- bgp_dest_unlock_node(dest);
-
- return min;
- }
-
- paddr.s_addr = INADDR_ANY;
- } while ((dest = bgp_route_next(dest)) != NULL);
- }
- return NULL;
-}
-
-static uint8_t *bgp4PathAttrTable(struct variable *v, oid name[],
- size_t *length, int exact, size_t *var_len,
- WriteMethod **write_method)
-{
- struct bgp *bgp;
- struct bgp_path_info *path;
- struct prefix_ipv4 addr;
-
- bgp = bgp_get_default();
- if (!bgp)
- return NULL;
-
- if (smux_header_table(v, name, length, exact, var_len, write_method)
- == MATCH_FAILED)
- return NULL;
- memset(&addr, 0, sizeof(addr));
-
- path = bgp4PathAttrLookup(v, name, length, bgp, &addr, exact);
- if (!path)
- return NULL;
-
- switch (v->magic) {
- case BGP4PATHATTRPEER: /* 1 */
- return SNMP_IPADDRESS(path->peer->su.sin.sin_addr);
- case BGP4PATHATTRIPADDRPREFIXLEN: /* 2 */
- return SNMP_INTEGER(addr.prefixlen);
- case BGP4PATHATTRIPADDRPREFIX: /* 3 */
- return SNMP_IPADDRESS(addr.prefix);
- case BGP4PATHATTRORIGIN: /* 4 */
- return SNMP_INTEGER(path->attr->origin);
- case BGP4PATHATTRASPATHSEGMENT: /* 5 */
- return aspath_snmp_pathseg(path->attr->aspath, var_len);
- case BGP4PATHATTRNEXTHOP: /* 6 */
- return SNMP_IPADDRESS(path->attr->nexthop);
- case BGP4PATHATTRMULTIEXITDISC: /* 7 */
- return SNMP_INTEGER(path->attr->med);
- case BGP4PATHATTRLOCALPREF: /* 8 */
- return SNMP_INTEGER(path->attr->local_pref);
- case BGP4PATHATTRATOMICAGGREGATE: /* 9 */
- return SNMP_INTEGER(1);
- case BGP4PATHATTRAGGREGATORAS: /* 10 */
- return SNMP_INTEGER(path->attr->aggregator_as);
- case BGP4PATHATTRAGGREGATORADDR: /* 11 */
- return SNMP_IPADDRESS(path->attr->aggregator_addr);
- case BGP4PATHATTRCALCLOCALPREF: /* 12 */
- return SNMP_INTEGER(-1);
- case BGP4PATHATTRBEST: /* 13 */
-#define BGP4_PathAttrBest_false 1
-#define BGP4_PathAttrBest_true 2
- if (CHECK_FLAG(path->flags, BGP_PATH_SELECTED))
- return SNMP_INTEGER(BGP4_PathAttrBest_true);
- else
- return SNMP_INTEGER(BGP4_PathAttrBest_false);
- case BGP4PATHATTRUNKNOWN: /* 14 */
- *var_len = 0;
- return NULL;
- }
- return NULL;
-}
-
-/* BGP Traps. */
-static struct trap_object bgpTrapList[] = {{3, {3, 1, BGPPEERREMOTEADDR} },
- {3, {3, 1, BGPPEERLASTERROR} },
- {3, {3, 1, BGPPEERSTATE} } };
-
-static int bgpTrapEstablished(struct peer *peer)
-{
- int ret;
- struct in_addr addr;
- oid index[sizeof(oid) * IN_ADDR_SIZE];
-
- /* Check if this peer just went to Established */
- if ((peer->ostatus != OpenConfirm) || !(peer_established(peer)))
- return 0;
-
- ret = inet_aton(peer->host, &addr);
- if (ret == 0)
- return 0;
-
- oid_copy_in_addr(index, &addr);
-
- smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid,
- array_size(bgp_trap_oid), bgp_oid,
- sizeof(bgp_oid) / sizeof(oid), index, IN_ADDR_SIZE,
- bgpTrapList, array_size(bgpTrapList), BGPESTABLISHED);
- return 0;
-}
-
-static int bgpTrapBackwardTransition(struct peer *peer)
-{
- int ret;
- struct in_addr addr;
- oid index[sizeof(oid) * IN_ADDR_SIZE];
-
- ret = inet_aton(peer->host, &addr);
- if (ret == 0)
- return 0;
-
- oid_copy_in_addr(index, &addr);
-
- smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid,
- array_size(bgp_trap_oid), bgp_oid,
- sizeof(bgp_oid) / sizeof(oid), index, IN_ADDR_SIZE,
- bgpTrapList, array_size(bgpTrapList), BGPBACKWARDTRANSITION);
- return 0;
-}
-
static int bgp_snmp_init(struct thread_master *tm)
{
smux_init(tm);
- REGISTER_MIB("mibII/bgp", bgp_variables, variable, bgp_oid);
+ bgp_snmp_bgp4_init(tm);
+ bgp_snmp_bgp4v2_init(tm);
bgp_mpls_l3vpn_module_init();
return 0;
}
diff --git a/bgpd/bgp_snmp.h b/bgpd/bgp_snmp.h
new file mode 100644
index 0000000000..a89e82b6c6
--- /dev/null
+++ b/bgpd/bgp_snmp.h
@@ -0,0 +1,32 @@
+/* Common header file for BGP SNMP implementation.
+ *
+ * Copyright (C) 2022 Donatas Abraitis <donatas@opensourcerouting.org>
+ *
+ * This file is part of FRRouting (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_SNMP_H_
+#define _FRR_BGP_SNMP_H_
+
+/* SNMP value hack. */
+#define INTEGER ASN_INTEGER
+#define INTEGER32 ASN_INTEGER
+#define COUNTER32 ASN_COUNTER
+#define OCTET_STRING ASN_OCTET_STR
+#define IPADDRESS ASN_IPADDRESS
+#define GAUGE32 ASN_UNSIGNED
+
+#endif /* _FRR_BGP_SNMP_H_ */
diff --git a/bgpd/bgp_snmp_bgp4.c b/bgpd/bgp_snmp_bgp4.c
new file mode 100644
index 0000000000..bb8b7f4b19
--- /dev/null
+++ b/bgpd/bgp_snmp_bgp4.c
@@ -0,0 +1,814 @@
+/* BGP4 SNMP support
+ * Copyright (C) 1999, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "thread.h"
+#include "smux.h"
+#include "filter.h"
+#include "hook.h"
+#include "libfrr.h"
+#include "lib/version.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_snmp.h"
+#include "bgpd/bgp_snmp_bgp4.h"
+#include "bgpd/bgp_mplsvpn_snmp.h"
+
+/* Declare static local variables for convenience. */
+SNMP_LOCAL_VARIABLES
+
+/* BGP-MIB instances. */
+static oid bgp_oid[] = {BGP4MIB};
+static oid bgp_trap_oid[] = {BGP4MIB, 0};
+
+/* IP address 0.0.0.0. */
+static struct in_addr bgp_empty_addr = {.s_addr = 0};
+
+static uint8_t *bgpVersion(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ static uint8_t version;
+
+ if (smux_header_generic(v, name, length, exact, var_len,
+ write_method) == MATCH_FAILED)
+ return NULL;
+
+ /* Return BGP version. Zebra bgpd only support version 4. */
+ version = (0x80 >> (BGP_VERSION_4 - 1));
+
+ /* Return octet string length 1. */
+ *var_len = 1;
+ return &version;
+}
+
+static uint8_t *bgpLocalAs(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct bgp *bgp;
+
+ if (smux_header_generic(v, name, length, exact, var_len,
+ write_method) == MATCH_FAILED)
+ return NULL;
+
+ /* Get BGP structure. */
+ bgp = bgp_get_default();
+ if (!bgp)
+ return NULL;
+
+ return SNMP_INTEGER(bgp->as);
+}
+
+static struct peer *peer_lookup_addr_ipv4(struct in_addr *src)
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ struct listnode *node;
+ struct listnode *bgpnode;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, bgpnode, bgp)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ if (sockunion_family(&peer->su) != AF_INET)
+ continue;
+
+ if (sockunion2ip(&peer->su) == src->s_addr)
+ return peer;
+ }
+ }
+
+ return NULL;
+}
+
+static struct peer *bgp_peer_lookup_next(struct in_addr *src)
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ struct peer *next_peer = NULL;
+ struct listnode *node;
+ struct listnode *bgpnode;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, bgpnode, bgp)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ if (sockunion_family(&peer->su) != AF_INET)
+ continue;
+ if (ntohl(sockunion2ip(&peer->su)) <=
+ ntohl(src->s_addr))
+ continue;
+
+ if (!next_peer ||
+ ntohl(sockunion2ip(&next_peer->su)) >
+ ntohl(sockunion2ip(&peer->su))) {
+ next_peer = peer;
+ }
+ }
+ }
+
+ if (next_peer) {
+ src->s_addr = sockunion2ip(&next_peer->su);
+ return next_peer;
+ }
+
+ return NULL;
+}
+
+/* 1.3.6.1.2.1.15.3.1.x = 10 */
+#define PEERTAB_NAMELEN 10
+
+static struct peer *bgpPeerTable_lookup(struct variable *v, oid name[],
+ size_t *length, struct in_addr *addr,
+ int exact)
+{
+ struct peer *peer = NULL;
+ size_t namelen = v ? v->namelen : PEERTAB_NAMELEN;
+ int len;
+
+ if (exact) {
+ /* Check the length. */
+ if (*length - namelen != sizeof(struct in_addr))
+ return NULL;
+
+ oid2in_addr(name + namelen, IN_ADDR_SIZE, addr);
+
+ peer = peer_lookup_addr_ipv4(addr);
+ return peer;
+ } else {
+ len = *length - namelen;
+ if (len > 4)
+ len = 4;
+
+ oid2in_addr(name + namelen, len, addr);
+
+ peer = bgp_peer_lookup_next(addr);
+
+ if (peer == NULL)
+ return NULL;
+
+ oid_copy_in_addr(name + namelen, addr);
+ *length = sizeof(struct in_addr) + namelen;
+
+ return peer;
+ }
+ return NULL;
+}
+
+/* BGP write methods. */
+static int write_bgpPeerTable(int action, uint8_t *var_val,
+ uint8_t var_val_type, size_t var_val_len,
+ uint8_t *statP, oid *name, size_t length)
+{
+ struct in_addr addr;
+ struct peer *peer;
+ long intval;
+
+ if (var_val_type != ASN_INTEGER) {
+ return SNMP_ERR_WRONGTYPE;
+ }
+ if (var_val_len != sizeof(long)) {
+ return SNMP_ERR_WRONGLENGTH;
+ }
+
+ intval = *(long *)var_val;
+
+ memset(&addr, 0, sizeof(addr));
+
+ peer = bgpPeerTable_lookup(NULL, name, &length, &addr, 1);
+ if (!peer)
+ return SNMP_ERR_NOSUCHNAME;
+
+ if (action != SNMP_MSG_INTERNAL_SET_COMMIT)
+ return SNMP_ERR_NOERROR;
+
+ zlog_info("%s: SNMP write .%ld = %ld", peer->host,
+ (long)name[PEERTAB_NAMELEN - 1], intval);
+
+ switch (name[PEERTAB_NAMELEN - 1]) {
+ case BGPPEERADMINSTATUS:
+#define BGP_PeerAdmin_stop 1
+#define BGP_PeerAdmin_start 2
+ /* When the peer is established, */
+ if (intval == BGP_PeerAdmin_stop)
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ else if (intval == BGP_PeerAdmin_start)
+ ; /* Do nothing. */
+ else
+ return SNMP_ERR_NOSUCHNAME;
+ break;
+ case BGPPEERCONNECTRETRYINTERVAL:
+ peer_flag_set(peer, PEER_FLAG_TIMER_CONNECT);
+ peer->connect = intval;
+ peer->v_connect = intval;
+ break;
+ case BGPPEERHOLDTIMECONFIGURED:
+ peer_flag_set(peer, PEER_FLAG_TIMER);
+ peer->holdtime = intval;
+ peer->v_holdtime = intval;
+ break;
+ case BGPPEERKEEPALIVECONFIGURED:
+ peer_flag_set(peer, PEER_FLAG_TIMER);
+ peer->keepalive = intval;
+ peer->v_keepalive = intval;
+ break;
+ case BGPPEERMINROUTEADVERTISEMENTINTERVAL:
+ peer->v_routeadv = intval;
+ break;
+ }
+ return SNMP_ERR_NOERROR;
+}
+
+static uint8_t *bgpPeerTable(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ static struct in_addr addr;
+ struct peer *peer;
+ uint32_t ui, uo;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method) ==
+ MATCH_FAILED)
+ return NULL;
+ memset(&addr, 0, sizeof(addr));
+
+ peer = bgpPeerTable_lookup(v, name, length, &addr, exact);
+ if (!peer)
+ return NULL;
+
+ switch (v->magic) {
+ case BGPPEERIDENTIFIER:
+ return SNMP_IPADDRESS(peer->remote_id);
+ case BGPPEERSTATE:
+ return SNMP_INTEGER(peer->status);
+ case BGPPEERADMINSTATUS:
+ *write_method = write_bgpPeerTable;
+#define BGP_PeerAdmin_stop 1
+#define BGP_PeerAdmin_start 2
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN))
+ return SNMP_INTEGER(BGP_PeerAdmin_stop);
+ else
+ return SNMP_INTEGER(BGP_PeerAdmin_start);
+ case BGPPEERNEGOTIATEDVERSION:
+ return SNMP_INTEGER(BGP_VERSION_4);
+ case BGPPEERLOCALADDR:
+ if (peer->su_local)
+ return SNMP_IPADDRESS(peer->su_local->sin.sin_addr);
+ else
+ return SNMP_IPADDRESS(bgp_empty_addr);
+ case BGPPEERLOCALPORT:
+ if (peer->su_local)
+ return SNMP_INTEGER(
+ ntohs(peer->su_local->sin.sin_port));
+ else
+ return SNMP_INTEGER(0);
+ case BGPPEERREMOTEADDR:
+ if (peer->su_remote)
+ return SNMP_IPADDRESS(peer->su_remote->sin.sin_addr);
+ else
+ return SNMP_IPADDRESS(bgp_empty_addr);
+ case BGPPEERREMOTEPORT:
+ if (peer->su_remote)
+ return SNMP_INTEGER(
+ ntohs(peer->su_remote->sin.sin_port));
+ else
+ return SNMP_INTEGER(0);
+ case BGPPEERREMOTEAS:
+ return SNMP_INTEGER(peer->as);
+ case BGPPEERINUPDATES:
+ ui = atomic_load_explicit(&peer->update_in,
+ memory_order_relaxed);
+ return SNMP_INTEGER(ui);
+ case BGPPEEROUTUPDATES:
+ uo = atomic_load_explicit(&peer->update_out,
+ memory_order_relaxed);
+ return SNMP_INTEGER(uo);
+ case BGPPEERINTOTALMESSAGES:
+ return SNMP_INTEGER(PEER_TOTAL_RX(peer));
+ case BGPPEEROUTTOTALMESSAGES:
+ return SNMP_INTEGER(PEER_TOTAL_TX(peer));
+ case BGPPEERLASTERROR: {
+ static uint8_t lasterror[2];
+ lasterror[0] = peer->notify.code;
+ lasterror[1] = peer->notify.subcode;
+ *var_len = 2;
+ return (uint8_t *)&lasterror;
+ }
+ case BGPPEERFSMESTABLISHEDTRANSITIONS:
+ return SNMP_INTEGER(peer->established);
+ case BGPPEERFSMESTABLISHEDTIME:
+ if (peer->uptime == 0)
+ return SNMP_INTEGER(0);
+ else
+ return SNMP_INTEGER(monotime(NULL) - peer->uptime);
+ case BGPPEERCONNECTRETRYINTERVAL:
+ *write_method = write_bgpPeerTable;
+ return SNMP_INTEGER(peer->v_connect);
+ case BGPPEERHOLDTIME:
+ return SNMP_INTEGER(peer->v_holdtime);
+ case BGPPEERKEEPALIVE:
+ return SNMP_INTEGER(peer->v_keepalive);
+ case BGPPEERHOLDTIMECONFIGURED:
+ *write_method = write_bgpPeerTable;
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
+ return SNMP_INTEGER(peer->holdtime);
+ else
+ return SNMP_INTEGER(peer->v_holdtime);
+ case BGPPEERKEEPALIVECONFIGURED:
+ *write_method = write_bgpPeerTable;
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
+ return SNMP_INTEGER(peer->keepalive);
+ else
+ return SNMP_INTEGER(peer->v_keepalive);
+ case BGPPEERMINROUTEADVERTISEMENTINTERVAL:
+ *write_method = write_bgpPeerTable;
+ return SNMP_INTEGER(peer->v_routeadv);
+ case BGPPEERINUPDATEELAPSEDTIME:
+ if (peer->update_time == 0)
+ return SNMP_INTEGER(0);
+ else
+ return SNMP_INTEGER(monotime(NULL) - peer->update_time);
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+static uint8_t *bgpIdentifier(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct bgp *bgp;
+
+ if (smux_header_generic(v, name, length, exact, var_len,
+ write_method) == MATCH_FAILED)
+ return NULL;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return NULL;
+
+ return SNMP_IPADDRESS(bgp->router_id);
+}
+
+static uint8_t *bgpRcvdPathAttrTable(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ /* Received Path Attribute Table. This table contains, one entry
+ per path to a network, path attributes received from all peers
+ running BGP version 3 or less. This table is obsolete, having
+ been replaced in functionality with the bgp4PathAttrTable. */
+ return NULL;
+}
+
+static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[],
+ size_t *length, struct bgp *bgp,
+ struct prefix_ipv4 *addr,
+ int exact)
+{
+ oid *offset;
+ int offsetlen;
+ struct bgp_path_info *path;
+ struct bgp_path_info *min;
+ struct bgp_dest *dest;
+ union sockunion su;
+ unsigned int len;
+ struct in_addr paddr;
+
+ sockunion_init(&su);
+
+#define BGP_PATHATTR_ENTRY_OFFSET (IN_ADDR_SIZE + 1 + IN_ADDR_SIZE)
+
+ if (exact) {
+ if (*length - v->namelen != BGP_PATHATTR_ENTRY_OFFSET)
+ return NULL;
+
+ /* Set OID offset for prefix. */
+ offset = name + v->namelen;
+ oid2in_addr(offset, IN_ADDR_SIZE, &addr->prefix);
+ offset += IN_ADDR_SIZE;
+
+ /* Prefix length. */
+ addr->prefixlen = *offset;
+ offset++;
+
+ /* Peer address. */
+ su.sin.sin_family = AF_INET;
+ oid2in_addr(offset, IN_ADDR_SIZE, &su.sin.sin_addr);
+
+ /* Lookup node. */
+ dest = bgp_node_lookup(bgp->rib[AFI_IP][SAFI_UNICAST],
+ (struct prefix *)addr);
+ if (dest) {
+ for (path = bgp_dest_get_bgp_path_info(dest); path;
+ path = path->next)
+ if (sockunion_same(&path->peer->su, &su))
+ return path;
+
+ bgp_dest_unlock_node(dest);
+ }
+ } else {
+ offset = name + v->namelen;
+ offsetlen = *length - v->namelen;
+ len = offsetlen;
+
+ if (offsetlen == 0)
+ dest = bgp_table_top(bgp->rib[AFI_IP][SAFI_UNICAST]);
+ else {
+ if (len > IN_ADDR_SIZE)
+ len = IN_ADDR_SIZE;
+
+ oid2in_addr(offset, len, &addr->prefix);
+
+ offset += IN_ADDR_SIZE;
+ offsetlen -= IN_ADDR_SIZE;
+
+ if (offsetlen > 0)
+ addr->prefixlen = *offset;
+ else
+ addr->prefixlen = len * 8;
+
+ dest = bgp_node_get(bgp->rib[AFI_IP][SAFI_UNICAST],
+ (struct prefix *)addr);
+
+ offset++;
+ offsetlen--;
+ }
+
+ if (offsetlen > 0) {
+ len = offsetlen;
+ if (len > IN_ADDR_SIZE)
+ len = IN_ADDR_SIZE;
+
+ oid2in_addr(offset, len, &paddr);
+ } else
+ paddr.s_addr = INADDR_ANY;
+
+ if (!dest)
+ return NULL;
+
+ do {
+ min = NULL;
+
+ for (path = bgp_dest_get_bgp_path_info(dest); path;
+ path = path->next) {
+ if (path->peer->su.sin.sin_family == AF_INET &&
+ ntohl(paddr.s_addr) <
+ ntohl(path->peer->su.sin.sin_addr
+ .s_addr)) {
+ if (min) {
+ if (ntohl(path->peer->su.sin
+ .sin_addr
+ .s_addr) <
+ ntohl(min->peer->su.sin
+ .sin_addr
+ .s_addr))
+ min = path;
+ } else
+ min = path;
+ }
+ }
+
+ if (min) {
+ const struct prefix *rn_p =
+ bgp_dest_get_prefix(dest);
+
+ *length =
+ v->namelen + BGP_PATHATTR_ENTRY_OFFSET;
+
+ offset = name + v->namelen;
+ oid_copy_in_addr(offset, &rn_p->u.prefix4);
+ offset += IN_ADDR_SIZE;
+ *offset = rn_p->prefixlen;
+ offset++;
+ oid_copy_in_addr(offset,
+ &min->peer->su.sin.sin_addr);
+ addr->prefix = rn_p->u.prefix4;
+ addr->prefixlen = rn_p->prefixlen;
+
+ bgp_dest_unlock_node(dest);
+
+ return min;
+ }
+
+ paddr.s_addr = INADDR_ANY;
+ } while ((dest = bgp_route_next(dest)) != NULL);
+ }
+ return NULL;
+}
+
+static uint8_t *bgp4PathAttrTable(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct bgp *bgp;
+ struct bgp_path_info *path;
+ struct prefix_ipv4 addr;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return NULL;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method) ==
+ MATCH_FAILED)
+ return NULL;
+ memset(&addr, 0, sizeof(addr));
+
+ path = bgp4PathAttrLookup(v, name, length, bgp, &addr, exact);
+ if (!path)
+ return NULL;
+
+ switch (v->magic) {
+ case BGP4PATHATTRPEER: /* 1 */
+ return SNMP_IPADDRESS(path->peer->su.sin.sin_addr);
+ case BGP4PATHATTRIPADDRPREFIXLEN: /* 2 */
+ return SNMP_INTEGER(addr.prefixlen);
+ case BGP4PATHATTRIPADDRPREFIX: /* 3 */
+ return SNMP_IPADDRESS(addr.prefix);
+ case BGP4PATHATTRORIGIN: /* 4 */
+ return SNMP_INTEGER(path->attr->origin);
+ case BGP4PATHATTRASPATHSEGMENT: /* 5 */
+ return aspath_snmp_pathseg(path->attr->aspath, var_len);
+ case BGP4PATHATTRNEXTHOP: /* 6 */
+ return SNMP_IPADDRESS(path->attr->nexthop);
+ case BGP4PATHATTRMULTIEXITDISC: /* 7 */
+ return SNMP_INTEGER(path->attr->med);
+ case BGP4PATHATTRLOCALPREF: /* 8 */
+ return SNMP_INTEGER(path->attr->local_pref);
+ case BGP4PATHATTRATOMICAGGREGATE: /* 9 */
+ return SNMP_INTEGER(1);
+ case BGP4PATHATTRAGGREGATORAS: /* 10 */
+ return SNMP_INTEGER(path->attr->aggregator_as);
+ case BGP4PATHATTRAGGREGATORADDR: /* 11 */
+ return SNMP_IPADDRESS(path->attr->aggregator_addr);
+ case BGP4PATHATTRCALCLOCALPREF: /* 12 */
+ return SNMP_INTEGER(-1);
+ case BGP4PATHATTRBEST: /* 13 */
+#define BGP4_PathAttrBest_false 1
+#define BGP4_PathAttrBest_true 2
+ if (CHECK_FLAG(path->flags, BGP_PATH_SELECTED))
+ return SNMP_INTEGER(BGP4_PathAttrBest_true);
+ else
+ return SNMP_INTEGER(BGP4_PathAttrBest_false);
+ case BGP4PATHATTRUNKNOWN: /* 14 */
+ *var_len = 0;
+ return NULL;
+ }
+ return NULL;
+}
+
+/* BGP Traps. */
+static struct trap_object bgpTrapList[] = {{3, {3, 1, BGPPEERREMOTEADDR}},
+ {3, {3, 1, BGPPEERLASTERROR}},
+ {3, {3, 1, BGPPEERSTATE}}};
+
+static struct variable bgp_variables[] = {
+ /* BGP version. */
+ {BGPVERSION, OCTET_STRING, RONLY, bgpVersion, 1, {1}},
+ /* BGP local AS. */
+ {BGPLOCALAS, INTEGER, RONLY, bgpLocalAs, 1, {2}},
+ /* BGP peer table. */
+ {BGPPEERIDENTIFIER, IPADDRESS, RONLY, bgpPeerTable, 3, {3, 1, 1}},
+ {BGPPEERSTATE, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 2}},
+ {BGPPEERADMINSTATUS, INTEGER, RWRITE, bgpPeerTable, 3, {3, 1, 3}},
+ {BGPPEERNEGOTIATEDVERSION,
+ INTEGER32,
+ RONLY,
+ bgpPeerTable,
+ 3,
+ {3, 1, 4}},
+ {BGPPEERLOCALADDR, IPADDRESS, RONLY, bgpPeerTable, 3, {3, 1, 5}},
+ {BGPPEERLOCALPORT, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 6}},
+ {BGPPEERREMOTEADDR, IPADDRESS, RONLY, bgpPeerTable, 3, {3, 1, 7}},
+ {BGPPEERREMOTEPORT, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 8}},
+ {BGPPEERREMOTEAS, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 9}},
+ {BGPPEERINUPDATES, COUNTER32, RONLY, bgpPeerTable, 3, {3, 1, 10}},
+ {BGPPEEROUTUPDATES, COUNTER32, RONLY, bgpPeerTable, 3, {3, 1, 11}},
+ {BGPPEERINTOTALMESSAGES, COUNTER32, RONLY, bgpPeerTable, 3, {3, 1, 12}},
+ {BGPPEEROUTTOTALMESSAGES,
+ COUNTER32,
+ RONLY,
+ bgpPeerTable,
+ 3,
+ {3, 1, 13}},
+ {BGPPEERLASTERROR, OCTET_STRING, RONLY, bgpPeerTable, 3, {3, 1, 14}},
+ {BGPPEERFSMESTABLISHEDTRANSITIONS,
+ COUNTER32,
+ RONLY,
+ bgpPeerTable,
+ 3,
+ {3, 1, 15}},
+ {BGPPEERFSMESTABLISHEDTIME,
+ GAUGE32,
+ RONLY,
+ bgpPeerTable,
+ 3,
+ {3, 1, 16}},
+ {BGPPEERCONNECTRETRYINTERVAL,
+ INTEGER,
+ RWRITE,
+ bgpPeerTable,
+ 3,
+ {3, 1, 17}},
+ {BGPPEERHOLDTIME, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 18}},
+ {BGPPEERKEEPALIVE, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 19}},
+ {BGPPEERHOLDTIMECONFIGURED,
+ INTEGER,
+ RWRITE,
+ bgpPeerTable,
+ 3,
+ {3, 1, 20}},
+ {BGPPEERKEEPALIVECONFIGURED,
+ INTEGER,
+ RWRITE,
+ bgpPeerTable,
+ 3,
+ {3, 1, 21}},
+ {BGPPEERMINROUTEADVERTISEMENTINTERVAL,
+ INTEGER,
+ RWRITE,
+ bgpPeerTable,
+ 3,
+ {3, 1, 23}},
+ {BGPPEERINUPDATEELAPSEDTIME,
+ GAUGE32,
+ RONLY,
+ bgpPeerTable,
+ 3,
+ {3, 1, 24}},
+ /* BGP identifier. */
+ {BGPIDENTIFIER, IPADDRESS, RONLY, bgpIdentifier, 1, {4}},
+ /* BGP received path attribute table. */
+ {BGPPATHATTRPEER, IPADDRESS, RONLY, bgpRcvdPathAttrTable, 3, {5, 1, 1}},
+ {BGPPATHATTRDESTNETWORK,
+ IPADDRESS,
+ RONLY,
+ bgpRcvdPathAttrTable,
+ 3,
+ {5, 1, 2}},
+ {BGPPATHATTRORIGIN, INTEGER, RONLY, bgpRcvdPathAttrTable, 3, {5, 1, 3}},
+ {BGPPATHATTRASPATH,
+ OCTET_STRING,
+ RONLY,
+ bgpRcvdPathAttrTable,
+ 3,
+ {5, 1, 4}},
+ {BGPPATHATTRNEXTHOP,
+ IPADDRESS,
+ RONLY,
+ bgpRcvdPathAttrTable,
+ 3,
+ {5, 1, 5}},
+ {BGPPATHATTRINTERASMETRIC,
+ INTEGER32,
+ RONLY,
+ bgpRcvdPathAttrTable,
+ 3,
+ {5, 1, 6}},
+ /* BGP-4 received path attribute table. */
+ {BGP4PATHATTRPEER, IPADDRESS, RONLY, bgp4PathAttrTable, 3, {6, 1, 1}},
+ {BGP4PATHATTRIPADDRPREFIXLEN,
+ INTEGER,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 2}},
+ {BGP4PATHATTRIPADDRPREFIX,
+ IPADDRESS,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 3}},
+ {BGP4PATHATTRORIGIN, INTEGER, RONLY, bgp4PathAttrTable, 3, {6, 1, 4}},
+ {BGP4PATHATTRASPATHSEGMENT,
+ OCTET_STRING,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 5}},
+ {BGP4PATHATTRNEXTHOP,
+ IPADDRESS,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 6}},
+ {BGP4PATHATTRMULTIEXITDISC,
+ INTEGER,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 7}},
+ {BGP4PATHATTRLOCALPREF,
+ INTEGER,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 8}},
+ {BGP4PATHATTRATOMICAGGREGATE,
+ INTEGER,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 9}},
+ {BGP4PATHATTRAGGREGATORAS,
+ INTEGER,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 10}},
+ {BGP4PATHATTRAGGREGATORADDR,
+ IPADDRESS,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 11}},
+ {BGP4PATHATTRCALCLOCALPREF,
+ INTEGER,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 12}},
+ {BGP4PATHATTRBEST, INTEGER, RONLY, bgp4PathAttrTable, 3, {6, 1, 13}},
+ {BGP4PATHATTRUNKNOWN,
+ OCTET_STRING,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 14}},
+};
+
+int bgpTrapEstablished(struct peer *peer)
+{
+ int ret;
+ struct in_addr addr;
+ oid index[sizeof(oid) * IN_ADDR_SIZE];
+
+ /* Check if this peer just went to Established */
+ if ((peer->ostatus != OpenConfirm) || !(peer_established(peer)))
+ return 0;
+
+ ret = inet_aton(peer->host, &addr);
+ if (ret == 0)
+ return 0;
+
+ oid_copy_in_addr(index, &addr);
+
+ smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid,
+ array_size(bgp_trap_oid), bgp_oid,
+ sizeof(bgp_oid) / sizeof(oid), index, IN_ADDR_SIZE,
+ bgpTrapList, array_size(bgpTrapList), BGPESTABLISHED);
+ return 0;
+}
+
+int bgpTrapBackwardTransition(struct peer *peer)
+{
+ int ret;
+ struct in_addr addr;
+ oid index[sizeof(oid) * IN_ADDR_SIZE];
+
+ ret = inet_aton(peer->host, &addr);
+ if (ret == 0)
+ return 0;
+
+ oid_copy_in_addr(index, &addr);
+
+ smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid,
+ array_size(bgp_trap_oid), bgp_oid,
+ sizeof(bgp_oid) / sizeof(oid), index, IN_ADDR_SIZE,
+ bgpTrapList, array_size(bgpTrapList), BGPBACKWARDTRANSITION);
+ return 0;
+}
+
+int bgp_snmp_bgp4_init(struct thread_master *tm)
+{
+ REGISTER_MIB("mibII/bgp", bgp_variables, variable, bgp_oid);
+ return 0;
+}
diff --git a/bgpd/bgp_snmp_bgp4.h b/bgpd/bgp_snmp_bgp4.h
new file mode 100644
index 0000000000..ac51191660
--- /dev/null
+++ b/bgpd/bgp_snmp_bgp4.h
@@ -0,0 +1,90 @@
+/* BGP4-MIB SNMP support
+ *
+ * Using: http://www.circitor.fr/Mibs/Html/B/BGP4-MIB.php
+ *
+ * Copyright (C) 2022 Donatas Abraitis <donatas@opensourcerouting.org>
+ *
+ * This file is part of FRRouting (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_SNMP_BGP4_H_
+#define _FRR_BGP_SNMP_BGP4_H_
+
+#define BGPVERSION 0
+#define BGPLOCALAS 0
+#define BGPIDENTIFIER 0
+
+/* bgp */
+#define BGP4MIB 1, 3, 6, 1, 2, 1, 15
+
+/* bgpTraps */
+#define BGPESTABLISHED 1
+#define BGPBACKWARDTRANSITION 2
+
+/* bgpPeerTable */
+#define BGPPEERIDENTIFIER 1
+#define BGPPEERSTATE 2
+#define BGPPEERADMINSTATUS 3
+#define BGPPEERNEGOTIATEDVERSION 4
+#define BGPPEERLOCALADDR 5
+#define BGPPEERLOCALPORT 6
+#define BGPPEERREMOTEADDR 7
+#define BGPPEERREMOTEPORT 8
+#define BGPPEERREMOTEAS 9
+#define BGPPEERINUPDATES 10
+#define BGPPEEROUTUPDATES 11
+#define BGPPEERINTOTALMESSAGES 12
+#define BGPPEEROUTTOTALMESSAGES 13
+#define BGPPEERLASTERROR 14
+#define BGPPEERFSMESTABLISHEDTRANSITIONS 15
+#define BGPPEERFSMESTABLISHEDTIME 16
+#define BGPPEERCONNECTRETRYINTERVAL 17
+#define BGPPEERHOLDTIME 18
+#define BGPPEERKEEPALIVE 19
+#define BGPPEERHOLDTIMECONFIGURED 20
+#define BGPPEERKEEPALIVECONFIGURED 21
+#define BGPPEERMINROUTEADVERTISEMENTINTERVAL 22
+#define BGPPEERINUPDATEELAPSEDTIME 23
+
+/* bgpPathAttrEntry */
+#define BGPPATHATTRPEER 1
+#define BGPPATHATTRDESTNETWORK 2
+#define BGPPATHATTRORIGIN 3
+#define BGPPATHATTRASPATH 4
+#define BGPPATHATTRNEXTHOP 5
+#define BGPPATHATTRINTERASMETRIC 6
+
+/* bgp4PathAttrEntry */
+#define BGP4PATHATTRPEER 1
+#define BGP4PATHATTRIPADDRPREFIXLEN 2
+#define BGP4PATHATTRIPADDRPREFIX 3
+#define BGP4PATHATTRORIGIN 4
+#define BGP4PATHATTRASPATHSEGMENT 5
+#define BGP4PATHATTRNEXTHOP 6
+#define BGP4PATHATTRMULTIEXITDISC 7
+#define BGP4PATHATTRLOCALPREF 8
+#define BGP4PATHATTRATOMICAGGREGATE 9
+#define BGP4PATHATTRAGGREGATORAS 10
+#define BGP4PATHATTRAGGREGATORADDR 11
+#define BGP4PATHATTRCALCLOCALPREF 12
+#define BGP4PATHATTRBEST 13
+#define BGP4PATHATTRUNKNOWN 14
+
+extern int bgpTrapEstablished(struct peer *peer);
+extern int bgpTrapBackwardTransition(struct peer *peer);
+extern int bgp_snmp_bgp4_init(struct thread_master *tm);
+
+#endif /* _FRR_BGP_SNMP_BGP4_H_ */
diff --git a/bgpd/bgp_snmp_bgp4v2.c b/bgpd/bgp_snmp_bgp4v2.c
new file mode 100644
index 0000000000..d59e63d7e0
--- /dev/null
+++ b/bgpd/bgp_snmp_bgp4v2.c
@@ -0,0 +1,455 @@
+/* BGP4V2-MIB SNMP support
+ *
+ * Copyright (C) 2022 Donatas Abraitis <donatas@opensourcerouting.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "thread.h"
+#include "smux.h"
+#include "filter.h"
+#include "hook.h"
+#include "libfrr.h"
+#include "lib/version.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_snmp.h"
+#include "bgpd/bgp_snmp_bgp4v2.h"
+
+SNMP_LOCAL_VARIABLES
+
+static oid bgpv2_oid[] = {BGP4V2MIB};
+static struct in_addr bgp_empty_addr = {};
+
+static struct peer *peer_lookup_all_vrf(struct ipaddr *addr)
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ struct listnode *node;
+ struct listnode *bgpnode;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, bgpnode, bgp)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ switch (sockunion_family(&peer->su)) {
+ case AF_INET:
+ if (IPV4_ADDR_SAME(&peer->su.sin.sin_addr,
+ &addr->ip._v4_addr))
+ return peer;
+ break;
+ case AF_INET6:
+ if (IPV6_ADDR_SAME(&peer->su.sin6.sin6_addr,
+ &addr->ip._v6_addr))
+ return peer;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static struct peer *peer_lookup_all_vrf_next(struct ipaddr *addr, oid *offset,
+ sa_family_t family)
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ struct peer *next_peer = NULL;
+ struct listnode *node;
+ struct listnode *bgpnode;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, bgpnode, bgp)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ sa_family_t peer_family = sockunion_family(&peer->su);
+
+ if (peer_family != family)
+ continue;
+
+ switch (sockunion_family(&peer->su)) {
+ case AF_INET:
+ oid2in_addr(offset, IN_ADDR_SIZE,
+ &addr->ip._v4_addr);
+ if (IPV4_ADDR_CMP(&peer->su.sin.sin_addr,
+ &addr->ip._v4_addr) < 0 ||
+ IPV4_ADDR_SAME(&peer->su.sin.sin_addr,
+ &addr->ip._v4_addr))
+ continue;
+
+ if (!next_peer ||
+ IPV4_ADDR_CMP(&next_peer->su.sin.sin_addr,
+ &peer->su.sin.sin_addr) > 0)
+ next_peer = peer;
+
+ break;
+ case AF_INET6:
+ oid2in6_addr(offset, &addr->ip._v6_addr);
+ if (IPV6_ADDR_CMP(&peer->su.sin6.sin6_addr,
+ &addr->ip._v6_addr) < 0 ||
+ IPV6_ADDR_SAME(&peer->su.sin6.sin6_addr,
+ &addr->ip._v6_addr))
+ continue;
+
+ if (!next_peer ||
+ IPV6_ADDR_CMP(&next_peer->su.sin6.sin6_addr,
+ &peer->su.sin6.sin6_addr) > 0)
+ next_peer = peer;
+
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (next_peer)
+ return next_peer;
+
+ return NULL;
+}
+
+static struct peer *bgpv2PeerTable_lookup(struct variable *v, oid name[],
+ size_t *length, int exact,
+ struct ipaddr *addr)
+{
+ struct peer *peer = NULL;
+ size_t namelen = v ? v->namelen : BGP4V2_PEER_ENTRY_OFFSET;
+ oid *offset = name + namelen;
+ sa_family_t family = name[namelen - 1] == 4 ? AF_INET : AF_INET6;
+
+ if (exact) {
+ if (family == AF_INET) {
+ oid2in_addr(offset, IN_ADDR_SIZE, &addr->ip._v4_addr);
+ peer = peer_lookup_all_vrf(addr);
+ return peer;
+ } else if (family == AF_INET6) {
+ oid2in6_addr(offset, &addr->ip._v6_addr);
+ return peer_lookup_all_vrf(addr);
+ }
+ } else {
+ peer = peer_lookup_all_vrf_next(addr, offset, family);
+ if (peer == NULL)
+ return NULL;
+
+ switch (sockunion_family(&peer->su)) {
+ case AF_INET:
+ oid_copy_in_addr(offset, &peer->su.sin.sin_addr);
+ *length = IN_ADDR_SIZE + namelen;
+ return peer;
+ case AF_INET6:
+ oid_copy_in6_addr(offset, &peer->su.sin6.sin6_addr);
+ *length = IN6_ADDR_SIZE + namelen;
+ return peer;
+ default:
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+static uint8_t *bgpv2PeerTable(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct peer *peer;
+ struct ipaddr addr = {};
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method) ==
+ MATCH_FAILED)
+ return NULL;
+
+ peer = bgpv2PeerTable_lookup(v, name, length, exact, &addr);
+ if (!peer)
+ return NULL;
+
+ switch (v->magic) {
+ case BGP4V2_PEER_INSTANCE:
+ return SNMP_INTEGER(peer->bgp->vrf_id);
+ case BGP4V2_PEER_LOCAL_ADDR_TYPE:
+ if (peer->su_local)
+ return SNMP_INTEGER(peer->su_local->sa.sa_family ==
+ AF_INET
+ ? AFI_IP
+ : AFI_IP6);
+ else
+ return SNMP_INTEGER(0);
+ case BGP4V2_PEER_LOCAL_ADDR:
+ if (peer->su_local)
+ if (peer->su_local->sa.sa_family == AF_INET)
+ return SNMP_IPADDRESS(
+ peer->su_local->sin.sin_addr);
+ else
+ return SNMP_IP6ADDRESS(
+ peer->su_local->sin6.sin6_addr);
+ else
+ return SNMP_IPADDRESS(bgp_empty_addr);
+ case BGP4V2_PEER_REMOTE_ADDR_TYPE:
+ if (peer->su_remote)
+ return SNMP_INTEGER(peer->su_remote->sa.sa_family ==
+ AF_INET
+ ? AFI_IP
+ : AFI_IP6);
+ else
+ return SNMP_INTEGER(0);
+ case BGP4V2_PEER_REMOTE_ADDR:
+ if (peer->su_remote)
+ if (peer->su_remote->sa.sa_family == AF_INET)
+ return SNMP_IPADDRESS(
+ peer->su_remote->sin.sin_addr);
+ else
+ return SNMP_IP6ADDRESS(
+ peer->su_remote->sin6.sin6_addr);
+ else
+ return SNMP_IPADDRESS(bgp_empty_addr);
+ case BGP4V2_PEER_LOCAL_PORT:
+ if (peer->su_local)
+ if (peer->su_local->sa.sa_family == AF_INET)
+ return SNMP_INTEGER(
+ ntohs(peer->su_local->sin.sin_port));
+ else
+ return SNMP_INTEGER(
+ ntohs(peer->su_local->sin6.sin6_port));
+ else
+ return SNMP_INTEGER(0);
+ case BGP4V2_PEER_LOCAL_AS:
+ return SNMP_INTEGER(peer->local_as);
+ case BGP4V2_PEER_LOCAL_IDENTIFIER:
+ return SNMP_IPADDRESS(peer->local_id);
+ case BGP4V2_PEER_REMOTE_PORT:
+ if (peer->su_remote)
+ if (peer->su_remote->sa.sa_family == AF_INET)
+ return SNMP_INTEGER(
+ ntohs(peer->su_remote->sin.sin_port));
+ else
+ return SNMP_INTEGER(
+ ntohs(peer->su_remote->sin6.sin6_port));
+ else
+ return SNMP_INTEGER(0);
+ case BGP4V2_PEER_REMOTE_AS:
+ return SNMP_INTEGER(peer->as);
+ case BGP4V2_PEER_REMOTE_IDENTIFIER:
+ return SNMP_IPADDRESS(peer->remote_id);
+ case BGP4V2_PEER_ADMIN_STATUS:
+#define BGP_PEER_ADMIN_STATUS_HALTED 1
+#define BGP_PEER_ADMIN_STATUS_RUNNING 2
+ if (BGP_PEER_START_SUPPRESSED(peer))
+ return SNMP_INTEGER(BGP_PEER_ADMIN_STATUS_HALTED);
+ else
+ return SNMP_INTEGER(BGP_PEER_ADMIN_STATUS_RUNNING);
+ case BGP4V2_PEER_STATE:
+ return SNMP_INTEGER(peer->status);
+ case BGP4V2_PEER_DESCRIPTION:
+ if (peer->desc)
+ return SNMP_STRING(peer->desc);
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static struct variable bgpv2_variables[] = {
+ {BGP4V2_PEER_INSTANCE,
+ ASN_UNSIGNED,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_INSTANCE, 1, 4}},
+ {BGP4V2_PEER_INSTANCE,
+ ASN_UNSIGNED,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_INSTANCE, 2, 16}},
+ {BGP4V2_PEER_LOCAL_ADDR_TYPE,
+ ASN_INTEGER,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_LOCAL_ADDR_TYPE, 1, 4}},
+ {BGP4V2_PEER_LOCAL_ADDR_TYPE,
+ ASN_INTEGER,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_LOCAL_ADDR_TYPE, 2, 16}},
+ {BGP4V2_PEER_LOCAL_ADDR,
+ ASN_OCTET_STR,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_LOCAL_ADDR, 1, 4}},
+ {BGP4V2_PEER_LOCAL_ADDR,
+ ASN_OCTET_STR,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_LOCAL_ADDR, 2, 16}},
+ {BGP4V2_PEER_REMOTE_ADDR_TYPE,
+ ASN_INTEGER,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_REMOTE_ADDR_TYPE, 1, 4}},
+ {BGP4V2_PEER_REMOTE_ADDR_TYPE,
+ ASN_INTEGER,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_REMOTE_ADDR_TYPE, 2, 16}},
+ {BGP4V2_PEER_REMOTE_ADDR,
+ ASN_OCTET_STR,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_REMOTE_ADDR, 1, 4}},
+ {BGP4V2_PEER_REMOTE_ADDR,
+ ASN_OCTET_STR,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_REMOTE_ADDR, 2, 16}},
+ {BGP4V2_PEER_LOCAL_PORT,
+ ASN_UNSIGNED,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_LOCAL_PORT, 1, 4}},
+ {BGP4V2_PEER_LOCAL_PORT,
+ ASN_UNSIGNED,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_LOCAL_PORT, 2, 16}},
+ {BGP4V2_PEER_LOCAL_AS,
+ ASN_UNSIGNED,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_LOCAL_AS, 1, 4}},
+ {BGP4V2_PEER_LOCAL_AS,
+ ASN_UNSIGNED,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_LOCAL_AS, 2, 16}},
+ {BGP4V2_PEER_LOCAL_IDENTIFIER,
+ ASN_OCTET_STR,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_LOCAL_IDENTIFIER, 1, 4}},
+ {BGP4V2_PEER_LOCAL_IDENTIFIER,
+ ASN_OCTET_STR,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_LOCAL_IDENTIFIER, 2, 16}},
+ {BGP4V2_PEER_REMOTE_PORT,
+ ASN_UNSIGNED,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_REMOTE_PORT, 1, 4}},
+ {BGP4V2_PEER_REMOTE_PORT,
+ ASN_UNSIGNED,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_REMOTE_PORT, 2, 16}},
+ {BGP4V2_PEER_REMOTE_AS,
+ ASN_UNSIGNED,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_REMOTE_AS, 1, 4}},
+ {BGP4V2_PEER_REMOTE_AS,
+ ASN_UNSIGNED,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_REMOTE_AS, 2, 16}},
+ {BGP4V2_PEER_REMOTE_IDENTIFIER,
+ ASN_OCTET_STR,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_REMOTE_IDENTIFIER, 1, 4}},
+ {BGP4V2_PEER_REMOTE_IDENTIFIER,
+ ASN_OCTET_STR,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_REMOTE_IDENTIFIER, 2, 16}},
+ {BGP4V2_PEER_ADMIN_STATUS,
+ ASN_INTEGER,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_ADMIN_STATUS, 1, 4}},
+ {BGP4V2_PEER_ADMIN_STATUS,
+ ASN_INTEGER,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_ADMIN_STATUS, 2, 16}},
+ {BGP4V2_PEER_STATE,
+ ASN_INTEGER,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_STATE, 1, 4}},
+ {BGP4V2_PEER_STATE,
+ ASN_INTEGER,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_STATE, 2, 16}},
+ {BGP4V2_PEER_DESCRIPTION,
+ ASN_OCTET_STR,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_DESCRIPTION, 1, 4}},
+ {BGP4V2_PEER_DESCRIPTION,
+ ASN_OCTET_STR,
+ RONLY,
+ bgpv2PeerTable,
+ 6,
+ {1, 2, 1, BGP4V2_PEER_DESCRIPTION, 2, 16}},
+};
+
+int bgp_snmp_bgp4v2_init(struct thread_master *tm)
+{
+ REGISTER_MIB("mibII/bgpv2", bgpv2_variables, variable, bgpv2_oid);
+ return 0;
+}
diff --git a/bgpd/bgp_snmp_bgp4v2.h b/bgpd/bgp_snmp_bgp4v2.h
new file mode 100644
index 0000000000..6980db9f8d
--- /dev/null
+++ b/bgpd/bgp_snmp_bgp4v2.h
@@ -0,0 +1,97 @@
+/* BGP4V2-MIB SNMP support
+ *
+ * Using: http://www.circitor.fr/Mibs/Html/B/BGP4V2-MIB.php
+ *
+ * Copyright (C) 2022 Donatas Abraitis <donatas@opensourcerouting.org>
+ *
+ * This file is part of FRRouting (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_SNMP_BGP4V2_H_
+#define _FRR_BGP_SNMP_BGP4V2_H_
+
+/* bgp4V2 */
+#define BGP4V2MIB 1, 3, 6, 1, 3, 5, 1
+
+/* bgp4V2PeerEntry:
+ * offset 1.3.6.1.3.5.1.1.2.1.x.(1|2).(4|16) = 13
+ */
+#define BGP4V2_PEER_ENTRY_OFFSET 13
+#define BGP4V2_PEER_INSTANCE 1
+#define BGP4V2_PEER_LOCAL_ADDR_TYPE 2
+#define BGP4V2_PEER_LOCAL_ADDR 3
+#define BGP4V2_PEER_REMOTE_ADDR_TYPE 4
+#define BGP4V2_PEER_REMOTE_ADDR 5
+#define BGP4V2_PEER_LOCAL_PORT 6
+#define BGP4V2_PEER_LOCAL_AS 7
+#define BGP4V2_PEER_LOCAL_IDENTIFIER 8
+#define BGP4V2_PEER_REMOTE_PORT 9
+#define BGP4V2_PEER_REMOTE_AS 10
+#define BGP4V2_PEER_REMOTE_IDENTIFIER 11
+#define BGP4V2_PEER_ADMIN_STATUS 12
+#define BGP4V2_PEER_STATE 13
+#define BGP4V2_PEER_DESCRIPTION 14
+
+/* bgp4V2PeerErrorsEntry */
+#define BGP4V2_PEER_LAST_ERROR_CODE_RECEIVED 1
+#define BGP4V2_PEER_LAST_ERROR_SUBCODE_RECEIVED 2
+#define BGP4V2_PEER_LAST_ERROR_RECEIVED_TIME 3
+#define BGP4V2_PEER_LAST_ERROR_RECEIVED_TEXT 4
+#define BGP4V2_PEER_LAST_ERROR_RECEIVED_DATA 5
+#define BGP4V2_PEER_LAST_ERROR_CODE_SENT 6
+#define BGP4V2_PEER_LAST_ERROR_SUBCODE_SENT 7
+#define BGP4V2_PEER_LAST_ERROR_SENT_TIME 8
+#define BGP4V2_PEER_LAST_ERROR_SENT_TEXT 9
+#define BGP4V2_PEER_LAST_ERROR_SENT_DATA 10
+
+/* bgp4V2PeerEventTimesEntry */
+#define BGP4V2_PEER_FSM_ESTABLISHED_TIME 1
+#define BGP4V2_PEER_PEER_IN_UPDATES_ELAPSED_TIME 2
+
+/* bgp4V2NlriEntry */
+#define BGP4V2_NLRI_INDEX 1
+#define BGP4V2_NLRI_AFI 2
+#define BGP4V2_NLRI_SAFI 3
+#define BGP4V2_NLRI_PREFIX_TYPE 4
+#define BGP4V2_NLRI_PREFIX 5
+#define BGP4V2_NLRI_PREFIX_LEN 6
+#define BGP4V2_NLRI_BEST 7
+#define BGP4V2_NLRI_CALC_LOCAL_PREF 8
+#define BGP4V2_NLRI_ORIGIN 9
+#define BGP4V2_NLRI_NEXT_HOP_ADDR_TYPE 10
+#define BGP4V2_NLRI_NEXT_HOP_ADDR 11
+#define BGP4V2_NLRI_LINK_LOCAL_NEXT_HOP_ADDR_TYPE 12
+#define BGP4V2_NLRI_LINK_LOCAL_NEXT_HOP_ADDR 13
+#define BGP4V2_NLRI_LOCAL_PREF_PRESENT 14
+#define BGP4V2_NLRI_LOCAL_PREF 15
+#define BGP4V2_NLRI_MED_PRESENT 16
+#define BGP4V2_NLRI_MED 17
+#define BGP4V2_NLRI_ATOMIC_AGGREGATE 18
+#define BGP4V2_NLRI_AGGREGATOR_PRESENT 19
+#define BGP4V2_NLRI_AGGREGATOR_AS 20
+#define BGP4V2_NLRI_AGGREGATOR_ADDR 21
+#define BGP4V2_NLRI_AS_PATH_CALC_LENGTH 22
+#define BGP4V2_NLRI_AS_PATH_STRING 23
+#define BGP4V2_NLRI_AS_PATH 24
+#define BGP4V2_NLRI_PATH_ATTR_UNKNOWN 25
+
+/* bgp4V2Notifications */
+#define BGP4V2_ESTABLISHED_NOTIFICATION 1
+#define BGP4V2_BACKWARD_TRANSITION_NOTIFICATION 2
+
+extern int bgp_snmp_bgp4v2_init(struct thread_master *tm);
+
+#endif /* _FRR_BGP_SNMP_BGP4V2_H_ */
diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h
index 56289d399d..ecd92a996e 100644
--- a/bgpd/bgp_updgrp.h
+++ b/bgpd/bgp_updgrp.h
@@ -88,6 +88,8 @@ typedef struct {
#define BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED (1 << 4)
#define BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED (1 << 5)
#define BPKT_ATTRVEC_FLAGS_RMAP_IPV6_LNH_CHANGED (1 << 6)
+#define BPKT_ATTRVEC_FLAGS_RMAP_VPNV4_NH_CHANGED (1 << 7)
+#define BPKT_ATTRVEC_FLAGS_RMAP_VPNV6_GNH_CHANGED (1 << 8)
typedef struct bpacket_attr_vec_arr {
bpacket_attr_vec entries[BGP_ATTR_VEC_MAX];
diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c
index 9de97cf060..b52d67ed3c 100644
--- a/bgpd/bgp_updgrp_packet.c
+++ b/bgpd/bgp_updgrp_packet.c
@@ -379,10 +379,11 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
route_map_sets_nh =
(CHECK_FLAG(vec->flags,
- BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED)
- || CHECK_FLAG(
- vec->flags,
- BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS));
+ BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED) ||
+ CHECK_FLAG(vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_VPNV4_NH_CHANGED) ||
+ CHECK_FLAG(vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS));
switch (nhlen) {
case BGP_ATTR_NHLEN_IPV4:
@@ -468,10 +469,12 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
route_map_sets_nh =
(CHECK_FLAG(vec->flags,
- BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED)
- || CHECK_FLAG(
+ BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED) ||
+ CHECK_FLAG(
vec->flags,
- BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS));
+ BPKT_ATTRVEC_FLAGS_RMAP_VPNV6_GNH_CHANGED) ||
+ CHECK_FLAG(vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS));
/*
* The logic here is rather similar to that for IPv4, the
@@ -1276,6 +1279,15 @@ bpacket_vec_arr_inherit_attr_flags(struct bpacket_attr_vec_arr *vecarr,
SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED);
+ if (CHECK_FLAG(attr->rmap_change_flags, BATTR_RMAP_VPNV4_NHOP_CHANGED))
+ SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_VPNV4_NH_CHANGED);
+
+ if (CHECK_FLAG(attr->rmap_change_flags,
+ BATTR_RMAP_VPNV6_GLOBAL_NHOP_CHANGED))
+ SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_VPNV6_GNH_CHANGED);
+
if (CHECK_FLAG(attr->rmap_change_flags,
BATTR_RMAP_IPV6_LL_NHOP_CHANGED))
SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
diff --git a/bgpd/subdir.am b/bgpd/subdir.am
index 04fe1f1249..88f53da35e 100644
--- a/bgpd/subdir.am
+++ b/bgpd/subdir.am
@@ -151,6 +151,9 @@ noinst_HEADERS += \
bgpd/bgp_route.h \
bgpd/bgp_routemap_nb.h \
bgpd/bgp_script.h \
+ bgpd/bgp_snmp.h \
+ bgpd/bgp_snmp_bgp4.h \
+ bgpd/bgp_snmp_bgp4v2.h \
bgpd/bgp_table.h \
bgpd/bgp_updgrp.h \
bgpd/bgp_vpn.h \
@@ -189,7 +192,7 @@ bgpd_bgp_btoa_SOURCES = bgpd/bgp_btoa.c
bgpd_bgpd_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBYANG_LIBS) $(LIBCAP) $(LIBM) $(UST_LIBS)
bgpd_bgp_btoa_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBYANG_LIBS) $(LIBCAP) $(LIBM) $(UST_LIBS)
-bgpd_bgpd_snmp_la_SOURCES = bgpd/bgp_snmp.c bgpd/bgp_mplsvpn_snmp.c
+bgpd_bgpd_snmp_la_SOURCES = bgpd/bgp_snmp_bgp4.c bgpd/bgp_snmp_bgp4v2.c bgpd/bgp_snmp.c bgpd/bgp_mplsvpn_snmp.c
bgpd_bgpd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
bgpd_bgpd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
bgpd_bgpd_snmp_la_LIBADD = lib/libfrrsnmp.la
diff --git a/debian/rules b/debian/rules
index fdb458e6a8..6305603341 100755
--- a/debian/rules
+++ b/debian/rules
@@ -27,10 +27,10 @@ else
CONF_LUA=--enable-scripting
endif
-ifeq ($(filter pkg.frr.pim6d,$(DEB_BUILD_PROFILES)),)
- CONF_PIM6=--disable-pim6d
-else
+ifeq ($(filter pkg.frr.nopim6d,$(DEB_BUILD_PROFILES)),)
CONF_PIM6=--enable-pim6d
+else
+ CONF_PIM6=--disable-pim6d
endif
export PYTHON=python3
diff --git a/doc/developer/packaging-debian.rst b/doc/developer/packaging-debian.rst
index 9aeb78c4fd..c2c3b7e7e1 100644
--- a/doc/developer/packaging-debian.rst
+++ b/doc/developer/packaging-debian.rst
@@ -66,7 +66,7 @@ buster.)
+----------------+-------------------+-----------------------------------------+
| pkg.frr.lua | pkg.frr.nolua | builds lua scripting extension |
+----------------+-------------------+-----------------------------------------+
- | pkg.frr.pim6d | pkg.frr.nopim6d | builds pim6d (work in progress) |
+ | pkg.frr.pim6d | pkg.frr.nopim6d | builds pim6d (default enabled) |
+----------------+-------------------+-----------------------------------------+
* the ``-uc -us`` options to disable signing the packages with your GPG key
diff --git a/doc/developer/packaging-redhat.rst b/doc/developer/packaging-redhat.rst
index 9e64b912f3..d88f449926 100644
--- a/doc/developer/packaging-redhat.rst
+++ b/doc/developer/packaging-redhat.rst
@@ -83,6 +83,7 @@ Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24.
%{!?with_watchfrr: %global with_watchfrr 1 }
%{!?with_bgp_vnc: %global with_bgp_vnc 0 }
%{!?with_pimd: %global with_pimd 1 }
+ %{!?with_pim6d: %global with_pim6d 1 }
%{!?with_rpki: %global with_rpki 0 }
8. Build the RPM::
diff --git a/include/subdir.am b/include/subdir.am
index f6328ef38e..4fa88a0afd 100644
--- a/include/subdir.am
+++ b/include/subdir.am
@@ -19,4 +19,5 @@ noinst_HEADERS += \
include/linux/seg6_local.h \
include/linux/mroute.h \
include/linux/mroute6.h \
+ include/linux/pkt_cls.h \
# end
diff --git a/lib/lib_vty.c b/lib/lib_vty.c
index 85816c5123..46a0a68103 100644
--- a/lib/lib_vty.c
+++ b/lib/lib_vty.c
@@ -225,10 +225,8 @@ static struct call_back {
} callback;
-DEFUN_HIDDEN (start_config,
- start_config_cmd,
- "XFRR_start_configuration",
- "The Beginning of Configuration\n")
+DEFUN_NOSH(start_config, start_config_cmd, "XFRR_start_configuration",
+ "The Beginning of Configuration\n")
{
callback.readin_time = monotime(NULL);
@@ -240,10 +238,8 @@ DEFUN_HIDDEN (start_config,
return CMD_SUCCESS;
}
-DEFUN_HIDDEN (end_config,
- end_config_cmd,
- "XFRR_end_configuration",
- "The End of Configuration\n")
+DEFUN_NOSH(end_config, end_config_cmd, "XFRR_end_configuration",
+ "The End of Configuration\n")
{
time_t readin_time;
char readin_time_str[MONOTIME_STRLEN];
diff --git a/lib/smux.h b/lib/smux.h
index 74447341d8..48c3374236 100644
--- a/lib/smux.h
+++ b/lib/smux.h
@@ -44,6 +44,7 @@ extern "C" {
#define SNMP_INVALID 2
#define IN_ADDR_SIZE sizeof(struct in_addr)
+#define IN6_ADDR_SIZE sizeof(struct in6_addr)
/* IANAipRouteProtocol */
#define IANAIPROUTEPROTOCOLOTHER 1
@@ -87,8 +88,9 @@ struct index_oid {
/* Declare SMUX return value. */
#define SNMP_LOCAL_VARIABLES \
static long snmp_int_val __attribute__((unused)); \
- static struct in_addr snmp_in_addr_val __attribute__((unused));
- static uint8_t snmp_octet_val __attribute__((unused));
+ static struct in_addr snmp_in_addr_val __attribute__((unused)); \
+ static uint8_t snmp_octet_val __attribute__((unused)); \
+ static char snmp_string_val[255] __attribute__((unused));
#define SNMP_INTEGER(V) \
(*var_len = sizeof(snmp_int_val), snmp_int_val = V, \
(uint8_t *)&snmp_int_val)
@@ -97,6 +99,10 @@ struct index_oid {
(*var_len = sizeof(snmp_octet_val), snmp_octet_val = V, \
(uint8_t *)&snmp_octet_val)
+#define SNMP_STRING(V) \
+ (*var_len = MIN(sizeof(snmp_string_val), strlen(V) + 1), \
+ strlcpy(snmp_string_val, V, *var_len), (uint8_t *)&snmp_string_val)
+
#define SNMP_IPADDRESS(V) \
(*var_len = sizeof(struct in_addr), snmp_in_addr_val = V, \
(uint8_t *)&snmp_in_addr_val)
diff --git a/lib/subdir.am b/lib/subdir.am
index ea6cb9339a..1a8c184823 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -123,6 +123,7 @@ lib_libfrr_la_SOURCES = \
lib/printf/glue.c \
lib/routing_nb.c \
lib/routing_nb_config.c \
+ lib/tc.c \
# end
nodist_lib_libfrr_la_SOURCES = \
@@ -275,6 +276,7 @@ pkginclude_HEADERS += \
lib/zlog_live.h \
lib/zlog_targets.h \
lib/pbr.h \
+ lib/tc.h \
lib/routing_nb.h \
\
lib/assert/assert.h \
diff --git a/lib/tc.c b/lib/tc.c
new file mode 100644
index 0000000000..1bc01ed3f6
--- /dev/null
+++ b/lib/tc.c
@@ -0,0 +1,88 @@
+/*
+ * Traffic Control (TC) main library
+ * Copyright (C) 2022 Shichu Yang
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "tc.h"
+
+int tc_getrate(const char *str, uint64_t *rate)
+{
+ char *endp;
+ uint64_t raw = strtoull(str, &endp, 10);
+
+ if (endp == str)
+ return -1;
+
+ /* if the string only contains a number, it must be valid rate (bps) */
+ bool valid = (*endp == '\0');
+
+ const char *p = endp;
+ bool bytes = false, binary_base = false;
+ int power = 0;
+
+ while (*p) {
+ if (strcmp(p, "Bps") == 0) {
+ bytes = true;
+ valid = true;
+ break;
+ } else if (strcmp(p, "bit") == 0) {
+ valid = true;
+ break;
+ }
+ switch (*p) {
+ case 'k':
+ case 'K':
+ power = 1;
+ break;
+ case 'm':
+ case 'M':
+ power = 2;
+ break;
+ case 'g':
+ case 'G':
+ power = 3;
+ break;
+ case 't':
+ case 'T':
+ power = 4;
+ break;
+ case 'i':
+ case 'I':
+ if (power != 0)
+ binary_base = true;
+ else
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ p++;
+ }
+
+ if (!valid)
+ return -1;
+
+ for (int i = 0; i < power; i++)
+ raw *= binary_base ? 1024ULL : 1000ULL;
+
+ if (bytes)
+ *rate = raw;
+ else
+ *rate = raw / 8ULL;
+
+ return 0;
+}
diff --git a/lib/tc.h b/lib/tc.h
new file mode 100644
index 0000000000..ec68e1f4cd
--- /dev/null
+++ b/lib/tc.h
@@ -0,0 +1,151 @@
+/*
+ * Traffic Control (TC) main header
+ * Copyright (C) 2022 Shichu Yang
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _TC_H
+#define _TC_H
+
+#include <zebra.h>
+#include "stream.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TC_STR "Traffic Control\n"
+
+/* qdisc definitions */
+
+/* qdisc kind (same as class kinds) */
+enum tc_qdisc_kind {
+ TC_QDISC_UNSPEC,
+ TC_QDISC_HTB,
+ TC_QDISC_NOQUEUE,
+};
+
+struct tc_qdisc_htb {
+ /* currently no members */
+};
+
+struct tc_qdisc {
+ ifindex_t ifindex;
+
+ enum tc_qdisc_kind kind;
+ union {
+ struct tc_qdisc_htb htb;
+ } u;
+};
+
+/* class definitions */
+
+/* since classes share the same kinds of qdisc, duplicates omitted */
+struct tc_class_htb {
+ uint64_t rate;
+ uint64_t ceil;
+};
+
+struct tc_class {
+ ifindex_t ifindex;
+ uint32_t handle;
+
+ enum tc_qdisc_kind kind;
+ union {
+ struct tc_class_htb htb;
+ } u;
+};
+
+/* filter definitions */
+
+/* filter kinds */
+enum tc_filter_kind {
+ TC_FILTER_UNSPEC,
+ TC_FILTER_BPF,
+ TC_FILTER_FLOW,
+ TC_FILTER_FLOWER,
+ TC_FILTER_U32,
+};
+
+struct tc_bpf {
+ /* TODO: fill in */
+};
+
+struct tc_flow {
+ /* TODO: fill in */
+};
+
+struct tc_flower {
+ uint32_t classid;
+
+#define TC_FLOWER_IP_PROTOCOL (1 << 0)
+#define TC_FLOWER_SRC_IP (1 << 1)
+#define TC_FLOWER_DST_IP (1 << 2)
+#define TC_FLOWER_SRC_PORT (1 << 3)
+#define TC_FLOWER_DST_PORT (1 << 4)
+#define TC_FLOWER_DSFIELD (1 << 5)
+
+ uint32_t filter_bm;
+
+ uint8_t ip_proto;
+
+ struct prefix src_ip;
+ struct prefix dst_ip;
+
+ uint16_t src_port_min;
+ uint16_t src_port_max;
+ uint16_t dst_port_min;
+ uint16_t dst_port_max;
+
+ uint8_t dsfield;
+ uint8_t dsfield_mask;
+};
+
+struct tc_u32 {
+ /* TODO: fill in */
+};
+
+struct tc_filter {
+ ifindex_t ifindex;
+ uint32_t handle;
+
+ uint32_t priority;
+ uint16_t protocol;
+
+ enum tc_filter_kind kind;
+
+ union {
+ struct tc_bpf bpf;
+ struct tc_flow flow;
+ struct tc_flower flower;
+ struct tc_u32 u32;
+ } u;
+};
+
+extern int tc_getrate(const char *str, uint64_t *rate);
+
+extern int zapi_tc_qdisc_encode(uint8_t cmd, struct stream *s,
+ struct tc_qdisc *qdisc);
+extern int zapi_tc_class_encode(uint8_t cmd, struct stream *s,
+ struct tc_class *class);
+extern int zapi_tc_filter_encode(uint8_t cmd, struct stream *s,
+ struct tc_filter *filter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TC_H */
diff --git a/lib/zclient.c b/lib/zclient.c
index fd6eb7db0d..07c7e5aea8 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -37,6 +37,7 @@
#include "mpls.h"
#include "sockopt.h"
#include "pbr.h"
+#include "tc.h"
#include "nexthop_group.h"
#include "lib_errors.h"
#include "srte.h"
@@ -1649,6 +1650,96 @@ int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule)
return 0;
}
+int zapi_tc_qdisc_encode(uint8_t cmd, struct stream *s, struct tc_qdisc *qdisc)
+{
+ stream_reset(s);
+ zclient_create_header(s, cmd, VRF_DEFAULT);
+
+
+ stream_putl(s, 1);
+
+ stream_putl(s, qdisc->ifindex);
+ stream_putl(s, qdisc->kind);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return 0;
+}
+
+int zapi_tc_class_encode(uint8_t cmd, struct stream *s, struct tc_class *class)
+{
+ stream_reset(s);
+ zclient_create_header(s, cmd, VRF_DEFAULT);
+
+ stream_putl(s, 1);
+
+ stream_putl(s, class->ifindex);
+ stream_putl(s, class->handle);
+ stream_putl(s, class->kind);
+
+ switch (class->kind) {
+ case TC_QDISC_HTB:
+ stream_putq(s, class->u.htb.rate);
+ stream_putq(s, class->u.htb.ceil);
+ break;
+ default:
+ /* not implemented */
+ break;
+ }
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return 0;
+}
+
+int zapi_tc_filter_encode(uint8_t cmd, struct stream *s,
+ struct tc_filter *filter)
+{
+ stream_reset(s);
+ zclient_create_header(s, cmd, VRF_DEFAULT);
+
+ stream_putl(s, 1);
+
+ stream_putl(s, filter->ifindex);
+ stream_putl(s, filter->handle);
+ stream_putl(s, filter->priority);
+ stream_putl(s, filter->protocol);
+ stream_putl(s, filter->kind);
+
+ switch (filter->kind) {
+ case TC_FILTER_FLOWER:
+ stream_putl(s, filter->u.flower.filter_bm);
+ if (filter->u.flower.filter_bm & TC_FLOWER_IP_PROTOCOL)
+ stream_putc(s, filter->u.flower.ip_proto);
+ if (filter->u.flower.filter_bm & TC_FLOWER_SRC_IP)
+ zapi_encode_prefix(s, &filter->u.flower.src_ip,
+ filter->u.flower.src_ip.family);
+ if (filter->u.flower.filter_bm & TC_FLOWER_SRC_PORT) {
+ stream_putw(s, filter->u.flower.src_port_min);
+ stream_putw(s, filter->u.flower.src_port_max);
+ }
+ if (filter->u.flower.filter_bm & TC_FLOWER_DST_IP)
+ zapi_encode_prefix(s, &filter->u.flower.dst_ip,
+ filter->u.flower.dst_ip.family);
+ if (filter->u.flower.filter_bm & TC_FLOWER_DST_PORT) {
+ stream_putw(s, filter->u.flower.dst_port_min);
+ stream_putw(s, filter->u.flower.dst_port_max);
+ }
+ if (filter->u.flower.filter_bm & TC_FLOWER_DSFIELD) {
+ stream_putc(s, filter->u.flower.dsfield);
+ stream_putc(s, filter->u.flower.dsfield_mask);
+ }
+ stream_putl(s, filter->u.flower.classid);
+ break;
+ default:
+ /* not implemented */
+ break;
+ }
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return 0;
+}
+
bool zapi_nhg_notify_decode(struct stream *s, uint32_t *id,
enum zapi_nhg_notify_owner *note)
{
diff --git a/lib/zclient.h b/lib/zclient.h
index 731769abf7..584a42194d 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -243,6 +243,12 @@ typedef enum {
ZEBRA_GRE_GET,
ZEBRA_GRE_UPDATE,
ZEBRA_GRE_SOURCE_SET,
+ ZEBRA_TC_QDISC_INSTALL,
+ ZEBRA_TC_QDISC_UNINSTALL,
+ ZEBRA_TC_CLASS_ADD,
+ ZEBRA_TC_CLASS_DELETE,
+ ZEBRA_TC_FILTER_ADD,
+ ZEBRA_TC_FILTER_DELETE,
} zebra_message_types_t;
enum zebra_error_types {
diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in
index afe75e1f26..71e62bf769 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -24,7 +24,7 @@
%{!?with_pam: %global with_pam 0 }
%{!?with_pbrd: %global with_pbrd 1 }
%{!?with_pimd: %global with_pimd 1 }
-%{!?with_pim6d: %global with_pim6d 0 }
+%{!?with_pim6d: %global with_pim6d 1 }
%{!?with_vrrpd: %global with_vrrpd 1 }
%{!?with_rtadv: %global with_rtadv 1 }
%{!?with_watchfrr: %global with_watchfrr 1 }
diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c
index 164a0fd218..0cedd2bafc 100644
--- a/sharpd/sharp_vty.c
+++ b/sharpd/sharp_vty.c
@@ -32,6 +32,7 @@
#include "linklist.h"
#include "link_state.h"
#include "cspf.h"
+#include "tc.h"
#include "sharpd/sharp_globals.h"
#include "sharpd/sharp_zebra.h"
@@ -1339,6 +1340,64 @@ DEFPY (no_sharp_interface_protodown,
return CMD_SUCCESS;
}
+DEFPY (tc_filter_rate,
+ tc_filter_rate_cmd,
+ "sharp tc dev IFNAME$ifname \
+ source <A.B.C.D/M|X:X::X:X/M>$src \
+ destination <A.B.C.D/M|X:X::X:X/M>$dst \
+ ip-protocol <tcp|udp>$ip_proto \
+ src-port (1-65535)$src_port \
+ dst-port (1-65535)$dst_port \
+ rate RATE$ratestr",
+ SHARP_STR
+ "Traffic control\n"
+ "TC interface (for qdisc, class, filter)\n"
+ "TC interface name\n"
+ "TC filter source\n"
+ "TC filter source IPv4 prefix\n"
+ "TC filter source IPv6 prefix\n"
+ "TC filter destination\n"
+ "TC filter destination IPv4 prefix\n"
+ "TC filter destination IPv6 prefix\n"
+ "TC filter IP protocol\n"
+ "TC filter IP protocol TCP\n"
+ "TC filter IP protocol UDP\n"
+ "TC filter source port\n"
+ "TC filter source port\n"
+ "TC filter destination port\n"
+ "TC filter destination port\n"
+ "TC rate\n"
+ "TC rate number (bits/s) or rate string (suffixed with Bps or bit)\n")
+{
+ struct interface *ifp;
+ struct protoent *p;
+ uint64_t rate;
+
+ ifp = if_lookup_vrf_all(ifname);
+
+ if (!ifp) {
+ vty_out(vty, "%% Can't find interface %s\n", ifname);
+ return CMD_WARNING;
+ }
+
+ p = getprotobyname(ip_proto);
+ if (!p) {
+ vty_out(vty, "Unable to convert %s to proto id\n", ip_proto);
+ return CMD_WARNING;
+ }
+
+ if (tc_getrate(ratestr, &rate) != 0) {
+ vty_out(vty, "Unable to convert %s to rate\n", ratestr);
+ return CMD_WARNING;
+ }
+
+ if (sharp_zebra_send_tc_filter_rate(ifp, src, dst, p->p_proto, src_port,
+ dst_port, rate) != 0)
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
void sharp_vty_init(void)
{
install_element(ENABLE_NODE, &install_routes_data_dump_cmd);
@@ -1374,5 +1433,7 @@ void sharp_vty_init(void)
install_element(ENABLE_NODE, &sharp_interface_protodown_cmd);
install_element(ENABLE_NODE, &no_sharp_interface_protodown_cmd);
+ install_element(ENABLE_NODE, &tc_filter_rate_cmd);
+
return;
}
diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c
index 9d343576d6..bf5b98544e 100644
--- a/sharpd/sharp_zebra.c
+++ b/sharpd/sharp_zebra.c
@@ -31,6 +31,7 @@
#include "nexthop.h"
#include "nexthop_group.h"
#include "link_state.h"
+#include "tc.h"
#include "sharp_globals.h"
#include "sharp_nht.h"
@@ -302,8 +303,8 @@ static bool route_add(const struct prefix *p, vrf_id_t vrf_id, uint8_t instance,
memcpy(api.opaque.data, opaque, api.opaque.length);
}
- if (zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api)
- == ZCLIENT_SEND_BUFFERED)
+ if (zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api) ==
+ ZCLIENT_SEND_BUFFERED)
return true;
else
return false;
@@ -326,8 +327,8 @@ static bool route_delete(struct prefix *p, vrf_id_t vrf_id, uint8_t instance)
api.instance = instance;
memcpy(&api.prefix, p, sizeof(*p));
- if (zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api)
- == ZCLIENT_SEND_BUFFERED)
+ if (zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api) ==
+ ZCLIENT_SEND_BUFFERED)
return true;
else
return false;
@@ -360,7 +361,7 @@ static void sharp_install_routes_restart(struct prefix *p, uint32_t count,
if (buffered) {
wb.p = *p;
- wb.count = i+1;
+ wb.count = i + 1;
wb.routes = routes;
wb.vrf_id = vrf_id;
wb.instance = instance;
@@ -447,17 +448,16 @@ static void handle_repeated(bool installed)
if (installed) {
sg.r.removed_routes = 0;
- sharp_remove_routes_helper(&p, sg.r.vrf_id,
- sg.r.inst, sg.r.total_routes);
+ sharp_remove_routes_helper(&p, sg.r.vrf_id, sg.r.inst,
+ sg.r.total_routes);
}
if (!installed) {
sg.r.installed_routes = 0;
- sharp_install_routes_helper(&p, sg.r.vrf_id, sg.r.inst,
- sg.r.nhgid, &sg.r.nhop_group,
- &sg.r.backup_nhop_group,
- sg.r.total_routes, sg.r.flags,
- sg.r.opaque);
+ sharp_install_routes_helper(
+ &p, sg.r.vrf_id, sg.r.inst, sg.r.nhgid,
+ &sg.r.nhop_group, &sg.r.backup_nhop_group,
+ sg.r.total_routes, sg.r.flags, sg.r.opaque);
}
}
@@ -467,8 +467,7 @@ static void sharp_zclient_buffer_ready(void)
case SHARP_INSTALL_ROUTES_RESTART:
sharp_install_routes_restart(
&wb.p, wb.count, wb.vrf_id, wb.instance, wb.nhgid,
- wb.nhg, wb.backup_nhg, wb.routes, wb.flags,
- wb.opaque);
+ wb.nhg, wb.backup_nhg, wb.routes, wb.flags, wb.opaque);
return;
case SHARP_DELETE_ROUTES_RESTART:
sharp_remove_routes_restart(&wb.p, wb.count, wb.vrf_id,
@@ -484,8 +483,8 @@ static int route_notify_owner(ZAPI_CALLBACK_ARGS)
enum zapi_route_notify_owner note;
uint32_t table;
- if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, &note,
- NULL, NULL))
+ if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, &note, NULL,
+ NULL))
return -1;
switch (note) {
@@ -574,8 +573,8 @@ void nhg_add(uint32_t id, const struct nexthop_group *nhg,
}
if (api_nhg.nexthop_num == 0) {
- zlog_debug("%s: nhg %u not sent: no valid nexthops",
- __func__, id);
+ zlog_debug("%s: nhg %u not sent: no valid nexthops", __func__,
+ id);
is_valid = false;
goto done;
}
@@ -635,8 +634,7 @@ void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import,
command = ZEBRA_NEXTHOP_UNREGISTER;
if (zclient_send_rnh(zclient, command, p, SAFI_UNICAST, connected,
- false, vrf_id)
- == ZCLIENT_SEND_FAILURE)
+ false, vrf_id) == ZCLIENT_SEND_FAILURE)
zlog_warn("%s: Failure to send nexthop to zebra", __func__);
}
@@ -645,8 +643,7 @@ static int sharp_debug_nexthops(struct zapi_route *api)
int i;
if (api->nexthop_num == 0) {
- zlog_debug(
- " Not installed");
+ zlog_debug(" Not installed");
return 0;
}
@@ -711,8 +708,8 @@ static int sharp_redistribute_route(ZAPI_CALLBACK_ARGS)
zlog_warn("%s: Decode of redistribute failed: %d", __func__,
ZEBRA_REDISTRIBUTE_ROUTE_ADD);
- zlog_debug("%s: %pFX (%s)", zserv_command_string(cmd),
- &api.prefix, zebra_route_string(api.type));
+ zlog_debug("%s: %pFX (%s)", zserv_command_string(cmd), &api.prefix,
+ zebra_route_string(api.type));
sharp_debug_nexthops(&api);
@@ -784,9 +781,9 @@ int sharp_zclient_delete(uint32_t session_id)
return 0;
}
-static const char *const type2txt[] = { "Generic", "Vertex", "Edge", "Subnet" };
-static const char *const status2txt[] = { "Unknown", "New", "Update",
- "Delete", "Sync", "Orphan"};
+static const char *const type2txt[] = {"Generic", "Vertex", "Edge", "Subnet"};
+static const char *const status2txt[] = {"Unknown", "New", "Update",
+ "Delete", "Sync", "Orphan"};
/* Handler for opaque messages */
static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS)
{
@@ -809,8 +806,7 @@ static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS)
status2txt[lse->status],
type2txt[lse->type]);
lse->status = SYNC;
- }
- else
+ } else
zlog_debug(
"%s: Error to convert Stream into Link State",
__func__);
@@ -847,12 +843,11 @@ void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance,
instance, session_id,
buf, sizeof(buf));
if (ret == ZCLIENT_SEND_FAILURE) {
- zlog_debug("%s: send_opaque() failed => %d",
- __func__, ret);
+ zlog_debug("%s: send_opaque() failed => %d", __func__,
+ ret);
break;
}
}
-
}
/*
@@ -883,7 +878,6 @@ void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance,
stream_putw_at(s, 0, stream_get_endp(s));
(void)zclient_send_message(zclient);
-
}
/* Link State registration */
@@ -985,6 +979,64 @@ int sharp_zebra_send_interface_protodown(struct interface *ifp, bool down)
return 0;
}
+int sharp_zebra_send_tc_filter_rate(struct interface *ifp,
+ const struct prefix *source,
+ const struct prefix *destination,
+ uint8_t ip_proto, uint16_t src_port,
+ uint16_t dst_port, uint64_t rate)
+{
+#define SHARPD_TC_HANDLE 0x0001
+ struct stream *s;
+
+ s = zclient->obuf;
+
+ struct tc_qdisc q = {.ifindex = ifp->ifindex, .kind = TC_QDISC_HTB};
+
+ zapi_tc_qdisc_encode(ZEBRA_TC_QDISC_INSTALL, s, &q);
+ if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ struct tc_class c = {.ifindex = ifp->ifindex,
+ .handle = SHARPD_TC_HANDLE & 0xffff,
+ .kind = TC_QDISC_HTB,
+ .u.htb.ceil = rate,
+ .u.htb.rate = rate};
+
+ zapi_tc_class_encode(ZEBRA_TC_CLASS_ADD, s, &c);
+ if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ struct tc_filter f = {.ifindex = ifp->ifindex,
+ .handle = SHARPD_TC_HANDLE,
+ .priority = 0x1,
+ .kind = TC_FILTER_FLOWER,
+ .u.flower.filter_bm = 0};
+
+#ifdef ETH_P_IP
+ f.protocol = ETH_P_IP;
+#else
+ f.protocol = 0x0800;
+#endif
+
+ f.u.flower.filter_bm |= TC_FLOWER_IP_PROTOCOL;
+ f.u.flower.ip_proto = ip_proto;
+ f.u.flower.filter_bm |= TC_FLOWER_SRC_IP;
+ prefix_copy(&f.u.flower.src_ip, source);
+ f.u.flower.filter_bm |= TC_FLOWER_DST_IP;
+ prefix_copy(&f.u.flower.dst_ip, destination);
+ f.u.flower.filter_bm |= TC_FLOWER_SRC_PORT;
+ f.u.flower.src_port_min = f.u.flower.src_port_max = src_port;
+ f.u.flower.filter_bm |= TC_FLOWER_DST_PORT;
+ f.u.flower.dst_port_min = f.u.flower.dst_port_max = dst_port;
+ f.u.flower.classid = SHARPD_TC_HANDLE & 0xffff;
+
+ zapi_tc_filter_encode(ZEBRA_TC_FILTER_ADD, s, &f);
+ if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ return 0;
+}
+
static zclient_handler *const sharp_handlers[] = {
[ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add,
[ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete,
@@ -1002,8 +1054,8 @@ void sharp_zebra_init(void)
{
struct zclient_options opt = {.receive_notify = true};
- if_zapi_callbacks(sharp_ifp_create, sharp_ifp_up,
- sharp_ifp_down, sharp_ifp_destroy);
+ if_zapi_callbacks(sharp_ifp_create, sharp_ifp_up, sharp_ifp_down,
+ sharp_ifp_destroy);
zclient = zclient_new(master, &opt, sharp_handlers,
array_size(sharp_handlers));
diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h
index d8ea679797..8de14ce5af 100644
--- a/sharpd/sharp_zebra.h
+++ b/sharpd/sharp_zebra.h
@@ -75,4 +75,9 @@ extern void sharp_install_seg6local_route_helper(struct prefix *p,
extern int sharp_zebra_send_interface_protodown(struct interface *ifp,
bool down);
+extern int sharp_zebra_send_tc_filter_rate(struct interface *ifp,
+ const struct prefix *source,
+ const struct prefix *destination,
+ uint8_t ip_proto, uint16_t src_port,
+ uint16_t dst_port, uint64_t rate);
#endif
diff --git a/tests/topotests/bfd_topo1/test_bfd_topo1.py b/tests/topotests/bfd_topo1/test_bfd_topo1.py
index c9020f16d3..955d54f30c 100644
--- a/tests/topotests/bfd_topo1/test_bfd_topo1.py
+++ b/tests/topotests/bfd_topo1/test_bfd_topo1.py
@@ -207,7 +207,7 @@ def test_bgp_fast_reconvergence():
test_func = partial(
topotest.router_json_cmp, router, "show ip bgp json", expected
)
- _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=1)
assertmsg = "{}: bgp did not converge".format(router.name)
assert res is None, assertmsg
diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py
index ab44ba3c83..0f501f7089 100644
--- a/tests/topotests/bgp_features/test_bgp_features.py
+++ b/tests/topotests/bgp_features/test_bgp_features.py
@@ -785,7 +785,7 @@ def test_bgp_delayopen_without():
test_func = functools.partial(
topotest.router_json_cmp, router, "show ip bgp summary json", expected
)
- _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=1)
assertmsg = "BGP session on r{} did not shut down peer".format(router_num)
assert res is None, assertmsg
@@ -862,7 +862,7 @@ def test_bgp_delayopen_singular():
test_func = functools.partial(
topotest.router_json_cmp, router, "show ip bgp summary json", expected
)
- _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=1)
assertmsg = "BGP session on r{} did not shut down peer".format(router_num)
assert res is None, assertmsg
@@ -880,7 +880,7 @@ def test_bgp_delayopen_singular():
test_func = functools.partial(
topotest.router_json_cmp, router, "show bgp neighbors json", expected
)
- _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=1)
assertmsg = "BGP session on r1 failed to set DelayOpenTime for r4"
assert res is None, assertmsg
@@ -974,7 +974,7 @@ def test_bgp_delayopen_dual():
test_func = functools.partial(
topotest.router_json_cmp, router, "show ip bgp summary json", expected
)
- _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=1)
assertmsg = "BGP session on r{} did not shut down peer".format(router_num)
assert res is None, assertmsg
@@ -1003,7 +1003,7 @@ def test_bgp_delayopen_dual():
test_func = functools.partial(
topotest.router_json_cmp, router, "show bgp neighbors json", expected
)
- _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=1)
assertmsg = "BGP session on r{} failed to set DelayOpenTime".format(router_num)
assert res is None, assertmsg
@@ -1032,7 +1032,7 @@ def test_bgp_delayopen_dual():
test_func = functools.partial(
topotest.router_json_cmp, router, "show ip bgp summary json", expected
)
- _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=1)
assertmsg = "BGP session on r{} did not enter Connect state with peer".format(
router_num
)
diff --git a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
index 96a294cae3..6d2b65a614 100644
--- a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
+++ b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
@@ -106,7 +106,7 @@ def test_bgp_route():
"show ip route 50.0.0.0 json",
expected,
)
- _, result = topotest.run_and_expect(test_func, None, count=3, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
assertmsg = '"r3" JSON output mismatches'
assert result is None, assertmsg
@@ -118,8 +118,9 @@ def test_bgp_route():
r3,
"show ip route 10.0.0.3 json",
expected,
- )
- _, result = topotest.run_and_expect(test_func, None, count=3, wait=0.5)
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+
def test_bgp_better_admin_won():
"A better Admin distance protocol may come along and knock us out"
diff --git a/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py
index eed0b34371..acf89835e1 100644
--- a/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py
+++ b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py
@@ -153,7 +153,7 @@ def test_bgp_tcp_mss():
"Verify if TCP MSS value is synced with neighbor in {}".format(router1.name)
)
test_func = functools.partial(_bgp_check_neighbor_tcp_mss, router1, "192.168.255.2")
- success, result = topotest.run_and_expect(test_func, None, count=3, wait=0.5)
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
assert (
result is None
), 'Failed to sync TCP MSS value over BGP session in "{}"'.format(router1.name)
@@ -163,7 +163,7 @@ def test_bgp_tcp_mss():
"Verify if TCP MSS value is synced with neighbor in {}".format(router2.name)
)
test_func = functools.partial(_bgp_check_neighbor_tcp_mss, router2, "192.168.255.1")
- success, result = topotest.run_and_expect(test_func, None, count=3, wait=0.5)
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
assert (
result is None
), 'Failed to sync TCP MSS value over BGP session in "{}"'.format(router2.name)
diff --git a/tests/topotests/bgp_vpn_5549_route_map/__init__.py b/tests/topotests/bgp_vpn_5549_route_map/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/__init__.py
diff --git a/tests/topotests/bgp_vpn_5549_route_map/cpe1/bgpd.conf b/tests/topotests/bgp_vpn_5549_route_map/cpe1/bgpd.conf
new file mode 100644
index 0000000000..013cd8c258
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/cpe1/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers 1 3
+ neighbor 192.168.1.2 timers connect 1
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/cpe1/zebra.conf b/tests/topotests/bgp_vpn_5549_route_map/cpe1/zebra.conf
new file mode 100644
index 0000000000..49dcfc3b02
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/cpe1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface cpe1-eth0
+ ip address 192.168.1.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/cpe2/bgpd.conf b/tests/topotests/bgp_vpn_5549_route_map/cpe2/bgpd.conf
new file mode 100644
index 0000000000..d65d507bf4
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/cpe2/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers 1 3
+ neighbor 192.168.2.2 timers connect 1
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/cpe2/zebra.conf b/tests/topotests/bgp_vpn_5549_route_map/cpe2/zebra.conf
new file mode 100644
index 0000000000..a47319e45f
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/cpe2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface cpe2-eth0
+ ip address 192.168.2.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf b/tests/topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf
new file mode 100644
index 0000000000..93da025463
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf
@@ -0,0 +1,38 @@
+router bgp 65001
+ bgp router-id 10.10.10.10
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001:db8:1::2 remote-as internal
+ neighbor 2001:db8:1::2 update-source 2001:db8:1::1
+ neighbor 2001:db8:1::2 timers 1 3
+ neighbor 2001:db8:1::2 timers connect 1
+ neighbor 2001:db8:1::2 capability extended-nexthop
+ address-family ipv4 vpn
+ neighbor 2001:db8:1::2 activate
+ neighbor 2001:db8:1::2 route-map pe2 out
+ exit-address-family
+!
+router bgp 65001 vrf RED
+ bgp router-id 192.168.1.2
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers 1 3
+ neighbor 192.168.1.1 timers connect 1
+ address-family ipv4 unicast
+ label vpn export 1111
+ rd vpn export 192.168.1.2:2
+ rt vpn import 192.168.2.2:2 192.168.1.2:2
+ rt vpn export 192.168.1.2:2
+ export vpn
+ import vpn
+ exit-address-family
+!
+ip prefix-list cpe1 seq 5 permit 172.16.255.1/32
+!
+route-map pe2 permit 10
+ match ip address prefix-list cpe1
+ set ipv6 vpn next-hop 2001:db8::1
+!
+route-map pe2 permit 20
+exit
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe1/ldpd.conf b/tests/topotests/bgp_vpn_5549_route_map/pe1/ldpd.conf
new file mode 100644
index 0000000000..fb40f06fa7
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe1/ldpd.conf
@@ -0,0 +1,10 @@
+mpls ldp
+ router-id 10.10.10.10
+ !
+ address-family ipv4
+ discovery transport-address 10.10.10.10
+ !
+ interface pe1-eth1
+ !
+ !
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe1/ospf6d.conf b/tests/topotests/bgp_vpn_5549_route_map/pe1/ospf6d.conf
new file mode 100644
index 0000000000..0053d1ecb5
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe1/ospf6d.conf
@@ -0,0 +1,12 @@
+!
+interface lo
+ ipv6 ospf6 area 0
+!
+interface pe1-eth1
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 dead-interval 3
+!
+router ospf6
+ ospf6 router-id 10.10.10.10
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe1/zebra.conf b/tests/topotests/bgp_vpn_5549_route_map/pe1/zebra.conf
new file mode 100644
index 0000000000..da91055135
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe1/zebra.conf
@@ -0,0 +1,14 @@
+!
+interface lo
+ ip address 10.10.10.10/32
+ ipv6 address 2001:db8:1::1/128
+!
+interface pe1-eth0 vrf RED
+ ip address 192.168.1.2/24
+!
+interface pe1-eth1
+ ip address 10.0.1.1/24
+ ipv6 address 2001:db8::1/64
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf b/tests/topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf
new file mode 100644
index 0000000000..6db1eef993
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf
@@ -0,0 +1,29 @@
+router bgp 65001
+ bgp router-id 10.10.10.20
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001:db8:1::1 remote-as internal
+ neighbor 2001:db8:1::1 update-source 2001:db8:1::2
+ neighbor 2001:db8:1::1 timers 1 3
+ neighbor 2001:db8:1::1 timers connect 1
+ neighbor 2001:db8:1::1 capability extended-nexthop
+ address-family ipv4 vpn
+ neighbor 2001:db8:1::1 activate
+ exit-address-family
+!
+router bgp 65001 vrf RED
+ bgp router-id 192.168.2.2
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 timers 1 3
+ neighbor 192.168.2.1 timers connect 1
+ address-family ipv4 unicast
+ label vpn export 2222
+ rd vpn export 192.168.2.2:2
+ rt vpn import 192.168.2.2:2 192.168.1.2:2
+ rt vpn export 192.168.2.2:2
+ export vpn
+ import vpn
+ exit-address-family
+!
+
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe2/ldpd.conf b/tests/topotests/bgp_vpn_5549_route_map/pe2/ldpd.conf
new file mode 100644
index 0000000000..e2b5359993
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe2/ldpd.conf
@@ -0,0 +1,10 @@
+mpls ldp
+ router-id 10.10.10.20
+ !
+ address-family ipv4
+ discovery transport-address 10.10.10.20
+ !
+ interface pe2-eth0
+ !
+ !
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe2/ospf6d.conf b/tests/topotests/bgp_vpn_5549_route_map/pe2/ospf6d.conf
new file mode 100644
index 0000000000..f79bb4f5f7
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe2/ospf6d.conf
@@ -0,0 +1,12 @@
+!
+interface lo
+ ipv6 ospf6 area 0
+!
+interface pe2-eth0
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 dead-interval 3
+!
+router ospf6
+ ospf6 router-id 10.10.10.20
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe2/zebra.conf b/tests/topotests/bgp_vpn_5549_route_map/pe2/zebra.conf
new file mode 100644
index 0000000000..19ef7bf911
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe2/zebra.conf
@@ -0,0 +1,14 @@
+!
+interface lo
+ ip address 10.10.10.20/32
+ ipv6 address 2001:db8:1::2/128
+!
+interface pe2-eth1 vrf RED
+ ip address 192.168.2.2/24
+!
+interface pe2-eth0
+ ip address 10.0.1.2/24
+ ipv6 address 2001:db8::2/64
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py b/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py
new file mode 100644
index 0000000000..e567d3654f
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Check if we can override VPN underlay next-hop from PE1 to PE2.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ tgen.add_router("cpe1")
+ tgen.add_router("cpe2")
+ tgen.add_router("pe1")
+ tgen.add_router("pe2")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["cpe1"])
+ switch.add_link(tgen.gears["pe1"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["pe1"])
+ switch.add_link(tgen.gears["pe2"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["pe2"])
+ switch.add_link(tgen.gears["cpe2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ pe1 = tgen.gears["pe1"]
+ pe2 = tgen.gears["pe2"]
+
+ pe1.run("ip link add RED type vrf table 1001")
+ pe1.run("ip link set up dev RED")
+ pe2.run("ip link add RED type vrf table 1001")
+ pe2.run("ip link set up dev RED")
+ pe1.run("ip link set pe1-eth0 master RED")
+ pe2.run("ip link set pe2-eth1 master RED")
+
+ pe1.run("sysctl -w net.ipv4.ip_forward=1")
+ pe2.run("sysctl -w net.ipv4.ip_forward=1")
+ pe1.run("sysctl -w net.mpls.conf.pe1-eth0.input=1")
+ pe2.run("sysctl -w net.mpls.conf.pe2-eth1.input=1")
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_vpn_5549():
+ tgen = get_topogen()
+
+ pe2 = tgen.gears["pe2"]
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_vpn_nexthop_changed():
+ output = json.loads(pe2.vtysh_cmd("show bgp ipv4 vpn json"))
+ expected = {
+ "routes": {
+ "routeDistinguishers": {
+ "192.168.1.2:2": {
+ "172.16.255.1/32": [
+ {"valid": True, "nexthops": [{"ip": "2001:db8::1"}]}
+ ],
+ "192.168.1.0/24": [
+ {"valid": True, "nexthops": [{"ip": "2001:db8:1::1"}]}
+ ],
+ }
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_vpn_nexthop_changed)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed overriding IPv6 next-hop for VPN underlay"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/cspf_topo1/test_cspf_topo1.py b/tests/topotests/cspf_topo1/test_cspf_topo1.py
index 220ec49768..7fe86cfabc 100644
--- a/tests/topotests/cspf_topo1/test_cspf_topo1.py
+++ b/tests/topotests/cspf_topo1/test_cspf_topo1.py
@@ -177,7 +177,7 @@ def compare_cspf_output(tgen, rname, fileref, src, dst, cost, bw=""):
test_func = partial(
topotest.router_output_cmp, tgen.gears[rname], command, expected
)
- result, diff = topotest.run_and_expect(test_func, "", count=2, wait=2)
+ result, diff = topotest.run_and_expect(test_func, "", count=5, wait=2)
assert result, "CSPF output mismatches the expected result on {}:\n{}".format(
rname, diff
)
diff --git a/tests/topotests/isis_lfa_topo1/rt1/bfdd.conf b/tests/topotests/isis_lfa_topo1/rt1/bfdd.conf
index 86cf68dd8d..ef67eae0cf 100644
--- a/tests/topotests/isis_lfa_topo1/rt1/bfdd.conf
+++ b/tests/topotests/isis_lfa_topo1/rt1/bfdd.conf
@@ -1,6 +1,4 @@
hostname rt1
!
bfd
- peer 2001:db8:1000::2 multihop local-address 2001:db8:1000::1
- !
!
diff --git a/tests/topotests/isis_lfa_topo1/rt2/bfdd.conf b/tests/topotests/isis_lfa_topo1/rt2/bfdd.conf
index 40357a4d03..25fa0d8a46 100644
--- a/tests/topotests/isis_lfa_topo1/rt2/bfdd.conf
+++ b/tests/topotests/isis_lfa_topo1/rt2/bfdd.conf
@@ -1,6 +1,4 @@
hostname rt2
!
bfd
- peer 2001:db8:1000::1 multihop local-address 2001:db8:1000::2
- !
!
diff --git a/tests/topotests/isis_lfa_topo1/test_isis_lfa_topo1.py b/tests/topotests/isis_lfa_topo1/test_isis_lfa_topo1.py
index f72942b607..61caf257eb 100755
--- a/tests/topotests/isis_lfa_topo1/test_isis_lfa_topo1.py
+++ b/tests/topotests/isis_lfa_topo1/test_isis_lfa_topo1.py
@@ -676,8 +676,8 @@ def test_rib_ipv6_step15():
rname,
"show ipv6 route isis json",
outputs[rname][15]["show_ipv6_route.ref"],
- count=2,
- wait=0.05,
+ count=10,
+ wait=0.5,
)
@@ -758,6 +758,80 @@ def test_rib_ipv6_step17():
# - Route switchover of routes via eth-rt2
#
def test_rib_ipv6_step18():
+ def _rt2_neigh_down(router):
+ output = json.loads(router.vtysh_cmd("show isis neighbor rt2 json"))
+
+ """
+ Previous output was:
+ {
+ "areas":[
+ {
+ "area":"1",
+ "circuits":[
+ {
+ "circuit":0,
+ "adj":"rt2",
+ "interface":{
+ "name":"eth-rt2",
+ "state":"Up",
+ "adj-flaps":1,
+ "last-ago":"21s",
+ "circuit-type":"L1",
+ "speaks":"IPv6",
+ "topologies":{
+ "topo-0":"ipv6-unicast"
+ },
+ "snpa":"2020.2020.2020",
+ "area-address":{
+ "isonet":"49.0000"
+ },
+ "ipv6-link-local":{
+ "ipv6":"fe80::ac19:a8ff:fee5:f48f"
+ },
+ "adj-sid":{
+ }
+ },
+ "level":1,
+ "expires-in":"2s"
+ },
+ {
+ "circuit":0
+ },
+ {
+ "circuit":0
+ },
+ {
+ "circuit":0
+ },
+ {
+ "circuit":0
+ },
+ {
+ "circuit":0
+ }
+ ]
+ }
+ ]
+ """
+
+ expected = {
+ "areas": [
+ {
+ "area": "1",
+ "circuits": [
+ {"circuit": 0},
+ {"circuit": 0},
+ {"circuit": 0},
+ {"circuit": 0},
+ {"circuit": 0},
+ {"circuit": 0},
+ ],
+ }
+ ]
+ }
+
+ return topotest.json_cmp(output, expected, exact=True)
+
logger.info("Test (step 18): verify IPv6 RIB")
tgen = get_topogen()
@@ -769,30 +843,17 @@ def test_rib_ipv6_step18():
tgen.net.cmd_raises("ip link set s1 down")
rname = "rt1"
-
- retry = 200 + 1
-
- while retry:
- retry -= 1
- output = tgen.gears[rname].vtysh_cmd("show isis neighbor json")
- output_json = json.loads(output)
- found = False
- for neighbor in output_json["areas"][0]["circuits"]:
- if "adj" in neighbor and neighbor["adj"] == "rt2":
- found = True
- break
- if not found:
- break
- time.sleep(0.05)
-
- assert not found, "rt2 neighbor is still present"
+ router = tgen.gears[rname]
+ test_func = partial(_rt2_neigh_down, router)
+ success, result = topotest.run_and_expect(test_func, None, count=200, wait=0.05)
+ assert result is None, 'rt2 neighbor is still present on "{}"'.format(router)
router_compare_json_output(
rname,
"show ipv6 route isis json",
outputs[rname][15]["show_ipv6_route.ref"],
- count=2,
- wait=0.05,
+ count=10,
+ wait=0.5,
)
@@ -887,8 +948,8 @@ def test_rib_ipv6_step21():
rname,
"show ipv6 route isis json",
outputs[rname][15]["show_ipv6_route.ref"],
- count=2,
- wait=0.05,
+ count=10,
+ wait=0.5,
)
@@ -941,14 +1002,14 @@ def test_rib_ipv6_step23():
conf_file = os.path.join(CWD, "{}/bfdd.conf".format(rname))
tgen.net[rname].cmd("vtysh -f {}".format(conf_file))
- rname = "rt1"
- expect = '[{"multihop":true,"peer":"2001:db8:1000::2","local":"2001:db8:1000::1","status":"up"}]'
- router_compare_json_output(rname, "show bfd peers json", expect)
-
logger.info("Set ISIS BFD")
tgen.net["rt1"].cmd('vtysh -c "conf t" -c "int eth-rt2" -c "isis bfd"')
tgen.net["rt2"].cmd('vtysh -c "conf t" -c "int eth-rt1" -c "isis bfd"')
+ rname = "rt1"
+ expect = '[{"multihop":false,"interface":"eth-rt2","status":"up"}]'
+ router_compare_json_output(rname, "show bfd peers json", expect)
+
router_compare_json_output(
rname,
"show ipv6 route isis json",
@@ -968,6 +1029,11 @@ def test_rib_ipv6_step23():
# - Route switchover of routes via eth-rt2
#
def test_rib_ipv6_step24():
+ def _bfd_down(router):
+ output = json.loads(router.vtysh_cmd("show bfd peers json"))
+ expected = []
+ return topotest.json_cmp(output, expected, exact=True)
+
logger.info("Test (step 24): verify IPv6 RIB")
tgen = get_topogen()
@@ -979,20 +1045,16 @@ def test_rib_ipv6_step24():
tgen.net.cmd_raises("ip link set s1 down")
rname = "rt1"
- expect = '[{"multihop":true,"peer":"2001:db8:1000::2","local":"2001:db8:1000::1","status":"down"}]'
- router_compare_json_output(
- rname,
- "show bfd peers json",
- expect,
- count=40,
- wait=0.05,
- )
+ router = tgen.gears[rname]
+ test_func = partial(_bfd_down, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.3)
+ assert result is None, 'BFD session is still up on "{}"'.format(router)
router_compare_json_output(
rname,
"show ipv6 route isis json",
outputs[rname][15]["show_ipv6_route.ref"],
- count=4,
+ count=5,
)
diff --git a/tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py b/tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py
index 5a5b9c59de..fa76072b4a 100755
--- a/tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py
+++ b/tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py
@@ -831,19 +831,19 @@ def test_rt6_step11():
rname,
"show ip route isis json",
outputs[rname][11]["show_ip_route.ref"],
- count=1,
+ count=5,
)
router_compare_json_output(
rname,
"show ipv6 route isis json",
outputs[rname][11]["show_ipv6_route.ref"],
- count=1,
+ count=5,
)
router_compare_json_output(
rname,
"show mpls table json",
outputs[rname][11]["show_mpls_table.ref"],
- count=1,
+ count=5,
)
@@ -1021,26 +1021,26 @@ def test_rt6_step14():
"show bfd peers json",
expect,
count=40,
- wait=0.05,
+ wait=0.5,
)
router_compare_json_output(
rname,
"show ip route isis json",
outputs[rname][11]["show_ip_route.ref"],
- count=4,
+ count=5,
)
router_compare_json_output(
rname,
"show ipv6 route isis json",
outputs[rname][11]["show_ipv6_route.ref"],
- count=4,
+ count=5,
)
router_compare_json_output(
rname,
"show mpls table json",
outputs[rname][11]["show_mpls_table.ref"],
- count=4,
+ count=5,
)
diff --git a/tests/topotests/tc_basic/test_tc_basic.py b/tests/topotests/tc_basic/test_tc_basic.py
new file mode 100755
index 0000000000..e98e11d71d
--- /dev/null
+++ b/tests/topotests/tc_basic/test_tc_basic.py
@@ -0,0 +1,133 @@
+#!/usr/bin/python
+
+#
+# test_tc_basic.py
+#
+# Copyright (c) 2022 by Shichu Yang
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+"""
+test_tc_basic.py: Test basic TC filters, classes and qdiscs.
+"""
+import sys
+import os
+import pytest
+import time
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+from lib.topogen import Topogen, TopoRouter
+from lib.topolog import logger
+
+pytestmark = [
+ pytest.mark.sharpd
+]
+
+def build_topo(tgen):
+ "Build function"
+
+ r1 = tgen.add_router("r1")
+ r2 = tgen.add_router("r2")
+
+ # Create a p2p connection between r1 and r2
+ tgen.add_link(r1, r2)
+
+ # Create switches with each router connected to it to simulate a empty network.
+ switch = tgen.add_switch("s1")
+ switch.add_link(r1)
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(r2)
+
+# New form of setup/teardown using pytest fixture
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, request.module.__name__)
+
+ # ... and here it calls initialization functions.
+ tgen.start_topology()
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all routers arrange for:
+ # - starting zebra using config file from <rtrname>/zebra.conf
+ # - starting ospfd using an empty config file.
+ for _, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA)
+ router.load_config(TopoRouter.RD_SHARP)
+
+ # Start and configure the router daemons
+ tgen.start_router()
+
+ # Provide tgen as argument to each test function
+ yield tgen
+
+ # Teardown after last test runs
+ tgen.stop_topology()
+
+
+# Fixture that executes before each test
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+def fetch_iproute2_tc_info(r, interface):
+ qdisc = r.cmd("tc qdisc show dev %s" % interface)
+ tclass = r.cmd("tc class show dev %s" % interface)
+ tfilter = r.cmd("tc filter show dev %s" % interface)
+ return qdisc, tclass, tfilter
+
+# ===================
+# The tests functions
+# ===================
+
+def test_tc_basic(tgen):
+ "Test installing one pair of filter & class by sharpd"
+
+ r1 = tgen.gears["r1"]
+ intf = "r1-eth0"
+ r1.vtysh_cmd("sharp tc dev %s source 192.168.100.0/24 destination 192.168.101.0/24 ip-protocol tcp src-port 8000 dst-port 8001 rate 20mbit" % intf)
+
+ time.sleep(3)
+
+ qdisc, tclass, tfilter = fetch_iproute2_tc_info(r1, intf)
+
+ logger.info("tc qdisc on %s: %s", intf, qdisc)
+ logger.info("tc class on %s: %s", intf, tclass)
+ logger.info("tc filter on %s: %s", intf, tfilter)
+
+ assert "htb" in qdisc
+ assert "beef:" in qdisc
+
+ assert "20Mbit" in tclass
+
+ assert "tcp" in tfilter
+ assert "dst_ip 192.168.101.0/24" in tfilter
+ assert "src_ip 192.168.100.0/24" in tfilter
+ assert "dst_port 8001" in tfilter
+ assert "src_port 8000" in tfilter
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args)) \ No newline at end of file
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 48274d7170..30f117505a 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -3413,6 +3413,30 @@ int vtysh_write_config_integrated(void)
return CMD_SUCCESS;
}
+DEFUN_HIDDEN(start_config, start_config_cmd, "XFRR_start_configuration",
+ "The Beginning of Configuration\n")
+{
+ unsigned int i;
+ char line[] = "XFRR_start_configuration";
+
+ for (i = 0; i < array_size(vtysh_client); i++)
+ vtysh_client_execute(&vtysh_client[i], line);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(end_config, end_config_cmd, "XFRR_end_configuration",
+ "The End of Configuration\n")
+{
+ unsigned int i;
+ char line[] = "XFRR_end_configuration";
+
+ for (i = 0; i < array_size(vtysh_client); i++)
+ vtysh_client_execute(&vtysh_client[i], line);
+
+ return CMD_SUCCESS;
+}
+
static bool want_config_integrated(void)
{
struct stat s;
@@ -4870,6 +4894,9 @@ void vtysh_init_vty(void)
/* "write memory" command. */
install_element(ENABLE_NODE, &vtysh_write_memory_cmd);
+ install_element(CONFIG_NODE, &start_config_cmd);
+ install_element(CONFIG_NODE, &end_config_cmd);
+
install_element(CONFIG_NODE, &vtysh_terminal_paginate_cmd);
install_element(VIEW_NODE, &vtysh_terminal_paginate_cmd);
install_element(VIEW_NODE, &vtysh_terminal_length_cmd);
diff --git a/zebra/debug.c b/zebra/debug.c
index 16aac7909f..953f0423af 100644
--- a/zebra/debug.c
+++ b/zebra/debug.c
@@ -42,6 +42,7 @@ unsigned long zebra_debug_nexthop;
unsigned long zebra_debug_evpn_mh;
unsigned long zebra_debug_pbr;
unsigned long zebra_debug_neigh;
+unsigned long zebra_debug_tc;
DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty));
@@ -374,6 +375,17 @@ DEFPY (debug_zebra_neigh,
return CMD_SUCCESS;
}
+DEFUN (debug_zebra_tc,
+ debug_zebra_tc_cmd,
+ "debug zebra tc",
+ DEBUG_STR
+ "Zebra configuration\n"
+ "Debug zebra tc events\n")
+{
+ SET_FLAG(zebra_debug_tc, ZEBRA_DEBUG_TC);
+ return CMD_SUCCESS;
+}
+
DEFPY (debug_zebra_mlag,
debug_zebra_mlag_cmd,
"[no$no] debug zebra mlag",
@@ -797,6 +809,7 @@ void zebra_debug_init(void)
install_element(ENABLE_NODE, &debug_zebra_nexthop_cmd);
install_element(ENABLE_NODE, &debug_zebra_pbr_cmd);
install_element(ENABLE_NODE, &debug_zebra_neigh_cmd);
+ install_element(ENABLE_NODE, &debug_zebra_tc_cmd);
install_element(ENABLE_NODE, &debug_zebra_dplane_dpdk_cmd);
install_element(ENABLE_NODE, &no_debug_zebra_events_cmd);
install_element(ENABLE_NODE, &no_debug_zebra_nht_cmd);
diff --git a/zebra/debug.h b/zebra/debug.h
index 73546de632..e0c6a9e2b9 100644
--- a/zebra/debug.h
+++ b/zebra/debug.h
@@ -75,6 +75,8 @@ extern "C" {
#define ZEBRA_DEBUG_NEIGH 0x01
+#define ZEBRA_DEBUG_TC 0x01
+
/* Debug related macro. */
#define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT)
@@ -133,6 +135,8 @@ extern "C" {
#define IS_ZEBRA_DEBUG_NEIGH (zebra_debug_neigh & ZEBRA_DEBUG_NEIGH)
+#define IS_ZEBRA_DEBUG_TC (zebra_debug_tc & ZEBRA_DEBUG_TC)
+
extern unsigned long zebra_debug_event;
extern unsigned long zebra_debug_packet;
extern unsigned long zebra_debug_kernel;
@@ -149,6 +153,7 @@ extern unsigned long zebra_debug_nexthop;
extern unsigned long zebra_debug_evpn_mh;
extern unsigned long zebra_debug_pbr;
extern unsigned long zebra_debug_neigh;
+extern unsigned long zebra_debug_tc;
extern void zebra_debug_init(void);
diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c
index c5e1c113cb..6c95be29df 100644
--- a/zebra/dplane_fpm_nl.c
+++ b/zebra/dplane_fpm_nl.c
@@ -816,9 +816,14 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx)
case DPLANE_OP_INTF_INSTALL:
case DPLANE_OP_INTF_UPDATE:
case DPLANE_OP_INTF_DELETE:
- case DPLANE_OP_TC_INSTALL:
- case DPLANE_OP_TC_UPDATE:
- case DPLANE_OP_TC_DELETE:
+ case DPLANE_OP_TC_QDISC_INSTALL:
+ case DPLANE_OP_TC_QDISC_UNINSTALL:
+ case DPLANE_OP_TC_CLASS_ADD:
+ case DPLANE_OP_TC_CLASS_DELETE:
+ case DPLANE_OP_TC_CLASS_UPDATE:
+ case DPLANE_OP_TC_FILTER_ADD:
+ case DPLANE_OP_TC_FILTER_DELETE:
+ case DPLANE_OP_TC_FILTER_UPDATE:
case DPLANE_OP_NONE:
break;
diff --git a/zebra/interface.c b/zebra/interface.c
index 32703b59bc..d61d3620f1 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -1573,9 +1573,14 @@ void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_IPSET_ENTRY_DELETE:
case DPLANE_OP_NEIGH_TABLE_UPDATE:
case DPLANE_OP_GRE_SET:
- case DPLANE_OP_TC_INSTALL:
- case DPLANE_OP_TC_UPDATE:
- case DPLANE_OP_TC_DELETE:
+ case DPLANE_OP_TC_QDISC_INSTALL:
+ case DPLANE_OP_TC_QDISC_UNINSTALL:
+ case DPLANE_OP_TC_CLASS_ADD:
+ case DPLANE_OP_TC_CLASS_DELETE:
+ case DPLANE_OP_TC_CLASS_UPDATE:
+ case DPLANE_OP_TC_FILTER_ADD:
+ case DPLANE_OP_TC_FILTER_DELETE:
+ case DPLANE_OP_TC_FILTER_UPDATE:
break; /* should never hit here */
}
}
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index a8b56bb8f2..54d9561e2b 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -423,6 +423,15 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
return netlink_nexthop_change(h, ns_id, startup);
case RTM_DELNEXTHOP:
return netlink_nexthop_change(h, ns_id, startup);
+ case RTM_NEWQDISC:
+ case RTM_DELQDISC:
+ return netlink_qdisc_change(h, ns_id, startup);
+ case RTM_NEWTCLASS:
+ case RTM_DELTCLASS:
+ return netlink_tclass_change(h, ns_id, startup);
+ case RTM_NEWTFILTER:
+ case RTM_DELTFILTER:
+ return netlink_tfilter_change(h, ns_id, startup);
/* Messages handled in the dplane thread */
case RTM_NEWADDR:
@@ -1640,10 +1649,17 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth,
case DPLANE_OP_INTF_DELETE:
return netlink_put_intf_update_msg(bth, ctx);
- case DPLANE_OP_TC_INSTALL:
- case DPLANE_OP_TC_UPDATE:
- case DPLANE_OP_TC_DELETE:
- return netlink_put_tc_update_msg(bth, ctx);
+ case DPLANE_OP_TC_QDISC_INSTALL:
+ case DPLANE_OP_TC_QDISC_UNINSTALL:
+ return netlink_put_tc_qdisc_update_msg(bth, ctx);
+ case DPLANE_OP_TC_CLASS_ADD:
+ case DPLANE_OP_TC_CLASS_DELETE:
+ case DPLANE_OP_TC_CLASS_UPDATE:
+ return netlink_put_tc_class_update_msg(bth, ctx);
+ case DPLANE_OP_TC_FILTER_ADD:
+ case DPLANE_OP_TC_FILTER_DELETE:
+ case DPLANE_OP_TC_FILTER_UPDATE:
+ return netlink_put_tc_filter_update_msg(bth, ctx);
}
return FRR_NETLINK_ERROR;
@@ -1757,15 +1773,16 @@ void kernel_init(struct zebra_ns *zns)
* RTNLGRP_XXX to a bit position for ourself
*/
groups = RTMGRP_LINK |
- RTMGRP_IPV4_ROUTE |
- RTMGRP_IPV4_IFADDR |
- RTMGRP_IPV6_ROUTE |
- RTMGRP_IPV6_IFADDR |
- RTMGRP_IPV4_MROUTE |
- RTMGRP_NEIGH |
- ((uint32_t) 1 << (RTNLGRP_IPV4_RULE - 1)) |
- ((uint32_t) 1 << (RTNLGRP_IPV6_RULE - 1)) |
- ((uint32_t) 1 << (RTNLGRP_NEXTHOP - 1));
+ RTMGRP_IPV4_ROUTE |
+ RTMGRP_IPV4_IFADDR |
+ RTMGRP_IPV6_ROUTE |
+ RTMGRP_IPV6_IFADDR |
+ RTMGRP_IPV4_MROUTE |
+ RTMGRP_NEIGH |
+ ((uint32_t) 1 << (RTNLGRP_IPV4_RULE - 1)) |
+ ((uint32_t) 1 << (RTNLGRP_IPV6_RULE - 1)) |
+ ((uint32_t) 1 << (RTNLGRP_NEXTHOP - 1)) |
+ ((uint32_t) 1 << (RTNLGRP_TC - 1));
dplane_groups = (RTMGRP_LINK |
RTMGRP_IPV4_IFADDR |
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index e76d8c0cc4..f84d31cc44 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -1594,9 +1594,14 @@ void kernel_update_multi(struct dplane_ctx_q *ctx_list)
res = kernel_intf_update(ctx);
break;
- case DPLANE_OP_TC_INSTALL:
- case DPLANE_OP_TC_UPDATE:
- case DPLANE_OP_TC_DELETE:
+ case DPLANE_OP_TC_QDISC_INSTALL:
+ case DPLANE_OP_TC_QDISC_UNINSTALL:
+ case DPLANE_OP_TC_CLASS_ADD:
+ case DPLANE_OP_TC_CLASS_DELETE:
+ case DPLANE_OP_TC_CLASS_UPDATE:
+ case DPLANE_OP_TC_FILTER_ADD:
+ case DPLANE_OP_TC_FILTER_DELETE:
+ case DPLANE_OP_TC_FILTER_UPDATE:
res = kernel_tc_update(ctx);
break;
diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c
index f70b006acd..8990f66ef8 100644
--- a/zebra/rtread_netlink.c
+++ b/zebra/rtread_netlink.c
@@ -26,8 +26,10 @@
#include "vty.h"
#include "zebra/rt.h"
#include "zebra/zebra_pbr.h"
+#include "zebra/zebra_tc.h"
#include "zebra/rt_netlink.h"
#include "zebra/rule_netlink.h"
+#include "zebra/tc_netlink.h"
void route_read(struct zebra_ns *zns)
{
@@ -71,4 +73,9 @@ void kernel_read_pbr_rules(struct zebra_ns *zns)
netlink_rules_read(zns);
}
+void kernel_read_tc_qdisc(struct zebra_ns *zns)
+{
+ netlink_qdisc_read(zns);
+}
+
#endif /* GNU_LINUX */
diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c
index 594f7c2dd9..35dde0e686 100644
--- a/zebra/rtread_sysctl.c
+++ b/zebra/rtread_sysctl.c
@@ -30,6 +30,7 @@
#include "zebra/rt.h"
#include "zebra/kernel_socket.h"
#include "zebra/zebra_pbr.h"
+#include "zebra/zebra_tc.h"
#include "zebra/zebra_errors.h"
/* Kernel routing table read up by sysctl function. */
@@ -108,4 +109,8 @@ void kernel_read_pbr_rules(struct zebra_ns *zns)
{
}
+void kernel_read_tc_qdisc(struct zebra_ns *zns)
+{
+}
+
#endif /* !defined(GNU_LINUX) */
diff --git a/zebra/subdir.am b/zebra/subdir.am
index 9842931496..5c4a87b934 100644
--- a/zebra/subdir.am
+++ b/zebra/subdir.am
@@ -100,6 +100,7 @@ zebra_zebra_SOURCES = \
zebra/zebra_routemap_nb_config.c \
zebra/zebra_script.c \
zebra/zebra_srte.c \
+ zebra/zebra_tc.c \
zebra/zebra_trace.c \
zebra/zebra_vrf.c \
zebra/zebra_vty.c \
@@ -175,6 +176,7 @@ noinst_HEADERS += \
zebra/zebra_router.h \
zebra/zebra_script.h \
zebra/zebra_srte.h \
+ zebra/zebra_tc.h \
zebra/zebra_trace.h \
zebra/zebra_vrf.h \
zebra/zebra_vxlan.h \
diff --git a/zebra/tc_netlink.c b/zebra/tc_netlink.c
index afa03a4fa5..1fa80e846a 100644
--- a/zebra/tc_netlink.c
+++ b/zebra/tc_netlink.c
@@ -3,28 +3,27 @@
*
* Copyright (C) 2022 Shichu Yang
*
- * This file is part of FRR.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
*
- * FRR is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
*
- * FRR is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with FRR; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
#ifdef HAVE_NETLINK
+#include <linux/pkt_cls.h>
+#include <linux/pkt_sched.h>
#include <netinet/if_ether.h>
#include <sys/socket.h>
@@ -32,34 +31,24 @@
#include "prefix.h"
#include "vrf.h"
-#include <linux/fib_rules.h>
-#include <linux/pkt_cls.h>
-#include <linux/pkt_sched.h>
#include "zebra/zserv.h"
#include "zebra/zebra_ns.h"
-#include "zebra/zebra_vrf.h"
#include "zebra/rt.h"
#include "zebra/interface.h"
#include "zebra/debug.h"
-#include "zebra/rtadv.h"
#include "zebra/kernel_netlink.h"
#include "zebra/tc_netlink.h"
#include "zebra/zebra_errors.h"
#include "zebra/zebra_dplane.h"
+#include "zebra/zebra_tc.h"
#include "zebra/zebra_trace.h"
-/* TODO: move these bitflags to zebra_tc.h */
-#define TC_FILTER_SRC_IP (1 << 0)
-#define TC_FILTER_DST_IP (1 << 1)
-#define TC_FILTER_IP_PROTOCOL (1 << 9)
-
#define TC_FREQ_DEFAULT (100)
-#define TC_MAJOR_BASE (0x1000u)
+/* some magic number */
+#define TC_QDISC_MAJOR_ZEBRA (0xbeef0000u)
#define TC_MINOR_NOCLASS (0xffffu)
-#define TC_FILTER_MASK (0x8000u)
-
#define TIME_UNITS_PER_SEC (1000000)
#define xmittime(r, s) (TIME_UNITS_PER_SEC * ((double)(s) / (double)(r)))
@@ -81,19 +70,6 @@ static uint32_t tc_get_freq(void)
return freq == 0 ? TC_FREQ_DEFAULT : freq;
}
-static inline uint32_t tc_make_handle(uint16_t major, uint16_t minor)
-{
- return (major) << 16 | (minor);
-}
-
-static inline uint32_t tc_get_handle(struct zebra_dplane_ctx *ctx,
- uint16_t minor)
-{
- uint16_t major = TC_MAJOR_BASE + (uint16_t)dplane_ctx_get_ifindex(ctx);
-
- return tc_make_handle(major, minor);
-}
-
static void tc_calc_rate_table(struct tc_ratespec *ratespec, uint32_t *table,
uint32_t mtu)
{
@@ -189,11 +165,7 @@ static ssize_t netlink_qdisc_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
void *data, size_t datalen)
{
struct nlsock *nl;
-
- const char *kind = "htb";
-
- struct tc_htb_glob htb_glob = {
- .rate2quantum = 10, .version = 3, .defcls = TC_MINOR_NOCLASS};
+ const char *kind_str = NULL;
struct rtattr *nest;
@@ -221,16 +193,41 @@ static ssize_t netlink_qdisc_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
req->t.tcm_family = AF_UNSPEC;
req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx);
- req->t.tcm_handle = tc_get_handle(ctx, 0);
+ req->t.tcm_info = 0;
+ req->t.tcm_handle = 0;
req->t.tcm_parent = TC_H_ROOT;
- nl_attr_put(&req->n, datalen, TCA_KIND, kind, strlen(kind) + 1);
+ if (cmd == RTM_NEWQDISC) {
+ req->t.tcm_handle = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0);
- nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
+ kind_str = dplane_ctx_tc_qdisc_get_kind_str(ctx);
- nl_attr_put(&req->n, datalen, TCA_HTB_INIT, &htb_glob,
- sizeof(htb_glob));
- nl_attr_nest_end(&req->n, nest);
+ nl_attr_put(&req->n, datalen, TCA_KIND, kind_str,
+ strlen(kind_str) + 1);
+
+ nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
+
+ switch (dplane_ctx_tc_qdisc_get_kind(ctx)) {
+ case TC_QDISC_HTB: {
+ struct tc_htb_glob htb_glob = {
+ .rate2quantum = 10,
+ .version = 3,
+ .defcls = TC_MINOR_NOCLASS};
+ nl_attr_put(&req->n, datalen, TCA_HTB_INIT, &htb_glob,
+ sizeof(htb_glob));
+ break;
+ }
+ case TC_QDISC_NOQUEUE:
+ break;
+ default:
+ break;
+ /* not implemented */
+ }
+
+ nl_attr_nest_end(&req->n, nest);
+ } else {
+ /* ifindex are enough for del/get qdisc */
+ }
return NLMSG_ALIGN(req->n.nlmsg_len);
}
@@ -241,17 +238,10 @@ static ssize_t netlink_qdisc_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
static ssize_t netlink_tclass_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
void *data, size_t datalen)
{
- struct nlsock *nl;
- struct tc_htb_opt htb_opt = {};
+ enum dplane_op_e op = dplane_ctx_get_op(ctx);
- uint64_t rate, ceil;
- uint64_t buffer, cbuffer;
-
- /* TODO: fetch mtu from interface */
- uint32_t mtu = 0;
-
- uint32_t rtab[256];
- uint32_t ctab[256];
+ struct nlsock *nl;
+ const char *kind_str = NULL;
struct rtattr *nest;
@@ -271,73 +261,239 @@ static ssize_t netlink_tclass_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
+ if (op == DPLANE_OP_TC_CLASS_UPDATE)
+ req->n.nlmsg_flags |= NLM_F_REPLACE;
+
req->n.nlmsg_type = cmd;
req->n.nlmsg_pid = nl->snl.nl_pid;
req->t.tcm_family = AF_UNSPEC;
req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx);
- req->t.tcm_handle = tc_get_handle(ctx, 1);
- req->t.tcm_parent = tc_get_handle(ctx, 0);
- rate = dplane_ctx_tc_get_rate(ctx);
- ceil = dplane_ctx_tc_get_ceil(ctx);
+ req->t.tcm_handle = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA,
+ dplane_ctx_tc_class_get_handle(ctx));
+ req->t.tcm_parent = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0);
+ req->t.tcm_info = 0;
+
+ kind_str = dplane_ctx_tc_class_get_kind_str(ctx);
+
+ if (op == DPLANE_OP_TC_CLASS_ADD || op == DPLANE_OP_TC_CLASS_UPDATE) {
+ zlog_debug("netlink tclass encoder: op: %s kind: %s handle: %u",
+ op == DPLANE_OP_TC_CLASS_UPDATE ? "update" : "add",
+ kind_str, dplane_ctx_tc_class_get_handle(ctx));
+
+ nl_attr_put(&req->n, datalen, TCA_KIND, kind_str,
+ strlen(kind_str) + 1);
+
+ nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
+
+ switch (dplane_ctx_tc_class_get_kind(ctx)) {
+ case TC_QDISC_HTB: {
+ struct tc_htb_opt htb_opt = {};
+
+ uint64_t rate = dplane_ctx_tc_class_get_rate(ctx),
+ ceil = dplane_ctx_tc_class_get_ceil(ctx);
+
+ uint64_t buffer, cbuffer;
+
+ /* TODO: fetch mtu from interface */
+ uint32_t mtu = 1500;
+
+ uint32_t rtab[256];
+ uint32_t ctab[256];
+
+ ceil = MAX(rate, ceil);
+
+ htb_opt.rate.rate = (rate >> 32 != 0) ? ~0U : rate;
+ htb_opt.ceil.rate = (ceil >> 32 != 0) ? ~0U : ceil;
+
+ buffer = rate / tc_get_freq() + mtu;
+ cbuffer = ceil / tc_get_freq() + mtu;
+
+ htb_opt.buffer = buffer;
+ htb_opt.cbuffer = cbuffer;
+
+ tc_calc_rate_table(&htb_opt.rate, rtab, mtu);
+ tc_calc_rate_table(&htb_opt.ceil, ctab, mtu);
+
+ htb_opt.ceil.mpu = htb_opt.rate.mpu = 0;
+ htb_opt.ceil.overhead = htb_opt.rate.overhead = 0;
+
+ if (rate >> 32 != 0) {
+ nl_attr_put(&req->n, datalen, TCA_HTB_RATE64,
+ &rate, sizeof(rate));
+ }
+
+ if (ceil >> 32 != 0) {
+ nl_attr_put(&req->n, datalen, TCA_HTB_CEIL64,
+ &ceil, sizeof(ceil));
+ }
+
+ nl_attr_put(&req->n, datalen, TCA_HTB_PARMS, &htb_opt,
+ sizeof(htb_opt));
+
+ nl_attr_put(&req->n, datalen, TCA_HTB_RTAB, rtab,
+ sizeof(rtab));
+ nl_attr_put(&req->n, datalen, TCA_HTB_CTAB, ctab,
+ sizeof(ctab));
+ break;
+ }
+ default:
+ break;
+ }
+
+ nl_attr_nest_end(&req->n, nest);
+ }
+
+ return NLMSG_ALIGN(req->n.nlmsg_len);
+}
+
+static int netlink_tfilter_flower_port_type(uint8_t ip_proto, bool src)
+{
+ if (ip_proto == IPPROTO_TCP)
+ return src ? TCA_FLOWER_KEY_TCP_SRC : TCA_FLOWER_KEY_TCP_DST;
+ else if (ip_proto == IPPROTO_UDP)
+ return src ? TCA_FLOWER_KEY_UDP_SRC : TCA_FLOWER_KEY_UDP_DST;
+ else if (ip_proto == IPPROTO_SCTP)
+ return src ? TCA_FLOWER_KEY_SCTP_SRC : TCA_FLOWER_KEY_SCTP_DST;
+ else
+ return -1;
+}
+
+static void netlink_tfilter_flower_put_options(struct nlmsghdr *n,
+ size_t datalen,
+ struct zebra_dplane_ctx *ctx)
+{
+ struct inet_prefix addr;
+ uint32_t flags = 0, classid;
+ uint8_t protocol = htons(dplane_ctx_tc_filter_get_eth_proto(ctx));
+ uint32_t filter_bm = dplane_ctx_tc_filter_get_filter_bm(ctx);
+
+ if (filter_bm & TC_FLOWER_SRC_IP) {
+ const struct prefix *src_p =
+ dplane_ctx_tc_filter_get_src_ip(ctx);
- ceil = ceil < rate ? rate : ceil;
+ if (tc_flower_get_inet_prefix(src_p, &addr) != 0)
+ return;
+
+ nl_attr_put(n, datalen,
+ (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_SRC
+ : TCA_FLOWER_KEY_IPV6_SRC,
+ addr.data, addr.bytelen);
- htb_opt.rate.rate = (rate >> 32 != 0) ? ~0U : rate;
- htb_opt.ceil.rate = (ceil >> 32 != 0) ? ~0U : ceil;
+ if (tc_flower_get_inet_mask(src_p, &addr) != 0)
+ return;
- buffer = rate / tc_get_freq(), cbuffer = ceil / tc_get_freq();
+ nl_attr_put(n, datalen,
+ (addr.family == AF_INET)
+ ? TCA_FLOWER_KEY_IPV4_SRC_MASK
+ : TCA_FLOWER_KEY_IPV6_SRC_MASK,
+ addr.data, addr.bytelen);
+ }
- htb_opt.buffer = buffer;
- htb_opt.cbuffer = cbuffer;
+ if (filter_bm & TC_FLOWER_DST_IP) {
+ const struct prefix *dst_p =
+ dplane_ctx_tc_filter_get_dst_ip(ctx);
- tc_calc_rate_table(&htb_opt.rate, rtab, mtu);
- tc_calc_rate_table(&htb_opt.ceil, ctab, mtu);
+ if (tc_flower_get_inet_prefix(dst_p, &addr) != 0)
+ return;
- htb_opt.ceil.mpu = htb_opt.rate.mpu = 0;
- htb_opt.ceil.overhead = htb_opt.rate.overhead = 0;
+ nl_attr_put(n, datalen,
+ (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_DST
+ : TCA_FLOWER_KEY_IPV6_DST,
+ addr.data, addr.bytelen);
- nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
+ if (tc_flower_get_inet_mask(dst_p, &addr) != 0)
+ return;
- if (rate >> 32 != 0) {
- nl_attr_put(&req->n, datalen, TCA_HTB_CEIL64, &rate,
- sizeof(rate));
+ nl_attr_put(n, datalen,
+ (addr.family == AF_INET)
+ ? TCA_FLOWER_KEY_IPV4_DST_MASK
+ : TCA_FLOWER_KEY_IPV6_DST_MASK,
+ addr.data, addr.bytelen);
}
- if (ceil >> 32 != 0) {
- nl_attr_put(&req->n, datalen, TCA_HTB_CEIL64, &ceil,
- sizeof(ceil));
+ if (filter_bm & TC_FLOWER_IP_PROTOCOL) {
+ nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_PROTO,
+ dplane_ctx_tc_filter_get_ip_proto(ctx));
}
- nl_attr_put(&req->n, datalen, TCA_HTB_PARMS, &htb_opt, sizeof(htb_opt));
+ if (filter_bm & TC_FLOWER_SRC_PORT) {
+ uint16_t min, max;
- nl_attr_put(&req->n, datalen, TCA_HTB_RTAB, rtab, sizeof(rtab));
- nl_attr_put(&req->n, datalen, TCA_HTB_CTAB, ctab, sizeof(ctab));
- nl_attr_nest_end(&req->n, nest);
+ min = dplane_ctx_tc_filter_get_src_port_min(ctx);
+ max = dplane_ctx_tc_filter_get_src_port_max(ctx);
- return NLMSG_ALIGN(req->n.nlmsg_len);
+ if (max > min) {
+ nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_SRC_MIN,
+ htons(min));
+
+ nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_SRC_MAX,
+ htons(max));
+ } else {
+ int type = netlink_tfilter_flower_port_type(
+ dplane_ctx_tc_filter_get_ip_proto(ctx), true);
+
+ if (type < 0)
+ return;
+
+ nl_attr_put16(n, datalen, type, htons(min));
+ }
+ }
+
+ if (filter_bm & TC_FLOWER_DST_PORT) {
+ uint16_t min = dplane_ctx_tc_filter_get_dst_port_min(ctx),
+ max = dplane_ctx_tc_filter_get_dst_port_max(ctx);
+
+ if (max > min) {
+ nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_DST_MIN,
+ htons(min));
+
+ nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_DST_MAX,
+ htons(max));
+ } else {
+ int type = netlink_tfilter_flower_port_type(
+ dplane_ctx_tc_filter_get_ip_proto(ctx), false);
+
+ if (type < 0)
+ return;
+
+ nl_attr_put16(n, datalen, type, htons(min));
+ }
+ }
+
+ if (filter_bm & TC_FLOWER_DSFIELD) {
+ nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_TOS,
+ dplane_ctx_tc_filter_get_dsfield(ctx));
+ nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_TOS_MASK,
+ dplane_ctx_tc_filter_get_dsfield_mask(ctx));
+ }
+
+ classid = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA,
+ dplane_ctx_tc_filter_get_classid(ctx));
+ nl_attr_put32(n, datalen, TCA_FLOWER_CLASSID, classid);
+
+ nl_attr_put32(n, datalen, TCA_FLOWER_FLAGS, flags);
+
+ nl_attr_put16(n, datalen, TCA_FLOWER_KEY_ETH_TYPE, protocol);
}
/*
- * Traffic control filter encoding (only "flower" supported)
+ * Traffic control filter encoding
*/
static ssize_t netlink_tfilter_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
void *data, size_t datalen)
{
+ enum dplane_op_e op = dplane_ctx_get_op(ctx);
+
struct nlsock *nl;
- struct rtattr *nest;
+ const char *kind_str = NULL;
- const char *kind = "flower";
+ struct rtattr *nest;
uint16_t priority;
uint16_t protocol;
- uint32_t classid;
- uint32_t filter_bm;
- uint32_t flags = 0;
-
- struct inet_prefix addr;
struct {
struct nlmsghdr n;
@@ -355,7 +511,8 @@ static ssize_t netlink_tfilter_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
- req->n.nlmsg_flags |= NLM_F_EXCL;
+ if (op == DPLANE_OP_TC_FILTER_UPDATE)
+ req->n.nlmsg_flags |= NLM_F_REPLACE;
req->n.nlmsg_type = cmd;
@@ -364,105 +521,361 @@ static ssize_t netlink_tfilter_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
req->t.tcm_family = AF_UNSPEC;
req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx);
- /* TODO: priority and layer-3 protocol support */
- priority = 0;
- protocol = htons(ETH_P_IP);
- classid = tc_get_handle(ctx, 1);
- filter_bm = dplane_ctx_tc_get_filter_bm(ctx);
+ priority = dplane_ctx_tc_filter_get_priority(ctx);
+ protocol = htons(dplane_ctx_tc_filter_get_eth_proto(ctx));
+
+ req->t.tcm_info = TC_H_MAKE(priority << 16, protocol);
+ req->t.tcm_handle = dplane_ctx_tc_filter_get_handle(ctx);
+ req->t.tcm_parent = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0);
+
+ kind_str = dplane_ctx_tc_filter_get_kind_str(ctx);
+
+ if (op == DPLANE_OP_TC_FILTER_ADD || op == DPLANE_OP_TC_FILTER_UPDATE) {
+ nl_attr_put(&req->n, datalen, TCA_KIND, kind_str,
+ strlen(kind_str) + 1);
+
+ zlog_debug(
+ "netlink tfilter encoder: op: %s priority: %u protocol: %u kind: %s handle: %u filter_bm: %u ip_proto: %u",
+ op == DPLANE_OP_TC_FILTER_UPDATE ? "update" : "add",
+ priority, protocol, kind_str,
+ dplane_ctx_tc_filter_get_handle(ctx),
+ dplane_ctx_tc_filter_get_filter_bm(ctx),
+ dplane_ctx_tc_filter_get_ip_proto(ctx));
+
+ nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
+ switch (dplane_ctx_tc_filter_get_kind(ctx)) {
+ case TC_FILTER_FLOWER: {
+ netlink_tfilter_flower_put_options(&req->n, datalen,
+ ctx);
+ break;
+ }
+ default:
+ break;
+ }
+ nl_attr_nest_end(&req->n, nest);
+ }
- req->t.tcm_info = tc_make_handle(priority, protocol);
+ return NLMSG_ALIGN(req->n.nlmsg_len);
+}
- req->t.tcm_handle = 1;
- req->t.tcm_parent = tc_get_handle(ctx, 0);
+static ssize_t netlink_newqdisc_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_qdisc_msg_encode(RTM_NEWQDISC, ctx, buf, buflen);
+}
- nl_attr_put(&req->n, datalen, TCA_KIND, kind, strlen(kind) + 1);
- nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
+static ssize_t netlink_delqdisc_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_qdisc_msg_encode(RTM_DELQDISC, ctx, buf, buflen);
+}
- nl_attr_put(&req->n, datalen, TCA_FLOWER_CLASSID, &classid,
- sizeof(classid));
+static ssize_t netlink_newtclass_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_tclass_msg_encode(RTM_NEWTCLASS, ctx, buf, buflen);
+}
- if (filter_bm & TC_FILTER_SRC_IP) {
- const struct prefix *src_p = dplane_ctx_tc_get_src_ip(ctx);
+static ssize_t netlink_deltclass_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_tclass_msg_encode(RTM_DELTCLASS, ctx, buf, buflen);
+}
- if (tc_flower_get_inet_prefix(src_p, &addr) != 0)
- return 0;
+static ssize_t netlink_newtfilter_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_tfilter_msg_encode(RTM_NEWTFILTER, ctx, buf, buflen);
+}
- nl_attr_put(&req->n, datalen,
- (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_SRC
- : TCA_FLOWER_KEY_IPV6_SRC,
- addr.data, addr.bytelen);
+static ssize_t netlink_deltfilter_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_tfilter_msg_encode(RTM_DELTFILTER, ctx, buf, buflen);
+}
- if (tc_flower_get_inet_mask(src_p, &addr) != 0)
- return 0;
+enum netlink_msg_status
+netlink_put_tc_qdisc_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
+ enum dplane_op_e op;
+ enum netlink_msg_status ret;
- nl_attr_put(&req->n, datalen,
- (addr.family == AF_INET)
- ? TCA_FLOWER_KEY_IPV4_SRC_MASK
- : TCA_FLOWER_KEY_IPV6_SRC_MASK,
- addr.data, addr.bytelen);
+ op = dplane_ctx_get_op(ctx);
+
+ if (op == DPLANE_OP_TC_QDISC_INSTALL) {
+ ret = netlink_batch_add_msg(
+ bth, ctx, netlink_newqdisc_msg_encoder, false);
+ } else if (op == DPLANE_OP_TC_QDISC_UNINSTALL) {
+ ret = netlink_batch_add_msg(
+ bth, ctx, netlink_delqdisc_msg_encoder, false);
+ } else {
+ return FRR_NETLINK_ERROR;
}
- if (filter_bm & TC_FILTER_DST_IP) {
- const struct prefix *dst_p = dplane_ctx_tc_get_dst_ip(ctx);
+ return ret;
+}
- if (tc_flower_get_inet_prefix(dst_p, &addr) != 0)
- return 0;
+enum netlink_msg_status
+netlink_put_tc_class_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
+ enum dplane_op_e op;
+ enum netlink_msg_status ret;
- nl_attr_put(&req->n, datalen,
- (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_DST
- : TCA_FLOWER_KEY_IPV6_DST,
- addr.data, addr.bytelen);
+ op = dplane_ctx_get_op(ctx);
- if (tc_flower_get_inet_mask(dst_p, &addr) != 0)
- return 0;
+ if (op == DPLANE_OP_TC_CLASS_ADD || op == DPLANE_OP_TC_CLASS_UPDATE) {
+ ret = netlink_batch_add_msg(
+ bth, ctx, netlink_newtclass_msg_encoder, false);
+ } else if (op == DPLANE_OP_TC_CLASS_DELETE) {
+ ret = netlink_batch_add_msg(
+ bth, ctx, netlink_deltclass_msg_encoder, false);
+ } else {
+ return FRR_NETLINK_ERROR;
+ }
- nl_attr_put(&req->n, datalen,
- (addr.family == AF_INET)
- ? TCA_FLOWER_KEY_IPV4_DST_MASK
- : TCA_FLOWER_KEY_IPV6_DST_MASK,
- addr.data, addr.bytelen);
+ return ret;
+}
+
+enum netlink_msg_status
+netlink_put_tc_filter_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
+ enum dplane_op_e op;
+ enum netlink_msg_status ret;
+
+ op = dplane_ctx_get_op(ctx);
+
+ if (op == DPLANE_OP_TC_FILTER_ADD) {
+ ret = netlink_batch_add_msg(
+ bth, ctx, netlink_newtfilter_msg_encoder, false);
+ } else if (op == DPLANE_OP_TC_FILTER_UPDATE) {
+ /*
+ * Replace will fail if either filter type or the number of
+ * filter options is changed, so DEL then NEW
+ *
+ * TFILTER may have refs to TCLASS.
+ */
+
+ (void)netlink_batch_add_msg(
+ bth, ctx, netlink_deltfilter_msg_encoder, false);
+ ret = netlink_batch_add_msg(
+ bth, ctx, netlink_newtfilter_msg_encoder, false);
+ } else if (op == DPLANE_OP_TC_FILTER_DELETE) {
+ ret = netlink_batch_add_msg(
+ bth, ctx, netlink_deltfilter_msg_encoder, false);
+ } else {
+ return FRR_NETLINK_ERROR;
}
- if (filter_bm & TC_FILTER_IP_PROTOCOL) {
- nl_attr_put8(&req->n, datalen, TCA_FLOWER_KEY_IP_PROTO,
- dplane_ctx_tc_get_ip_proto(ctx));
+ return ret;
+}
+
+/*
+ * Request filters from the kernel
+ */
+static int netlink_request_filters(struct zebra_ns *zns, int family, int type,
+ ifindex_t ifindex)
+{
+ struct {
+ struct nlmsghdr n;
+ struct tcmsg tc;
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_type = type;
+ req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
+ req.tc.tcm_family = family;
+ req.tc.tcm_ifindex = ifindex;
+
+ return netlink_request(&zns->netlink_cmd, &req);
+}
+
+/*
+ * Request queue discipline from the kernel
+ */
+static int netlink_request_qdiscs(struct zebra_ns *zns, int family, int type)
+{
+ struct {
+ struct nlmsghdr n;
+ struct tcmsg tc;
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_type = type;
+ req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
+ req.tc.tcm_family = family;
+
+ return netlink_request(&zns->netlink_cmd, &req);
+}
+
+int netlink_qdisc_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
+{
+ struct tcmsg *tcm;
+ struct zebra_tc_qdisc qdisc = {};
+
+ int len;
+ struct rtattr *tb[TCA_MAX + 1];
+
+ frrtrace(3, frr_zebra, netlink_tc_qdisc_change, h, ns_id, startup);
+
+ len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg));
+
+ if (len < 0) {
+ zlog_err(
+ "%s: Message received from netlink is of a broken size %d %zu",
+ __func__, h->nlmsg_len,
+ (size_t)NLMSG_LENGTH(sizeof(struct tcmsg)));
+ return -1;
}
- nl_attr_put32(&req->n, datalen, TCA_FLOWER_FLAGS, flags);
+ tcm = NLMSG_DATA(h);
+ netlink_parse_rtattr(tb, TCA_MAX, TCA_RTA(tcm), len);
- nl_attr_put16(&req->n, datalen, TCA_FLOWER_KEY_ETH_TYPE, protocol);
- nl_attr_nest_end(&req->n, nest);
+ const char *kind_str = (const char *)RTA_DATA(tb[TCA_KIND]);
- return NLMSG_ALIGN(req->n.nlmsg_len);
+ enum tc_qdisc_kind kind = tc_qdisc_str2kind(kind_str);
+
+ qdisc.qdisc.ifindex = tcm->tcm_ifindex;
+
+ switch (kind) {
+ case TC_QDISC_NOQUEUE:
+ /* "noqueue" is the default qdisc */
+ break;
+ default:
+ break;
+ }
+
+ if (tb[TCA_OPTIONS] != NULL) {
+ struct rtattr *options[TCA_HTB_MAX + 1];
+
+ netlink_parse_rtattr_nested(options, TCA_HTB_MAX,
+ tb[TCA_OPTIONS]);
+
+ /* TODO: more details */
+ /* struct tc_htb_glob *glob = RTA_DATA(options[TCA_HTB_INIT]);
+ */
+ }
+
+ if (h->nlmsg_type == RTM_NEWQDISC) {
+ if (startup &&
+ TC_H_MAJ(tcm->tcm_handle) == TC_QDISC_MAJOR_ZEBRA) {
+ enum zebra_dplane_result ret;
+
+ ret = dplane_tc_qdisc_uninstall(&qdisc);
+
+ zlog_debug("%s: %s leftover qdisc: ifindex %d kind %s",
+ __func__,
+ ((ret == ZEBRA_DPLANE_REQUEST_FAILURE)
+ ? "Failed to remove"
+ : "Removed"),
+ qdisc.qdisc.ifindex, kind_str);
+ }
+ }
+
+ return 0;
}
-static ssize_t netlink_newqdisc_msg_encoder(struct zebra_dplane_ctx *ctx,
- void *buf, size_t buflen)
+int netlink_tclass_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
- return netlink_qdisc_msg_encode(RTM_NEWQDISC, ctx, buf, buflen);
+ struct tcmsg *tcm;
+
+ int len;
+ struct rtattr *tb[TCA_MAX + 1];
+
+ frrtrace(3, frr_zebra, netlink_tc_class_change, h, ns_id, startup);
+
+ len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg));
+
+ if (len < 0) {
+ zlog_err(
+ "%s: Message received from netlink is of a broken size %d %zu",
+ __func__, h->nlmsg_len,
+ (size_t)NLMSG_LENGTH(sizeof(struct tcmsg)));
+ return -1;
+ }
+
+ tcm = NLMSG_DATA(h);
+ netlink_parse_rtattr(tb, TCA_MAX, TCA_RTA(tcm), len);
+
+
+ if (tb[TCA_OPTIONS] != NULL) {
+ struct rtattr *options[TCA_HTB_MAX + 1];
+
+ netlink_parse_rtattr_nested(options, TCA_HTB_MAX,
+ tb[TCA_OPTIONS]);
+
+ /* TODO: more details */
+ /* struct tc_htb_opt *opt = RTA_DATA(options[TCA_HTB_PARMS]); */
+ }
+
+ return 0;
}
-static ssize_t netlink_newtclass_msg_encoder(struct zebra_dplane_ctx *ctx,
- void *buf, size_t buflen)
+int netlink_tfilter_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
- return netlink_tclass_msg_encode(RTM_NEWTCLASS, ctx, buf, buflen);
+ struct tcmsg *tcm;
+
+ int len;
+ struct rtattr *tb[TCA_MAX + 1];
+
+ frrtrace(3, frr_zebra, netlink_tc_filter_change, h, ns_id, startup);
+
+ len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg));
+
+ if (len < 0) {
+ zlog_err(
+ "%s: Message received from netlink is of a broken size %d %zu",
+ __func__, h->nlmsg_len,
+ (size_t)NLMSG_LENGTH(sizeof(struct tcmsg)));
+ return -1;
+ }
+
+ tcm = NLMSG_DATA(h);
+ netlink_parse_rtattr(tb, TCA_MAX, TCA_RTA(tcm), len);
+
+ return 0;
}
-static ssize_t netlink_newtfilter_msg_encoder(struct zebra_dplane_ctx *ctx,
- void *buf, size_t buflen)
+int netlink_qdisc_read(struct zebra_ns *zns)
{
- return netlink_tfilter_msg_encode(RTM_NEWTFILTER, ctx, buf, buflen);
+ int ret;
+ struct zebra_dplane_info dp_info;
+
+ zebra_dplane_info_from_zns(&dp_info, zns, true);
+
+ ret = netlink_request_qdiscs(zns, AF_UNSPEC, RTM_GETQDISC);
+ if (ret < 0)
+ return ret;
+
+ ret = netlink_parse_info(netlink_qdisc_change, &zns->netlink_cmd,
+ &dp_info, 0, true);
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
-enum netlink_msg_status netlink_put_tc_update_msg(struct nl_batch *bth,
- struct zebra_dplane_ctx *ctx)
+int netlink_tfilter_read_for_interface(struct zebra_ns *zns, ifindex_t ifindex)
{
- /* TODO: error handling and other actions (delete, replace, ...) */
+ int ret;
+ struct zebra_dplane_info dp_info;
+
+ zebra_dplane_info_from_zns(&dp_info, zns, true);
- netlink_batch_add_msg(bth, ctx, netlink_newqdisc_msg_encoder, false);
- netlink_batch_add_msg(bth, ctx, netlink_newtclass_msg_encoder, false);
- return netlink_batch_add_msg(bth, ctx, netlink_newtfilter_msg_encoder,
- false);
+ ret = netlink_request_filters(zns, AF_UNSPEC, RTM_GETTFILTER, ifindex);
+ if (ret < 0)
+ return ret;
+
+ ret = netlink_parse_info(netlink_tfilter_change, &zns->netlink_cmd,
+ &dp_info, 0, true);
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
#endif /* HAVE_NETLINK */
diff --git a/zebra/tc_netlink.h b/zebra/tc_netlink.h
index 2190bca4f9..166bd917c6 100644
--- a/zebra/tc_netlink.h
+++ b/zebra/tc_netlink.h
@@ -3,22 +3,19 @@
*
* Copyright (C) 2022 Shichu Yang
*
- * This file is part of FRR.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
*
- * FRR is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
*
- * FRR is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with FRR; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _ZEBRA_TC_NETLINK_H
@@ -51,7 +48,30 @@ enum {
};
extern enum netlink_msg_status
-netlink_put_tc_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+netlink_put_tc_qdisc_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_tc_class_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_tc_filter_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+
+/**
+ * "filter" & "class" in the following become "tfilter" & "tclass" for
+ * the sake of consistency with kernel message types (RTM_NEWTFILTER etc.)
+ */
+
+extern int netlink_qdisc_read(struct zebra_ns *zns);
+extern int netlink_tfilter_read_for_interface(struct zebra_ns *zns,
+ ifindex_t ifindex);
+
+extern int netlink_tfilter_change(struct nlmsghdr *h, ns_id_t ns_id,
+ int startup);
+extern int netlink_tclass_change(struct nlmsghdr *h, ns_id_t ns_id,
+ int startup);
+extern int netlink_qdisc_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
+
#ifdef __cplusplus
}
diff --git a/zebra/tc_socket.c b/zebra/tc_socket.c
index 0bf9e487b0..17373c7af7 100644
--- a/zebra/tc_socket.c
+++ b/zebra/tc_socket.c
@@ -3,22 +3,19 @@
*
* Copyright (C) 2022 Shichu Yang
*
- * This file is part of FRR.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
*
- * FRR is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
*
- * FRR is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with FRR; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
@@ -30,6 +27,7 @@
#include "zebra/rt.h"
#include "zebra/zebra_dplane.h"
#include "zebra/zebra_errors.h"
+#include "zebra/zebra_tc.h"
enum zebra_dplane_result kernel_tc_update(struct zebra_dplane_ctx *ctx)
{
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index 130fb972db..d594512b36 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -53,6 +53,7 @@
#include "zebra/zebra_evpn_mh.h"
#include "zebra/rt.h"
#include "zebra/zebra_pbr.h"
+#include "zebra/zebra_tc.h"
#include "zebra/table_manager.h"
#include "zebra/zapi_msg.h"
#include "zebra/zebra_errors.h"
@@ -3272,6 +3273,171 @@ stream_failure:
return;
}
+static inline void zread_tc_qdisc(ZAPI_HANDLER_ARGS)
+{
+ struct zebra_tc_qdisc qdisc;
+ struct stream *s;
+ uint32_t total, i;
+
+ s = msg;
+ STREAM_GETL(s, total);
+
+ for (i = 0; i < total; i++) {
+ memset(&qdisc, 0, sizeof(qdisc));
+
+ qdisc.sock = client->sock;
+ STREAM_GETL(s, qdisc.qdisc.ifindex);
+ STREAM_GETL(s, qdisc.qdisc.kind);
+
+ if (hdr->command == ZEBRA_TC_QDISC_INSTALL)
+ zebra_tc_qdisc_install(&qdisc);
+ else
+ zebra_tc_qdisc_uninstall(&qdisc);
+ }
+
+stream_failure:
+ return;
+}
+
+static inline void zread_tc_class(ZAPI_HANDLER_ARGS)
+{
+ struct zebra_tc_class class;
+ struct stream *s;
+ uint32_t total, i;
+
+ s = msg;
+ STREAM_GETL(s, total);
+
+ for (i = 0; i < total; i++) {
+ memset(&class, 0, sizeof(class));
+
+ class.sock = client->sock;
+ STREAM_GETL(s, class.class.ifindex);
+ STREAM_GETL(s, class.class.handle);
+ STREAM_GETL(s, class.class.kind);
+ STREAM_GETQ(s, class.class.u.htb.rate);
+ STREAM_GETQ(s, class.class.u.htb.ceil);
+
+ if (hdr->command == ZEBRA_TC_CLASS_ADD)
+ zebra_tc_class_add(&class);
+ else
+ zebra_tc_class_delete(&class);
+ }
+
+stream_failure:
+ return;
+}
+
+static inline void zread_tc_filter(ZAPI_HANDLER_ARGS)
+{
+ struct zebra_tc_filter filter;
+ struct stream *s;
+ uint32_t total, i;
+
+ s = msg;
+ STREAM_GETL(s, total);
+
+ for (i = 0; i < total; i++) {
+ memset(&filter, 0, sizeof(filter));
+
+ filter.sock = client->sock;
+ STREAM_GETL(s, filter.filter.ifindex);
+ STREAM_GETL(s, filter.filter.handle);
+ STREAM_GETL(s, filter.filter.priority);
+ STREAM_GETL(s, filter.filter.protocol);
+ STREAM_GETL(s, filter.filter.kind);
+ switch (filter.filter.kind) {
+ case TC_FILTER_FLOWER: {
+ STREAM_GETL(s, filter.filter.u.flower.filter_bm);
+ uint32_t filter_bm = filter.filter.u.flower.filter_bm;
+
+ if (filter_bm & TC_FLOWER_IP_PROTOCOL)
+ STREAM_GETC(s, filter.filter.u.flower.ip_proto);
+ if (filter_bm & TC_FLOWER_SRC_IP) {
+ STREAM_GETC(
+ s,
+ filter.filter.u.flower.src_ip.family);
+ STREAM_GETC(s, filter.filter.u.flower.src_ip
+ .prefixlen);
+ STREAM_GET(
+ &filter.filter.u.flower.src_ip.u.prefix,
+ s,
+ prefix_blen(&filter.filter.u.flower
+ .src_ip));
+
+ if (!(filter.filter.u.flower.src_ip.family ==
+ AF_INET ||
+ filter.filter.u.flower.src_ip.family ==
+ AF_INET6)) {
+ zlog_warn(
+ "Unsupported TC source IP family: %s (%hhu)",
+ family2str(
+ filter.filter.u.flower
+ .src_ip.family),
+ filter.filter.u.flower.src_ip
+ .family);
+ return;
+ }
+ }
+ if (filter_bm & TC_FLOWER_SRC_PORT) {
+ STREAM_GETW(
+ s, filter.filter.u.flower.src_port_min);
+ STREAM_GETW(
+ s, filter.filter.u.flower.src_port_max);
+ }
+ if (filter_bm & TC_FLOWER_DST_IP) {
+ STREAM_GETC(
+ s,
+ filter.filter.u.flower.dst_ip.family);
+ STREAM_GETC(s, filter.filter.u.flower.dst_ip
+ .prefixlen);
+ STREAM_GET(
+ &filter.filter.u.flower.dst_ip.u.prefix,
+ s,
+ prefix_blen(&filter.filter.u.flower
+ .dst_ip));
+ if (!(filter.filter.u.flower.dst_ip.family ==
+ AF_INET ||
+ filter.filter.u.flower.dst_ip.family ==
+ AF_INET6)) {
+ zlog_warn(
+ "Unsupported TC destination IP family: %s (%hhu)",
+ family2str(
+ filter.filter.u.flower
+ .dst_ip.family),
+ filter.filter.u.flower.dst_ip
+ .family);
+ return;
+ }
+ }
+ if (filter_bm & TC_FLOWER_DST_PORT) {
+ STREAM_GETW(
+ s, filter.filter.u.flower.dst_port_min);
+ STREAM_GETW(
+ s, filter.filter.u.flower.dst_port_max);
+ }
+ if (filter_bm & TC_FLOWER_DSFIELD) {
+ STREAM_GETC(s, filter.filter.u.flower.dsfield);
+ STREAM_GETC(
+ s, filter.filter.u.flower.dsfield_mask);
+ }
+ STREAM_GETL(s, filter.filter.u.flower.classid);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (hdr->command == ZEBRA_TC_FILTER_ADD)
+ zebra_tc_filter_add(&filter);
+ else
+ zebra_tc_filter_delete(&filter);
+ }
+
+stream_failure:
+ return;
+}
+
static inline void zread_ipset(ZAPI_HANDLER_ARGS)
{
struct zebra_pbr_ipset zpi;
@@ -3772,6 +3938,12 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
[ZEBRA_CONFIGURE_ARP] = zebra_configure_arp,
[ZEBRA_GRE_GET] = zebra_gre_get,
[ZEBRA_GRE_SOURCE_SET] = zebra_gre_source_set,
+ [ZEBRA_TC_QDISC_INSTALL] = zread_tc_qdisc,
+ [ZEBRA_TC_QDISC_UNINSTALL] = zread_tc_qdisc,
+ [ZEBRA_TC_CLASS_ADD] = zread_tc_class,
+ [ZEBRA_TC_CLASS_DELETE] = zread_tc_class,
+ [ZEBRA_TC_FILTER_ADD] = zread_tc_filter,
+ [ZEBRA_TC_FILTER_DELETE] = zread_tc_filter,
};
/*
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 6a691a222f..6b9005d645 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -37,6 +37,7 @@
#include "zebra/debug.h"
#include "zebra/zebra_pbr.h"
#include "zebra/zebra_neigh.h"
+#include "zebra/zebra_tc.h"
#include "printfrr.h"
/* Memory types */
@@ -313,23 +314,36 @@ struct dplane_netconf_info {
enum dplane_netconf_status_e linkdown_val;
};
-/*
- * Traffic control contexts for the dplane
- */
-struct dplane_tc_info {
- /* Rate spec (unit: Bytes/s) */
+struct dplane_tc_qdisc_info {
+ enum tc_qdisc_kind kind;
+ const char *kind_str;
+};
+
+struct dplane_tc_class_info {
+ uint32_t handle;
+ enum tc_qdisc_kind kind;
+ const char *kind_str;
uint64_t rate;
uint64_t ceil;
+};
- /* TODO: custom burst */
-
- /* Filter components for "tfilter" */
+struct dplane_tc_filter_info {
+ uint32_t handle;
+ uint16_t priority;
+ enum tc_filter_kind kind;
+ const char *kind_str;
uint32_t filter_bm;
+ uint16_t eth_proto;
+ uint8_t ip_proto;
struct prefix src_ip;
struct prefix dst_ip;
- uint8_t ip_proto;
-
- /* TODO: more filter components */
+ uint16_t src_port_min;
+ uint16_t src_port_max;
+ uint16_t dst_port_min;
+ uint16_t dst_port_max;
+ uint8_t dsfield;
+ uint8_t dsfield_mask;
+ uint32_t classid;
};
/*
@@ -381,7 +395,9 @@ struct zebra_dplane_ctx {
struct dplane_mac_info macinfo;
struct dplane_neigh_info neigh;
struct dplane_rule_info rule;
- struct dplane_tc_info tc;
+ struct dplane_tc_qdisc_info tc_qdisc;
+ struct dplane_tc_class_info tc_class;
+ struct dplane_tc_filter_info tc_filter;
struct zebra_pbr_iptable iptable;
struct zebra_pbr_ipset ipset;
struct {
@@ -800,9 +816,14 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_INTF_INSTALL:
case DPLANE_OP_INTF_UPDATE:
case DPLANE_OP_INTF_DELETE:
- case DPLANE_OP_TC_INSTALL:
- case DPLANE_OP_TC_UPDATE:
- case DPLANE_OP_TC_DELETE:
+ case DPLANE_OP_TC_QDISC_INSTALL:
+ case DPLANE_OP_TC_QDISC_UNINSTALL:
+ case DPLANE_OP_TC_CLASS_ADD:
+ case DPLANE_OP_TC_CLASS_DELETE:
+ case DPLANE_OP_TC_CLASS_UPDATE:
+ case DPLANE_OP_TC_FILTER_ADD:
+ case DPLANE_OP_TC_FILTER_DELETE:
+ case DPLANE_OP_TC_FILTER_UPDATE:
break;
case DPLANE_OP_IPSET_ENTRY_ADD:
@@ -1127,14 +1148,29 @@ const char *dplane_op2str(enum dplane_op_e op)
ret = "INTF_DELETE";
break;
- case DPLANE_OP_TC_INSTALL:
- ret = "TC_INSTALL";
+ case DPLANE_OP_TC_QDISC_INSTALL:
+ ret = "TC_QDISC_INSTALL";
+ break;
+ case DPLANE_OP_TC_QDISC_UNINSTALL:
+ ret = "TC_QDISC_UNINSTALL";
+ break;
+ case DPLANE_OP_TC_CLASS_ADD:
+ ret = "TC_CLASS_ADD";
+ break;
+ case DPLANE_OP_TC_CLASS_DELETE:
+ ret = "TC_CLASS_DELETE";
+ break;
+ case DPLANE_OP_TC_CLASS_UPDATE:
+ ret = "TC_CLASS_UPDATE";
+ break;
+ case DPLANE_OP_TC_FILTER_ADD:
+ ret = "TC_FILTER_ADD";
break;
- case DPLANE_OP_TC_UPDATE:
- ret = "TC_UPDATE";
+ case DPLANE_OP_TC_FILTER_DELETE:
+ ret = "TC_FILTER_DELETE";
break;
- case DPLANE_OP_TC_DELETE:
- ret = "TC_DELETE";
+ case DPLANE_OP_TC_FILTER_UPDATE:
+ ret = "TC__FILTER_UPDATE";
break;
}
@@ -1455,48 +1491,175 @@ uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx)
return ctx->u.rinfo.zd_old_distance;
}
-uint64_t dplane_ctx_tc_get_rate(const struct zebra_dplane_ctx *ctx)
+int dplane_ctx_tc_qdisc_get_kind(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_qdisc.kind;
+}
+
+const char *dplane_ctx_tc_qdisc_get_kind_str(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_qdisc.kind_str;
+}
+
+uint32_t dplane_ctx_tc_class_get_handle(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_class.handle;
+}
+
+int dplane_ctx_tc_class_get_kind(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_class.kind;
+}
+
+const char *dplane_ctx_tc_class_get_kind_str(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_class.kind_str;
+}
+
+uint64_t dplane_ctx_tc_class_get_rate(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_class.rate;
+}
+
+uint64_t dplane_ctx_tc_class_get_ceil(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_class.ceil;
+}
+
+int dplane_ctx_tc_filter_get_kind(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_filter.kind;
+}
+
+const char *
+dplane_ctx_tc_filter_get_kind_str(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_filter.kind_str;
+}
+
+uint32_t dplane_ctx_tc_filter_get_priority(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_filter.priority;
+}
+
+uint32_t dplane_ctx_tc_filter_get_handle(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
- return ctx->u.tc.rate;
+ return ctx->u.tc_filter.handle;
}
-uint64_t dplane_ctx_tc_get_ceil(const struct zebra_dplane_ctx *ctx)
+uint16_t dplane_ctx_tc_filter_get_eth_proto(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
- return ctx->u.tc.ceil;
+ return ctx->u.tc_filter.eth_proto;
}
-uint32_t dplane_ctx_tc_get_filter_bm(const struct zebra_dplane_ctx *ctx)
+uint32_t dplane_ctx_tc_filter_get_filter_bm(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
- return ctx->u.tc.filter_bm;
+ return ctx->u.tc_filter.filter_bm;
}
const struct prefix *
-dplane_ctx_tc_get_src_ip(const struct zebra_dplane_ctx *ctx)
+dplane_ctx_tc_filter_get_src_ip(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return &ctx->u.tc_filter.src_ip;
+}
+
+uint16_t
+dplane_ctx_tc_filter_get_src_port_min(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_filter.src_port_min;
+}
+
+
+uint16_t
+dplane_ctx_tc_filter_get_src_port_max(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
- return &(ctx->u.tc.src_ip);
+ return ctx->u.tc_filter.src_port_max;
}
const struct prefix *
-dplane_ctx_tc_get_dst_ip(const struct zebra_dplane_ctx *ctx)
+dplane_ctx_tc_filter_get_dst_ip(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return &ctx->u.tc_filter.dst_ip;
+}
+
+uint16_t
+dplane_ctx_tc_filter_get_dst_port_min(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
- return &(ctx->u.tc.dst_ip);
+ return ctx->u.tc_filter.dst_port_min;
}
-uint8_t dplane_ctx_tc_get_ip_proto(const struct zebra_dplane_ctx *ctx)
+
+uint16_t
+dplane_ctx_tc_filter_get_dst_port_max(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
- return ctx->u.tc.ip_proto;
+ return ctx->u.tc_filter.dst_port_max;
+}
+
+uint8_t dplane_ctx_tc_filter_get_ip_proto(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_filter.ip_proto;
+}
+
+uint8_t dplane_ctx_tc_filter_get_dsfield(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_filter.dsfield;
+}
+
+uint8_t
+dplane_ctx_tc_filter_get_dsfield_mask(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_filter.dsfield_mask;
+}
+
+uint32_t dplane_ctx_tc_filter_get_classid(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.tc_filter.classid;
}
/*
@@ -2771,7 +2934,9 @@ done:
return ret;
}
-int dplane_ctx_tc_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op)
+static int dplane_ctx_tc_qdisc_init(struct zebra_dplane_ctx *ctx,
+ enum dplane_op_e op,
+ const struct zebra_tc_qdisc *qdisc)
{
int ret = EINVAL;
@@ -2779,6 +2944,9 @@ int dplane_ctx_tc_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op)
ctx->zd_op = op;
ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ ctx->zd_ifindex = qdisc->qdisc.ifindex;
+ ctx->u.tc_qdisc.kind = qdisc->qdisc.kind;
+ ctx->u.tc_qdisc.kind_str = tc_qdisc_kind2str(qdisc->qdisc.kind);
/* TODO: init traffic control qdisc */
zns = zebra_ns_lookup(NS_DEFAULT);
@@ -2790,6 +2958,74 @@ int dplane_ctx_tc_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op)
return ret;
}
+static int dplane_ctx_tc_class_init(struct zebra_dplane_ctx *ctx,
+ enum dplane_op_e op,
+ struct zebra_tc_class *class)
+{
+ int ret = EINVAL;
+
+ struct zebra_ns *zns = NULL;
+
+ ctx->zd_op = op;
+ ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ ctx->zd_ifindex = class->class.ifindex;
+
+ ctx->u.tc_class.handle = class->class.handle;
+ ctx->u.tc_class.kind = class->class.kind;
+ ctx->u.tc_class.kind_str = tc_qdisc_kind2str(class->class.kind);
+ ctx->u.tc_class.rate = class->class.u.htb.rate;
+ ctx->u.tc_class.ceil = class->class.u.htb.ceil;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+
+ dplane_ctx_ns_init(ctx, zns, true);
+
+ ret = AOK;
+
+ return ret;
+}
+
+static int dplane_ctx_tc_filter_init(struct zebra_dplane_ctx *ctx,
+ enum dplane_op_e op,
+ struct zebra_tc_filter *filter)
+{
+ int ret = EINVAL;
+
+ struct zebra_ns *zns = NULL;
+
+ ctx->zd_op = op;
+ ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ ctx->zd_ifindex = filter->filter.ifindex;
+
+ ctx->u.tc_filter.eth_proto = filter->filter.protocol;
+ ctx->u.tc_filter.ip_proto = filter->filter.u.flower.ip_proto;
+
+ ctx->u.tc_filter.kind = filter->filter.kind;
+ ctx->u.tc_filter.kind_str = tc_filter_kind2str(filter->filter.kind);
+
+ ctx->u.tc_filter.filter_bm = filter->filter.u.flower.filter_bm;
+ prefix_copy(&ctx->u.tc_filter.src_ip, &filter->filter.u.flower.src_ip);
+ ctx->u.tc_filter.src_port_min = filter->filter.u.flower.src_port_min;
+ ctx->u.tc_filter.src_port_max = filter->filter.u.flower.src_port_max;
+ prefix_copy(&ctx->u.tc_filter.dst_ip, &filter->filter.u.flower.dst_ip);
+ ctx->u.tc_filter.dst_port_min = filter->filter.u.flower.dst_port_min;
+ ctx->u.tc_filter.dst_port_max = filter->filter.u.flower.dst_port_max;
+ ctx->u.tc_filter.dsfield = filter->filter.u.flower.dsfield;
+ ctx->u.tc_filter.dsfield_mask = filter->filter.u.flower.dsfield_mask;
+ ctx->u.tc_filter.classid = filter->filter.u.flower.classid;
+
+ ctx->u.tc_filter.priority = filter->filter.priority;
+ ctx->u.tc_filter.handle = filter->filter.handle;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+
+ dplane_ctx_ns_init(ctx, zns, true);
+
+ ret = AOK;
+
+ return ret;
+}
+
/**
* dplane_ctx_nexthop_init() - Initialize a context block for a nexthop update
*
@@ -3509,7 +3745,9 @@ dplane_route_update_internal(struct route_node *rn,
return result;
}
-static enum zebra_dplane_result dplane_tc_update_internal(enum dplane_op_e op)
+static enum zebra_dplane_result
+tc_qdisc_update_internal(enum dplane_op_e op,
+ const struct zebra_tc_qdisc *qdisc)
{
enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
int ret;
@@ -3524,7 +3762,7 @@ static enum zebra_dplane_result dplane_tc_update_internal(enum dplane_op_e op)
}
/* Init context with info from zebra data structs */
- ret = dplane_ctx_tc_init(ctx, op);
+ ret = dplane_ctx_tc_qdisc_init(ctx, op, qdisc);
if (ret == AOK)
ret = dplane_update_enqueue(ctx);
@@ -3545,9 +3783,118 @@ done:
return result;
}
-enum zebra_dplane_result dplane_tc_update(void)
+static enum zebra_dplane_result
+tc_class_update_internal(enum dplane_op_e op, struct zebra_tc_class *class)
{
- return dplane_tc_update_internal(DPLANE_OP_TC_UPDATE);
+ enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ int ret;
+ struct zebra_dplane_ctx *ctx = NULL;
+
+ /* Obtain context block */
+ ctx = dplane_ctx_alloc();
+
+ if (!ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Init context with info from zebra data structs */
+ ret = dplane_ctx_tc_class_init(ctx, op, class);
+
+ if (ret == AOK)
+ ret = dplane_update_enqueue(ctx);
+
+done:
+ /* Update counter */
+ atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1,
+ memory_order_relaxed);
+ if (ret == AOK) {
+ result = ZEBRA_DPLANE_REQUEST_QUEUED;
+ } else {
+ atomic_fetch_add_explicit(&zdplane_info.dg_tcs_errors, 1,
+ memory_order_relaxed);
+ if (ctx)
+ dplane_ctx_free(&ctx);
+ }
+
+ return result;
+}
+
+static enum zebra_dplane_result
+tc_filter_update_internal(enum dplane_op_e op, struct zebra_tc_filter *filter)
+{
+ enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ int ret;
+ struct zebra_dplane_ctx *ctx = NULL;
+
+ /* Obtain context block */
+ ctx = dplane_ctx_alloc();
+
+ if (!ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Init context with info from zebra data structs */
+ ret = dplane_ctx_tc_filter_init(ctx, op, filter);
+
+ if (ret == AOK)
+ ret = dplane_update_enqueue(ctx);
+
+done:
+ /* Update counter */
+ atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1,
+ memory_order_relaxed);
+ if (ret == AOK) {
+ result = ZEBRA_DPLANE_REQUEST_QUEUED;
+ } else {
+ atomic_fetch_add_explicit(&zdplane_info.dg_tcs_errors, 1,
+ memory_order_relaxed);
+ if (ctx)
+ dplane_ctx_free(&ctx);
+ }
+
+ return result;
+}
+
+enum zebra_dplane_result dplane_tc_qdisc_install(struct zebra_tc_qdisc *qdisc)
+{
+ return tc_qdisc_update_internal(DPLANE_OP_TC_QDISC_INSTALL, qdisc);
+}
+
+enum zebra_dplane_result dplane_tc_qdisc_uninstall(struct zebra_tc_qdisc *qdisc)
+{
+ return tc_qdisc_update_internal(DPLANE_OP_TC_QDISC_UNINSTALL, qdisc);
+}
+
+enum zebra_dplane_result dplane_tc_class_add(struct zebra_tc_class *class)
+{
+ return tc_class_update_internal(DPLANE_OP_TC_CLASS_ADD, class);
+}
+
+enum zebra_dplane_result dplane_tc_class_delete(struct zebra_tc_class *class)
+{
+ return tc_class_update_internal(DPLANE_OP_TC_CLASS_DELETE, class);
+}
+
+enum zebra_dplane_result dplane_tc_class_update(struct zebra_tc_class *class)
+{
+ return tc_class_update_internal(DPLANE_OP_TC_CLASS_UPDATE, class);
+}
+
+enum zebra_dplane_result dplane_tc_filter_add(struct zebra_tc_filter *filter)
+{
+ return tc_filter_update_internal(DPLANE_OP_TC_FILTER_ADD, filter);
+}
+
+enum zebra_dplane_result dplane_tc_filter_delete(struct zebra_tc_filter *filter)
+{
+ return tc_filter_update_internal(DPLANE_OP_TC_FILTER_DELETE, filter);
+}
+
+enum zebra_dplane_result dplane_tc_filter_update(struct zebra_tc_filter *filter)
+{
+ return tc_filter_update_internal(DPLANE_OP_TC_FILTER_UPDATE, filter);
}
/**
@@ -5733,10 +6080,18 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx)
break;
/* TODO: more detailed log */
- case DPLANE_OP_TC_INSTALL:
- case DPLANE_OP_TC_UPDATE:
- case DPLANE_OP_TC_DELETE:
- zlog_debug("Dplane tc ifidx %u", dplane_ctx_get_ifindex(ctx));
+ case DPLANE_OP_TC_QDISC_INSTALL:
+ case DPLANE_OP_TC_QDISC_UNINSTALL:
+ zlog_debug("Dplane tc qdisc ifidx %u",
+ dplane_ctx_get_ifindex(ctx));
+ break;
+ case DPLANE_OP_TC_CLASS_ADD:
+ case DPLANE_OP_TC_CLASS_DELETE:
+ case DPLANE_OP_TC_CLASS_UPDATE:
+ break;
+ case DPLANE_OP_TC_FILTER_ADD:
+ case DPLANE_OP_TC_FILTER_DELETE:
+ case DPLANE_OP_TC_FILTER_UPDATE:
break;
}
}
@@ -5881,9 +6236,14 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx)
1, memory_order_relaxed);
break;
- case DPLANE_OP_TC_INSTALL:
- case DPLANE_OP_TC_UPDATE:
- case DPLANE_OP_TC_DELETE:
+ case DPLANE_OP_TC_QDISC_INSTALL:
+ case DPLANE_OP_TC_QDISC_UNINSTALL:
+ case DPLANE_OP_TC_CLASS_ADD:
+ case DPLANE_OP_TC_CLASS_DELETE:
+ case DPLANE_OP_TC_CLASS_UPDATE:
+ case DPLANE_OP_TC_FILTER_ADD:
+ case DPLANE_OP_TC_FILTER_DELETE:
+ case DPLANE_OP_TC_FILTER_UPDATE:
if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
atomic_fetch_add_explicit(&zdplane_info.dg_tcs_errors,
1, memory_order_relaxed);
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index 8b239a9ba1..b9fd176de7 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -195,9 +195,14 @@ enum dplane_op_e {
DPLANE_OP_INTF_DELETE,
/* Traffic control */
- DPLANE_OP_TC_INSTALL,
- DPLANE_OP_TC_UPDATE,
- DPLANE_OP_TC_DELETE,
+ DPLANE_OP_TC_QDISC_INSTALL,
+ DPLANE_OP_TC_QDISC_UNINSTALL,
+ DPLANE_OP_TC_CLASS_ADD,
+ DPLANE_OP_TC_CLASS_DELETE,
+ DPLANE_OP_TC_CLASS_UPDATE,
+ DPLANE_OP_TC_FILTER_ADD,
+ DPLANE_OP_TC_FILTER_DELETE,
+ DPLANE_OP_TC_FILTER_UPDATE
};
/*
@@ -384,14 +389,42 @@ void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance);
uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx);
/* Accessors for traffic control context */
-uint64_t dplane_ctx_tc_get_rate(const struct zebra_dplane_ctx *ctx);
-uint64_t dplane_ctx_tc_get_ceil(const struct zebra_dplane_ctx *ctx);
-uint32_t dplane_ctx_tc_get_filter_bm(const struct zebra_dplane_ctx *ctx);
+int dplane_ctx_tc_qdisc_get_kind(const struct zebra_dplane_ctx *ctx);
+const char *
+dplane_ctx_tc_qdisc_get_kind_str(const struct zebra_dplane_ctx *ctx);
+
+uint32_t dplane_ctx_tc_class_get_handle(const struct zebra_dplane_ctx *ctx);
+int dplane_ctx_tc_class_get_kind(const struct zebra_dplane_ctx *ctx);
+const char *
+dplane_ctx_tc_class_get_kind_str(const struct zebra_dplane_ctx *ctx);
+uint64_t dplane_ctx_tc_class_get_rate(const struct zebra_dplane_ctx *ctx);
+uint64_t dplane_ctx_tc_class_get_ceil(const struct zebra_dplane_ctx *ctx);
+
+int dplane_ctx_tc_filter_get_kind(const struct zebra_dplane_ctx *ctx);
+const char *
+dplane_ctx_tc_filter_get_kind_str(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_tc_filter_get_priority(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_tc_filter_get_handle(const struct zebra_dplane_ctx *ctx);
+uint16_t dplane_ctx_tc_filter_get_minor(const struct zebra_dplane_ctx *ctx);
+uint16_t dplane_ctx_tc_filter_get_eth_proto(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_tc_filter_get_filter_bm(const struct zebra_dplane_ctx *ctx);
const struct prefix *
-dplane_ctx_tc_get_src_ip(const struct zebra_dplane_ctx *ctx);
+dplane_ctx_tc_filter_get_src_ip(const struct zebra_dplane_ctx *ctx);
+uint16_t
+dplane_ctx_tc_filter_get_src_port_min(const struct zebra_dplane_ctx *ctx);
+uint16_t
+dplane_ctx_tc_filter_get_src_port_max(const struct zebra_dplane_ctx *ctx);
const struct prefix *
-dplane_ctx_tc_get_dst_ip(const struct zebra_dplane_ctx *ctx);
-uint8_t dplane_ctx_tc_get_ip_proto(const struct zebra_dplane_ctx *ctx);
+dplane_ctx_tc_filter_get_dst_ip(const struct zebra_dplane_ctx *ctx);
+uint16_t
+dplane_ctx_tc_filter_get_dst_port_min(const struct zebra_dplane_ctx *ctx);
+uint16_t
+dplane_ctx_tc_filter_get_dst_port_max(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_tc_filter_get_ip_proto(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_tc_filter_get_dsfield(const struct zebra_dplane_ctx *ctx);
+uint8_t
+dplane_ctx_tc_filter_get_dsfield_mask(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_tc_filter_get_classid(const struct zebra_dplane_ctx *ctx);
void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh);
void dplane_ctx_set_backup_nhg(struct zebra_dplane_ctx *ctx,
@@ -723,11 +756,23 @@ enum zebra_dplane_result dplane_intf_update(const struct interface *ifp);
enum zebra_dplane_result dplane_intf_delete(const struct interface *ifp);
/*
- * Enqueue interface link changes for the dataplane.
+ * Enqueue tc link changes for the dataplane.
*/
-enum zebra_dplane_result dplane_tc_add(void);
-enum zebra_dplane_result dplane_tc_update(void);
-enum zebra_dplane_result dplane_tc_delete(void);
+
+struct zebra_tc_qdisc;
+struct zebra_tc_class;
+struct zebra_tc_filter;
+enum zebra_dplane_result dplane_tc_qdisc_install(struct zebra_tc_qdisc *qdisc);
+enum zebra_dplane_result
+dplane_tc_qdisc_uninstall(struct zebra_tc_qdisc *qdisc);
+enum zebra_dplane_result dplane_tc_class_add(struct zebra_tc_class *class);
+enum zebra_dplane_result dplane_tc_class_delete(struct zebra_tc_class *class);
+enum zebra_dplane_result dplane_tc_class_update(struct zebra_tc_class *class);
+enum zebra_dplane_result dplane_tc_filter_add(struct zebra_tc_filter *filter);
+enum zebra_dplane_result
+dplane_tc_filter_delete(struct zebra_tc_filter *filter);
+enum zebra_dplane_result
+dplane_tc_filter_update(struct zebra_tc_filter *filter);
/*
* Link layer operations for the dataplane.
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 5fadd4c82b..286cc0292d 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -3168,9 +3168,14 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_INTF_INSTALL:
case DPLANE_OP_INTF_UPDATE:
case DPLANE_OP_INTF_DELETE:
- case DPLANE_OP_TC_INSTALL:
- case DPLANE_OP_TC_UPDATE:
- case DPLANE_OP_TC_DELETE:
+ case DPLANE_OP_TC_QDISC_INSTALL:
+ case DPLANE_OP_TC_QDISC_UNINSTALL:
+ case DPLANE_OP_TC_CLASS_ADD:
+ case DPLANE_OP_TC_CLASS_DELETE:
+ case DPLANE_OP_TC_CLASS_UPDATE:
+ case DPLANE_OP_TC_FILTER_ADD:
+ case DPLANE_OP_TC_FILTER_DELETE:
+ case DPLANE_OP_TC_FILTER_UPDATE:
break;
}
}
diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c
index 13fd972499..f52c5e0058 100644
--- a/zebra/zebra_ns.c
+++ b/zebra/zebra_ns.c
@@ -34,6 +34,7 @@
#include "zebra_netns_notify.h"
#include "zebra_netns_id.h"
#include "zebra_pbr.h"
+#include "zebra_tc.h"
#include "rib.h"
#include "table_manager.h"
#include "zebra_errors.h"
@@ -127,6 +128,7 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
interface_list(zns);
route_read(zns);
kernel_read_pbr_rules(zns);
+ kernel_read_tc_qdisc(zns);
return 0;
}
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 54ef4768eb..656588bb82 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -4710,9 +4710,14 @@ static void rib_process_dplane_results(struct thread *thread)
zebra_if_dplane_result(ctx);
break;
- case DPLANE_OP_TC_INSTALL:
- case DPLANE_OP_TC_UPDATE:
- case DPLANE_OP_TC_DELETE:
+ case DPLANE_OP_TC_QDISC_INSTALL:
+ case DPLANE_OP_TC_QDISC_UNINSTALL:
+ case DPLANE_OP_TC_CLASS_ADD:
+ case DPLANE_OP_TC_CLASS_DELETE:
+ case DPLANE_OP_TC_CLASS_UPDATE:
+ case DPLANE_OP_TC_FILTER_ADD:
+ case DPLANE_OP_TC_FILTER_DELETE:
+ case DPLANE_OP_TC_FILTER_UPDATE:
break;
/* Some op codes not handled here */
diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c
index c66849863e..b8923ef57d 100644
--- a/zebra/zebra_router.c
+++ b/zebra/zebra_router.c
@@ -30,6 +30,7 @@
#include "zebra_mlag.h"
#include "zebra_nhg.h"
#include "zebra_neigh.h"
+#include "zebra/zebra_tc.h"
#include "debug.h"
#include "zebra_script.h"
@@ -312,6 +313,20 @@ void zebra_router_init(bool asic_offload, bool notify_on_ack)
hash_create_size(8, zebra_nhg_id_key, zebra_nhg_hash_id_equal,
"Zebra Router Nexthop Groups ID index");
+ zrouter.rules_hash =
+ hash_create_size(8, zebra_pbr_rules_hash_key,
+ zebra_pbr_rules_hash_equal, "Rules Hash");
+
+ zrouter.qdisc_hash =
+ hash_create_size(8, zebra_tc_qdisc_hash_key,
+ zebra_tc_qdisc_hash_equal, "TC (qdisc) Hash");
+ zrouter.class_hash = hash_create_size(8, zebra_tc_class_hash_key,
+ zebra_tc_class_hash_equal,
+ "TC (classes) Hash");
+ zrouter.filter_hash = hash_create_size(8, zebra_tc_filter_hash_key,
+ zebra_tc_filter_hash_equal,
+ "TC (filter) Hash");
+
zrouter.asic_offloaded = asic_offload;
zrouter.notify_on_ack = notify_on_ack;
diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h
index 992bcd5c08..069437ef47 100644
--- a/zebra/zebra_router.h
+++ b/zebra/zebra_router.h
@@ -172,6 +172,10 @@ struct zebra_router {
struct hash *iptable_hash;
+ struct hash *qdisc_hash;
+ struct hash *class_hash;
+ struct hash *filter_hash;
+
/* A sequence number used for tracking routes */
_Atomic uint32_t sequence_num;
diff --git a/zebra/zebra_tc.c b/zebra/zebra_tc.c
new file mode 100644
index 0000000000..09d9986fb3
--- /dev/null
+++ b/zebra/zebra_tc.c
@@ -0,0 +1,444 @@
+/*
+ * Zebra Traffic Control (TC) main handling.
+ *
+ * Copyright (C) 2022 Shichu Yang
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <jhash.h>
+#include <hash.h>
+#include <memory.h>
+#include <hook.h>
+
+#include "zebra/zebra_router.h"
+#include "zebra/zebra_dplane.h"
+#include "zebra/zebra_tc.h"
+#include "zebra/debug.h"
+
+DEFINE_MTYPE_STATIC(ZEBRA, TC_QDISC, "TC queue discipline");
+DEFINE_MTYPE_STATIC(ZEBRA, TC_CLASS, "TC class");
+DEFINE_MTYPE_STATIC(ZEBRA, TC_FILTER, "TC filter");
+
+const struct message tc_qdisc_kinds[] = {
+ {TC_QDISC_HTB, "htb"},
+ {TC_QDISC_NOQUEUE, "noqueue"},
+ {0},
+};
+
+const struct message tc_filter_kinds[] = {
+ {TC_FILTER_BPF, "bpf"},
+ {TC_FILTER_FLOW, "flow"},
+ {TC_FILTER_FLOWER, "flower"},
+ {TC_FILTER_U32, "u32"},
+ {0},
+};
+
+const struct message *tc_class_kinds = tc_qdisc_kinds;
+
+static uint32_t lookup_key(const struct message *mz, const char *msg,
+ uint32_t nf)
+{
+ static struct message nt = {0};
+ uint32_t rz = nf ? nf : UINT32_MAX;
+ const struct message *pnt;
+
+ for (pnt = mz; memcmp(pnt, &nt, sizeof(struct message)); pnt++)
+ if (strcmp(pnt->str, msg) == 0) {
+ rz = pnt->key;
+ break;
+ }
+ return rz;
+}
+
+const char *tc_qdisc_kind2str(uint32_t type)
+{
+ return lookup_msg(tc_qdisc_kinds, type, "Unrecognized QDISC Type");
+}
+
+enum tc_qdisc_kind tc_qdisc_str2kind(const char *type)
+{
+ return lookup_key(tc_qdisc_kinds, type, TC_QDISC_UNSPEC);
+}
+
+uint32_t zebra_tc_qdisc_hash_key(const void *arg)
+{
+ const struct zebra_tc_qdisc *qdisc;
+ uint32_t key;
+
+ qdisc = arg;
+
+ key = jhash_1word(qdisc->qdisc.ifindex, 0);
+
+ return key;
+}
+
+bool zebra_tc_qdisc_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_tc_qdisc *q1, *q2;
+
+ q1 = (const struct zebra_tc_qdisc *)arg1;
+ q2 = (const struct zebra_tc_qdisc *)arg2;
+
+ if (q1->qdisc.ifindex != q2->qdisc.ifindex)
+ return false;
+
+ return true;
+}
+
+struct tc_qdisc_ifindex_lookup {
+ struct zebra_tc_qdisc *qdisc;
+ ifindex_t ifindex;
+};
+
+
+static int tc_qdisc_lookup_ifindex_walker(struct hash_bucket *b, void *data)
+{
+ struct tc_qdisc_ifindex_lookup *lookup = data;
+ struct zebra_tc_qdisc *qdisc = b->data;
+
+ if (lookup->ifindex == qdisc->qdisc.ifindex) {
+ lookup->qdisc = qdisc;
+ return HASHWALK_ABORT;
+ }
+
+ return HASHWALK_CONTINUE;
+}
+
+static struct zebra_tc_qdisc *
+tc_qdisc_lookup_ifindex(struct zebra_tc_qdisc *qdisc)
+{
+ struct tc_qdisc_ifindex_lookup lookup;
+
+ lookup.ifindex = qdisc->qdisc.ifindex;
+ lookup.qdisc = NULL;
+ hash_walk(zrouter.rules_hash, &tc_qdisc_lookup_ifindex_walker, &lookup);
+
+ return lookup.qdisc;
+}
+
+static void *tc_qdisc_alloc_intern(void *arg)
+{
+ struct zebra_tc_qdisc *ztq;
+ struct zebra_tc_qdisc *new;
+
+ ztq = (struct zebra_tc_qdisc *)arg;
+
+ new = XCALLOC(MTYPE_TC_QDISC, sizeof(*new));
+
+ memcpy(new, ztq, sizeof(*ztq));
+
+ return new;
+}
+
+static struct zebra_tc_qdisc *tc_qdisc_free(struct zebra_tc_qdisc *hash_data,
+ bool free_data)
+{
+ hash_release(zrouter.qdisc_hash, hash_data);
+
+ if (free_data) {
+ XFREE(MTYPE_TC_QDISC, hash_data);
+ return NULL;
+ }
+
+ return hash_data;
+}
+
+static struct zebra_tc_qdisc *tc_qdisc_release(struct zebra_tc_qdisc *qdisc,
+ bool free_data)
+{
+ struct zebra_tc_qdisc *lookup;
+
+ lookup = hash_lookup(zrouter.qdisc_hash, qdisc);
+
+ if (!lookup)
+ return NULL;
+
+ return tc_qdisc_free(lookup, free_data);
+}
+
+void zebra_tc_qdisc_install(struct zebra_tc_qdisc *qdisc)
+{
+ if (IS_ZEBRA_DEBUG_TC)
+ zlog_debug("%s: install tc qdisc ifindex %d kind %s", __func__,
+ qdisc->qdisc.ifindex,
+ tc_qdisc_kind2str(qdisc->qdisc.kind));
+
+ struct zebra_tc_qdisc *found;
+ struct zebra_tc_qdisc *old;
+ struct zebra_tc_qdisc *new;
+
+ found = tc_qdisc_lookup_ifindex(qdisc);
+
+ if (found) {
+ if (!zebra_tc_qdisc_hash_equal(qdisc, found)) {
+ old = tc_qdisc_release(found, false);
+ (void)dplane_tc_qdisc_uninstall(old);
+ new = hash_get(zrouter.qdisc_hash, qdisc,
+ tc_qdisc_alloc_intern);
+ (void)dplane_tc_qdisc_install(new);
+ XFREE(MTYPE_TC_QDISC, old);
+ }
+ } else {
+ new = hash_get(zrouter.qdisc_hash, qdisc,
+ tc_qdisc_alloc_intern);
+ (void)dplane_tc_qdisc_install(new);
+ }
+}
+
+void zebra_tc_qdisc_uninstall(struct zebra_tc_qdisc *qdisc)
+{
+ if (IS_ZEBRA_DEBUG_TC)
+ zlog_debug("%s: uninstall tc qdisc ifindex %d kind %s",
+ __func__, qdisc->qdisc.ifindex,
+ tc_qdisc_kind2str(qdisc->qdisc.kind));
+
+ (void)dplane_tc_qdisc_uninstall(qdisc);
+
+ if (tc_qdisc_release(qdisc, true))
+ zlog_debug("%s: tc qdisc being deleted we know nothing about",
+ __func__);
+}
+
+uint32_t zebra_tc_class_hash_key(const void *arg)
+{
+ const struct zebra_tc_class *class;
+ uint32_t key;
+
+ class = arg;
+
+ key = jhash_2words(class->class.ifindex, class->class.handle, 0);
+
+ return key;
+}
+
+bool zebra_tc_class_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_tc_class *c1, *c2;
+
+ c1 = (const struct zebra_tc_class *)arg1;
+ c2 = (const struct zebra_tc_class *)arg2;
+
+ if (c1->class.ifindex != c2->class.ifindex)
+ return false;
+
+ if (c1->class.handle != c2->class.handle)
+ return false;
+
+ return true;
+}
+
+static void *tc_class_alloc_intern(void *arg)
+{
+ struct zebra_tc_class *class;
+ struct zebra_tc_class *new;
+
+ class = (struct zebra_tc_class *)arg;
+
+ new = XCALLOC(MTYPE_TC_CLASS, sizeof(*new));
+
+ memcpy(new, class, sizeof(*class));
+
+ return new;
+}
+
+static struct zebra_tc_class *tc_class_free(struct zebra_tc_class *hash_data,
+ bool free_data)
+{
+ hash_release(zrouter.class_hash, hash_data);
+
+ if (free_data) {
+ XFREE(MTYPE_TC_CLASS, hash_data);
+ return NULL;
+ }
+
+ return hash_data;
+}
+
+static struct zebra_tc_class *tc_class_release(struct zebra_tc_class *class,
+ bool free_data)
+{
+ struct zebra_tc_class *lookup;
+
+ lookup = hash_lookup(zrouter.class_hash, class);
+
+ if (!lookup)
+ return NULL;
+
+ return tc_class_free(lookup, free_data);
+}
+
+void zebra_tc_class_add(struct zebra_tc_class *class)
+{
+ if (IS_ZEBRA_DEBUG_TC)
+ zlog_debug(
+ "%s: add tc class ifindex %d handle %04x:%04x kind %s",
+ __func__, class->class.ifindex,
+ (class->class.handle & 0xffff0000u) >> 16,
+ class->class.handle & 0x0000ffffu,
+ tc_qdisc_kind2str(class->class.kind));
+
+ struct zebra_tc_class *found;
+ struct zebra_tc_class *new;
+
+ /*
+ * We find the class in the hash by (ifindex, handle) directly, and by
+ * testing their deep equality to seek out whether it's an update.
+ *
+ * Currently deep equality is not checked since it will be okay to
+ * update the totally same class again.
+ */
+ found = hash_lookup(zrouter.class_hash, class);
+ new = hash_get(zrouter.class_hash, class, tc_class_alloc_intern);
+
+ if (found)
+ (void)dplane_tc_class_update(new);
+ else
+ (void)dplane_tc_class_add(new);
+}
+
+void zebra_tc_class_delete(struct zebra_tc_class *class)
+{
+ if (IS_ZEBRA_DEBUG_TC)
+ zlog_debug(
+ "%s: delete tc class ifindex %d handle %04x:%04x kind %s",
+ __func__, class->class.ifindex,
+ (class->class.handle & 0xffff0000u) >> 16,
+ class->class.handle & 0x0000ffffu,
+ tc_qdisc_kind2str(class->class.kind));
+
+ (void)dplane_tc_class_delete(class);
+
+ if (tc_class_release(class, true))
+ zlog_debug("%s: tc class being deleted we know nothing about",
+ __func__);
+}
+
+const char *tc_filter_kind2str(uint32_t type)
+{
+ return lookup_msg(tc_filter_kinds, type, "Unrecognized TFILTER Type");
+}
+
+enum tc_qdisc_kind tc_filter_str2kind(const char *type)
+{
+ return lookup_key(tc_filter_kinds, type, TC_FILTER_UNSPEC);
+}
+
+uint32_t zebra_tc_filter_hash_key(const void *arg)
+{
+ const struct zebra_tc_filter *filter;
+ uint32_t key;
+
+ filter = arg;
+
+ key = jhash_2words(filter->filter.ifindex, filter->filter.handle, 0);
+
+ return key;
+}
+
+bool zebra_tc_filter_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_tc_filter *f1, *f2;
+
+ f1 = (const struct zebra_tc_filter *)arg1;
+ f2 = (const struct zebra_tc_filter *)arg2;
+
+ if (f1->filter.ifindex != f2->filter.ifindex)
+ return false;
+
+ if (f1->filter.handle != f2->filter.handle)
+ return false;
+
+ return true;
+}
+
+static struct zebra_tc_filter *tc_filter_free(struct zebra_tc_filter *hash_data,
+ bool free_data)
+{
+ hash_release(zrouter.filter_hash, hash_data);
+
+ if (free_data) {
+ XFREE(MTYPE_TC_FILTER, hash_data);
+ return NULL;
+ }
+
+ return hash_data;
+}
+
+static struct zebra_tc_filter *tc_filter_release(struct zebra_tc_filter *filter,
+ bool free_data)
+{
+ struct zebra_tc_filter *lookup;
+
+ lookup = hash_lookup(zrouter.filter_hash, filter);
+
+ if (!lookup)
+ return NULL;
+
+ return tc_filter_free(lookup, free_data);
+}
+
+static void *tc_filter_alloc_intern(void *arg)
+{
+ struct zebra_tc_filter *ztf;
+ struct zebra_tc_filter *new;
+
+ ztf = (struct zebra_tc_filter *)arg;
+
+ new = XCALLOC(MTYPE_TC_FILTER, sizeof(*new));
+
+ memcpy(new, ztf, sizeof(*ztf));
+
+ return new;
+}
+
+void zebra_tc_filter_add(struct zebra_tc_filter *filter)
+{
+ if (IS_ZEBRA_DEBUG_TC)
+ zlog_debug(
+ "%s: add tc filter ifindex %d priority %u handle %08x kind %s",
+ __func__, filter->filter.ifindex,
+ filter->filter.priority, filter->filter.handle,
+ tc_filter_kind2str(filter->filter.kind));
+
+ struct zebra_tc_filter *found;
+ struct zebra_tc_filter *new;
+
+ found = hash_lookup(zrouter.filter_hash, filter);
+ new = hash_get(zrouter.filter_hash, filter, tc_filter_alloc_intern);
+
+ if (found)
+ (void)dplane_tc_filter_update(new);
+ else
+ (void)dplane_tc_filter_add(new);
+}
+
+void zebra_tc_filter_delete(struct zebra_tc_filter *filter)
+{
+ if (IS_ZEBRA_DEBUG_PBR)
+ zlog_debug(
+ "%s: delete tc filter ifindex %d priority %u handle %08x kind %s",
+ __func__, filter->filter.ifindex,
+ filter->filter.priority, filter->filter.handle,
+ tc_filter_kind2str(filter->filter.kind));
+
+ (void)dplane_tc_filter_delete(filter);
+
+ if (tc_filter_release(filter, true))
+ zlog_debug("%s: tc filter being deleted we know nothing about",
+ __func__);
+}
diff --git a/zebra/zebra_tc.h b/zebra/zebra_tc.h
new file mode 100644
index 0000000000..832972b713
--- /dev/null
+++ b/zebra/zebra_tc.h
@@ -0,0 +1,79 @@
+/*
+ * Zebra Traffic Control (TC) Data structures and definitions
+ * These are public definitions referenced by multiple files.
+ *
+ * Copyright (C) 2022 Shichu Yang
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _ZEBRA_TC_H
+#define _ZEBRA_TC_H
+
+#include <zebra.h>
+#include "rt.h"
+#include "tc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct zebra_tc_qdisc {
+ int sock;
+
+ struct tc_qdisc qdisc;
+};
+
+struct zebra_tc_class {
+ int sock;
+
+ struct tc_class class;
+};
+
+struct zebra_tc_filter {
+ int sock;
+
+ struct tc_filter filter;
+};
+
+const char *tc_qdisc_kind2str(uint32_t type);
+enum tc_qdisc_kind tc_qdisc_str2kind(const char *type);
+
+uint32_t zebra_tc_qdisc_hash_key(const void *arg);
+bool zebra_tc_qdisc_hash_equal(const void *arg1, const void *arg2);
+void zebra_tc_qdisc_install(struct zebra_tc_qdisc *qdisc);
+void zebra_tc_qdisc_uninstall(struct zebra_tc_qdisc *qdisc);
+
+uint32_t zebra_tc_class_hash_key(const void *arg);
+bool zebra_tc_class_hash_equal(const void *arg1, const void *arg2);
+void zebra_tc_class_add(struct zebra_tc_class *class);
+void zebra_tc_class_delete(struct zebra_tc_class *class);
+
+const char *tc_filter_kind2str(uint32_t type);
+enum tc_qdisc_kind tc_filter_str2kind(const char *type);
+void zebra_tc_filter_add(struct zebra_tc_filter *filter);
+void zebra_tc_filter_delete(struct zebra_tc_filter *filter);
+
+void zebra_tc_filters_free(void *arg);
+uint32_t zebra_tc_filter_hash_key(const void *arg);
+bool zebra_tc_filter_hash_equal(const void *arg1, const void *arg2);
+
+void kernel_read_tc_qdisc(struct zebra_ns *zns);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZEBRA_TC_H */