diff options
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, ¬e, - NULL, NULL)) + if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, ¬e, 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 */ |
