diff options
219 files changed, 11257 insertions, 2152 deletions
diff --git a/.github/workflows/build-test-docker.yml b/.github/workflows/build-test-docker.yml index 3f53f32d3a..aef41d5733 100644 --- a/.github/workflows/build-test-docker.yml +++ b/.github/workflows/build-test-docker.yml @@ -12,8 +12,8 @@ defaults: shell: bash jobs: - build-docker: - name: Build the ubuntu 22.04 docker image + build-x86-docker: + name: Build the x86 ubuntu 22.04 docker image runs-on: ubuntu-latest steps: - name: Checkout @@ -22,32 +22,32 @@ jobs: fetch-depth: 1 - name: Build docker image run: | - docker build -t frr-ubuntu22 -f docker/ubuntu-ci/Dockerfile . - docker save --output /tmp/frr-ubuntu22.tar frr-ubuntu22 + docker build -t frr-x86-ubuntu22 -f docker/ubuntu-ci/Dockerfile . + docker save --output /tmp/frr-x86-ubuntu22.tar frr-x86-ubuntu22 - name: Upload docker image artifact uses: actions/upload-artifact@v4 with: - name: ubuntu-image - path: /tmp/frr-ubuntu22.tar + name: ubuntu-x86-image + path: /tmp/frr-x86-ubuntu22.tar - name: Clear any previous results # So if all jobs are re-run then all tests will be re-run run: | - rm -rf test-results* - mkdir -p test-results - touch test-results/cleared-results.txt + rm -rf test-results-x86* + mkdir -p test-results-x86 + touch test-results-x86/cleared-results.txt - name: Save cleared previous results uses: actions/upload-artifact@v4 with: - name: test-results - path: test-results + name: test-results-x86 + path: test-results-x86 overwrite: true - name: Cleanup if: ${{ always() }} - run: rm -rf test-results* /tmp/frr-ubuntu22.tar + run: rm -rf test-results-x86* /tmp/frr-x86-ubuntu22.tar - test-docker: - name: Test ubuntu docker image - needs: build-docker + test-x86-docker: + name: Test ubuntu x86 docker image + needs: build-x86-docker runs-on: ubuntu-latest steps: - name: Checkout @@ -57,14 +57,14 @@ jobs: - name: Fetch docker image artifact uses: actions/download-artifact@v4 with: - name: ubuntu-image + name: ubuntu-x86-image path: /tmp - name: Fetch previous results if: ${{ github.run_attempt > 1 }} uses: actions/download-artifact@v4 with: - name: test-results - path: test-results + name: test-results-x86 + path: test-results-x86 - name: Run topotests run: | uname -a @@ -75,37 +75,37 @@ jobs: sudo modprobe vrf || true sudo modprobe mpls-iptunnel sudo modprobe mpls-router - docker load --input /tmp/frr-ubuntu22.tar + docker load --input /tmp/frr-x86-ubuntu22.tar if ! grep CONFIG_IP_MROUTE_MULTIPLE_TABLES=y /boot/config*; then ADD_DOCKER_ENV+="-e MROUTE_VRF_MISSING=1" fi echo "ADD_DOCKER_ENV: ${ADD_DOCKER_ENV}" - if [ -f test-results/topotests.xml ]; then - ./tests/topotests/analyze.py -r test-results - ls -l test-results/topotests.xml - run_tests=$(./tests/topotests/analyze.py -r test-results | cut -f1 -d: | sort -u) + if [ -f test-results-x86/topotests.xml ]; then + ./tests/topotests/analyze.py -r test-results-x86 + ls -l test-results-x86/topotests.xml + run_tests=$(./tests/topotests/analyze.py -r test-results-x86 | cut -f1 -d: | sort -u) else echo "No test results dir" run_tests="" fi - rm -rf test-results* /tmp/topotests + rm -rf test-results-x86* /tmp/topotests echo RUN_TESTS: $run_tests - if docker run --init -i --privileged --name frr-ubuntu-cont ${ADD_DOCKER_ENV} -v /lib/modules:/lib/modules frr-ubuntu22 \ + if docker run --init -i --privileged --name frr-ubuntu-cont ${ADD_DOCKER_ENV} -v /lib/modules:/lib/modules frr-x86-ubuntu22 \ bash -c 'cd ~/frr/tests/topotests ; sudo -E pytest -n$(($(nproc) * 5 / 2)) --dist=loadfile '$run_tests; then echo "All tests passed." exit 0 fi # Grab the results from the container - if ! ./tests/topotests/analyze.py -Ar test-results -C frr-ubuntu-cont; then - if [ ! -d test-results ]; then + if ! ./tests/topotests/analyze.py -Ar test-results-x86 -C frr-ubuntu-cont; then + if [ ! -d test-results-x86 ]; then echo "ERROR: Basic failure in docker run, no test results directory available." >&2 exit 1; fi - if [ ! -f test-results/topotests.xml ]; then + if [ ! -f test-results-x86/topotests.xml ]; then # In this case we may be missing topotests.xml echo "ERROR: No topotests.xml available perhaps docker run aborted?" >&2 exit 1; @@ -114,11 +114,11 @@ jobs: fi # Save some information useful for debugging - cp /boot/config* test-results/ - sysctl -a > test-results/sysctl.out 2> /dev/null + cp /boot/config* test-results-x86/ + sysctl -a > test-results-x86/sysctl.out 2> /dev/null # Now get the failed tests (if any) from the archived results directory. - rerun_tests=$(./tests/topotests/analyze.py -r test-results | cut -f1 -d: | sort -u) + rerun_tests=$(./tests/topotests/analyze.py -r test-results-x86 | cut -f1 -d: | sort -u) if [ -z "$rerun_tests" ]; then echo "All tests passed during parallel run." exit 0 @@ -129,8 +129,8 @@ jobs: docker stop frr-ubuntu-cont docker rm frr-ubuntu-cont - mv test-results test-results-initial - if docker run --init -i --privileged --name frr-ubuntu-cont ${ADD_DOCKER_ENV} -v /lib/modules:/lib/modules frr-ubuntu22 \ + mv test-results-x86 test-results-x86-initial + if docker run --init -i --privileged --name frr-ubuntu-cont ${ADD_DOCKER_ENV} -v /lib/modules:/lib/modules frr-x86-ubuntu22 \ bash -c 'cd ~/frr/tests/topotests ; sudo -E pytest '$rerun_tests; then echo "All rerun tests passed." exit 0 @@ -140,8 +140,8 @@ jobs: - name: Gather results if: ${{ always() }} run: | - if [ ! -d test-results ]; then - if ! ./tests/topotests/analyze.py -Ar test-results -C frr-ubuntu-cont; then + if [ ! -d test-results-x86 ]; then + if ! ./tests/topotests/analyze.py -Ar test-results-x86 -C frr-ubuntu-cont; then echo "ERROR: gathering results produced an error, perhaps due earlier run cancellation." >&2 fi fi @@ -149,15 +149,163 @@ jobs: if: ${{ always() }} uses: actions/upload-artifact@v4 with: - name: test-results + name: test-results-x86 path: | - test-results - test-results-initial + test-results-x86 + test-results-x86-initial overwrite: true - name: Cleanup if: ${{ always() }} run: | - rm -rf test-results* /tmp/frr-ubuntu22.tar + rm -rf test-results-x86* /tmp/frr-x86-ubuntu22.tar docker stop frr-ubuntu-cont || true docker rm frr-ubuntu-cont || true + build-arm-docker: + name: Build the ARM ubuntu 22.04 docker image + runs-on: ubuntu-22.04-arm + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1 + - name: Build docker image + run: | + docker build -t frr-arm-ubuntu22 -f docker/ubuntu-ci/Dockerfile . + docker save --output /tmp/frr-arm-ubuntu22.tar frr-arm-ubuntu22 + - name: Upload docker image artifact + uses: actions/upload-artifact@v4 + with: + name: ubuntu-arm-image + path: /tmp/frr-arm-ubuntu22.tar + - name: Clear any previous results + # So if all jobs are re-run then all tests will be re-run + run: | + rm -rf test-results-arm* + mkdir -p test-results-arm + touch test-results-arm/cleared-results.txt + - name: Save cleared previous results + uses: actions/upload-artifact@v4 + with: + name: test-results-arm + path: test-results-arm + overwrite: true + - name: Cleanup + if: ${{ always() }} + run: rm -rf test-results-arm* /tmp/frr-arm-ubuntu22.tar + + test-arm-docker: + name: Test ubuntu ARM docker image + needs: build-arm-docker + runs-on: ubuntu-22.04-arm + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1 + - name: Fetch docker image artifact + uses: actions/download-artifact@v4 + with: + name: ubuntu-arm-image + path: /tmp + - name: Fetch previous results + if: ${{ github.run_attempt > 1 }} + uses: actions/download-artifact@v4 + with: + name: test-results-arm + path: test-results-arm + - name: Run topotests + run: | + uname -a + MODPKGVER=$(uname -r) + sudo apt-get update -y + # Github is running old kernels but installing newer packages :( + sudo apt-get install -y linux-modules-extra-azure linux-modules-${MODPKGVER} linux-modules-extra-${MODPKGVER} python3-xmltodict + sudo modprobe vrf || true + sudo modprobe mpls-iptunnel + sudo modprobe mpls-router + docker load --input /tmp/frr-arm-ubuntu22.tar + + if ! grep CONFIG_IP_MROUTE_MULTIPLE_TABLES=y /boot/config*; then + ADD_DOCKER_ENV+="-e MROUTE_VRF_MISSING=1" + fi + echo "ADD_DOCKER_ENV: ${ADD_DOCKER_ENV}" + + if [ -f test-results-arm/topotests.xml ]; then + ./tests/topotests/analyze.py -r test-results-arm + ls -l test-results-arm/topotests.xml + run_tests=$(./tests/topotests/analyze.py -r test-results-arm | cut -f1 -d: | sort -u) + else + echo "No test results dir" + run_tests="" + fi + rm -rf test-results-arm* /tmp/topotests + + echo RUN_TESTS: $run_tests + if docker run --init -i --privileged --name frr-ubuntu-cont ${ADD_DOCKER_ENV} -v /lib/modules:/lib/modules frr-arm-ubuntu22 \ + bash -c 'cd ~/frr/tests/topotests ; sudo -E pytest -n$(($(nproc) * 5 / 2)) --dist=loadfile '$run_tests; then + echo "All tests passed." + exit 0 + fi + + # Grab the results from the container + if ! ./tests/topotests/analyze.py -Ar test-results-arm -C frr-ubuntu-cont; then + if [ ! -d test-results-arm ]; then + echo "ERROR: Basic failure in docker run, no test results directory available." >&2 + exit 1; + fi + if [ ! -f test-results-arm/topotests.xml ]; then + # In this case we may be missing topotests.xml + echo "ERROR: No topotests.xml available perhaps docker run aborted?" >&2 + exit 1; + fi + echo "WARNING: analyyze.py returned error but grabbed results anyway." >&2 + fi + + # Save some information useful for debugging + cp /boot/config* test-results-arm/ + sysctl -a > test-results-arm/sysctl.out 2> /dev/null + + # Now get the failed tests (if any) from the archived results directory. + rerun_tests=$(./tests/topotests/analyze.py -r test-results-arm | cut -f1 -d: | sort -u) + if [ -z "$rerun_tests" ]; then + echo "All tests passed during parallel run." + exit 0 + fi + + echo "ERROR: Some tests failed during parallel run, rerunning serially." >&2 + echo RERUN_TESTS: $rerun_tests >&2 + docker stop frr-ubuntu-cont + docker rm frr-ubuntu-cont + + mv test-results-arm test-results-arm-initial + if docker run --init -i --privileged --name frr-ubuntu-cont ${ADD_DOCKER_ENV} -v /lib/modules:/lib/modules frr-arm-ubuntu22 \ + bash -c 'cd ~/frr/tests/topotests ; sudo -E pytest '$rerun_tests; then + echo "All rerun tests passed." + exit 0 + fi + echo "Some rerun tests still failed." + exit 1 + - name: Gather results + if: ${{ always() }} + run: | + if [ ! -d test-results-arm ]; then + if ! ./tests/topotests/analyze.py -Ar test-results-arm -C frr-ubuntu-cont; then + echo "ERROR: gathering results produced an error, perhaps due earlier run cancellation." >&2 + fi + fi + - name: Upload test results + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: test-results-arm + path: | + test-results-arm + test-results-arm-initial + overwrite: true + - name: Cleanup + if: ${{ always() }} + run: | + rm -rf test-results-arm* /tmp/frr-arm-ubuntu22.tar + docker stop frr-ubuntu-cont || true + docker rm frr-ubuntu-cont || true diff --git a/bfdd/bfd.c b/bfdd/bfd.c index f32bc2598b..164910556b 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -23,6 +23,7 @@ DEFINE_MTYPE_STATIC(BFDD, BFDD_CONFIG, "long-lived configuration memory"); DEFINE_MTYPE_STATIC(BFDD, BFDD_PROFILE, "long-lived profile memory"); DEFINE_MTYPE_STATIC(BFDD, BFDD_SESSION_OBSERVER, "Session observer"); DEFINE_MTYPE_STATIC(BFDD, BFDD_VRF, "BFD VRF"); +DEFINE_MTYPE_STATIC(BFDD, SBFD_REFLECTOR, "SBFD REFLECTOR"); /* * Prototypes @@ -39,6 +40,10 @@ static void bs_down_handler(struct bfd_session *bs, int nstate); static void bs_init_handler(struct bfd_session *bs, int nstate); static void bs_up_handler(struct bfd_session *bs, int nstate); +static void ptm_sbfd_echo_xmt_TO(struct bfd_session *bfd); +static void sbfd_down_handler(struct bfd_session *bs, int nstate); +static void sbfd_up_handler(struct bfd_session *bs, int nstate); + /** * Remove BFD profile from all BFD sessions so we don't leave dangling * pointers. @@ -192,10 +197,12 @@ void bfd_session_apply(struct bfd_session *bs) } /* Toggle 'passive-mode' if default value. */ - if (bs->peer_profile.passive == false) - bfd_set_passive_mode(bs, bp->passive); - else - bfd_set_passive_mode(bs, bs->peer_profile.passive); + if (bs->bfd_mode == BFD_MODE_TYPE_BFD) { + if (bs->peer_profile.passive == false) + bfd_set_passive_mode(bs, bp->passive); + else + bfd_set_passive_mode(bs, bs->peer_profile.passive); + } /* Toggle 'no shutdown' if default value. */ if (bs->peer_profile.admin_shutdown == false) @@ -222,10 +229,11 @@ void bfd_profile_remove(struct bfd_session *bs) bfd_session_apply(bs); } -void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, - struct sockaddr_any *local, bool mhop, const char *ifname, - const char *vrfname) +void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, struct sockaddr_any *local, + bool mhop, const char *ifname, const char *vrfname, const char *bfdname) { + struct vrf *vrf = NULL; + memset(key, 0, sizeof(*key)); switch (peer->sa_sin.sin_family) { @@ -248,10 +256,20 @@ void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, key->mhop = mhop; if (ifname && ifname[0]) strlcpy(key->ifname, ifname, sizeof(key->ifname)); - if (vrfname && vrfname[0]) - strlcpy(key->vrfname, vrfname, sizeof(key->vrfname)); - else + if (vrfname && vrfname[0] && strcmp(vrfname, VRF_DEFAULT_NAME) != 0) { + vrf = vrf_lookup_by_name(vrfname); + if (vrf) { + strlcpy(key->vrfname, vrf->name, sizeof(key->vrfname)); + } else { + strlcpy(key->vrfname, vrfname, sizeof(key->vrfname)); + } + } else { strlcpy(key->vrfname, VRF_DEFAULT_NAME, sizeof(key->vrfname)); + } + + if (bfdname && bfdname[0]) { + strlcpy(key->bfdname, bfdname, sizeof(key->bfdname)); + } } struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc) @@ -259,8 +277,8 @@ struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc) struct bfd_key key; /* Otherwise fallback to peer/local hash lookup. */ - gen_bfd_key(&key, &bpc->bpc_peer, &bpc->bpc_local, bpc->bpc_mhop, - bpc->bpc_localif, bpc->bpc_vrfname); + gen_bfd_key(&key, &bpc->bpc_peer, &bpc->bpc_local, bpc->bpc_mhop, bpc->bpc_localif, + bpc->bpc_vrfname, bpc->bfd_name); return bfd_key_lookup(key); } @@ -333,14 +351,24 @@ int bfd_session_enable(struct bfd_session *bs) * could use the destination port (3784) for the source * port we wouldn't need a socket per session. */ - if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) { + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + psock = bp_peer_srh_socketv6(bs); + if (psock <= 0) { + zlog_err("bp_peer_srh_socketv6 error"); + return 0; + } + } else if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) { psock = bp_peer_socket(bs); - if (psock == -1) + if (psock == -1) { + zlog_err("bp_peer_socket error"); return 0; + } } else { psock = bp_peer_socketv6(bs); - if (psock == -1) + if (psock == -1) { + zlog_err("bp_peer_socketv6 error"); return 0; + } } /* @@ -351,10 +379,18 @@ int bfd_session_enable(struct bfd_session *bs) /* Only start timers if we are using active mode. */ if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE) == 0) { - bfd_recvtimer_update(bs); - ptm_bfd_start_xmt_timer(bs, false); - } + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + /*enable receive echo response*/ + bfd_set_echo(bs, true); + bs->echo_detect_TO = (bs->remote_detect_mult * bs->echo_xmt_TO); + sbfd_echo_recvtimer_update(bs); + ptm_bfd_start_xmt_timer(bs, true); + } else { + bfd_recvtimer_update(bs); + ptm_bfd_start_xmt_timer(bs, false); + } + } /* initialize RTT */ bfd_rtt_init(bs); @@ -383,6 +419,8 @@ void bfd_session_disable(struct bfd_session *bs) bfd_recvtimer_delete(bs); bfd_xmttimer_delete(bs); ptm_bfd_echo_stop(bs); + bs->vrf = NULL; + bs->ifp = NULL; /* Set session down so it doesn't report UP and disabled. */ ptm_bfd_sess_dn(bs, BD_PATH_DOWN); @@ -422,10 +460,18 @@ void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo) jitter = (xmt_TO * (75 + (frr_weak_random() % maxpercent))) / 100; /* XXX remove that division above */ - if (is_echo) - bfd_echo_xmttimer_update(bfd, jitter); - else - bfd_xmttimer_update(bfd, jitter); + if (bfd->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bfd->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + if (is_echo) + sbfd_echo_xmttimer_update(bfd, jitter); + else + sbfd_init_xmttimer_update(bfd, jitter); + + } else { + if (is_echo) + bfd_echo_xmttimer_update(bfd, jitter); + else + bfd_xmttimer_update(bfd, jitter); + } } static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd) @@ -456,6 +502,37 @@ void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit) ptm_bfd_start_xmt_timer(bfd, false); } +static void ptm_sbfd_echo_xmt_TO(struct bfd_session *bfd) +{ + /* Send the scheduled sbfd-echo packet */ + ptm_sbfd_echo_snd(bfd); + + /* Restart the timer for next time */ + ptm_bfd_start_xmt_timer(bfd, true); +} + +void ptm_sbfd_init_xmt_TO(struct bfd_session *bfd, int fbit) +{ + /* Send the scheduled control packet */ + ptm_sbfd_initiator_snd(bfd, fbit); + + /* Restart the timer for next time */ + ptm_bfd_start_xmt_timer(bfd, false); +} + +void ptm_sbfd_init_reset(struct bfd_session *bfd) +{ + bfd->xmt_TO = BFD_DEF_SLOWTX; + bfd->detect_TO = 0; + ptm_sbfd_init_xmt_TO(bfd, 0); +} +void ptm_sbfd_echo_reset(struct bfd_session *bfd) +{ + bfd->echo_xmt_TO = SBFD_ECHO_DEF_SLOWTX; + bfd->echo_detect_TO = 0; + ptm_sbfd_echo_xmt_TO(bfd); +} + void ptm_bfd_echo_stop(struct bfd_session *bfd) { bfd->echo_xmt_TO = 0; @@ -550,12 +627,103 @@ void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag) UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET); memset(bfd->peer_hw_addr, 0, sizeof(bfd->peer_hw_addr)); /* reset local address ,it might has been be changed after bfd is up*/ - memset(&bfd->local_address, 0, sizeof(bfd->local_address)); + if (bfd->bfd_mode == BFD_MODE_TYPE_BFD) + memset(&bfd->local_address, 0, sizeof(bfd->local_address)); /* reset RTT */ bfd_rtt_init(bfd); } +/*sbfd session up , include sbfd and sbfd echo*/ +void ptm_sbfd_sess_up(struct bfd_session *bfd) +{ + int old_state = bfd->ses_state; + + bfd->local_diag = 0; + bfd->ses_state = PTM_BFD_UP; + monotime(&bfd->uptime); + + /*notify session up*/ + ptm_bfd_notify(bfd, bfd->ses_state); + + if (old_state != bfd->ses_state) { + bfd->stats.session_up++; + if (bglobal.debug_peer_event) + zlog_info("state-change: [%s] %s -> %s", bs_to_string(bfd), + state_list[old_state].str, state_list[bfd->ses_state].str); + } +} + +/*sbfd init session TO */ +void ptm_sbfd_init_sess_dn(struct bfd_session *bfd, uint8_t diag) +{ + int old_state = bfd->ses_state; + + bfd->local_diag = diag; + bfd->ses_state = PTM_BFD_DOWN; + bfd->polling = 0; + bfd->demand_mode = 0; + monotime(&bfd->downtime); + + /* + * Only attempt to send if we have a valid socket: + * this function might be called by session disablers and in + * this case we won't have a valid socket (i.e. interface was + * removed or VRF doesn't exist anymore). + */ + if (bfd->sock != -1) + ptm_sbfd_init_reset(bfd); + + /* Slow down the control packets, the connection is down. */ + bs_set_slow_timers(bfd); + + /* only signal clients when going from up->down state */ + if (old_state == PTM_BFD_UP) + ptm_bfd_notify(bfd, PTM_BFD_DOWN); + + /* Stop attempting to transmit or expect control packets if passive. */ + if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_PASSIVE)) { + sbfd_init_recvtimer_delete(bfd); + sbfd_init_xmttimer_delete(bfd); + } + + if (old_state != bfd->ses_state) { + bfd->stats.session_down++; + if (bglobal.debug_peer_event) + zlog_debug("state-change: [%s] %s -> %s reason:%s", bs_to_string(bfd), + state_list[old_state].str, state_list[bfd->ses_state].str, + get_diag_str(bfd->local_diag)); + } + /* reset local address ,it might has been be changed after bfd is up*/ + //memset(&bfd->local_address, 0, sizeof(bfd->local_address)); +} + +/*sbfd echo session TO */ +void ptm_sbfd_echo_sess_dn(struct bfd_session *bfd, uint8_t diag) +{ + int old_state = bfd->ses_state; + + bfd->local_diag = diag; + bfd->discrs.remote_discr = 0; + bfd->ses_state = PTM_BFD_DOWN; + bfd->polling = 0; + bfd->demand_mode = 0; + monotime(&bfd->downtime); + /* only signal clients when going from up->down state */ + if (old_state == PTM_BFD_UP) + ptm_bfd_notify(bfd, PTM_BFD_DOWN); + + ptm_sbfd_echo_reset(bfd); + + if (old_state != bfd->ses_state) { + bfd->stats.session_down++; + if (bglobal.debug_peer_event) + zlog_warn("state-change: [%s] %s -> %s reason:%s", bs_to_string(bfd), + state_list[old_state].str, state_list[bfd->ses_state].str, + get_diag_str(bfd->local_diag)); + } +} + static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa, uint32_t ldisc) { @@ -599,7 +767,7 @@ struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp, vrf = vrf_lookup_by_id(vrfid); gen_bfd_key(&key, peer, local, is_mhop, ifp ? ifp->name : NULL, - vrf ? vrf->name : VRF_DEFAULT_NAME); + vrf ? vrf->name : VRF_DEFAULT_NAME, NULL); /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */ return bfd_key_lookup(key); @@ -620,6 +788,21 @@ void bfd_echo_xmt_cb(struct event *t) ptm_bfd_echo_xmt_TO(bs); } +void sbfd_init_xmt_cb(struct event *t) +{ + struct bfd_session *bs = EVENT_ARG(t); + + ptm_sbfd_init_xmt_TO(bs, 0); +} + +void sbfd_echo_xmt_cb(struct event *t) +{ + struct bfd_session *bs = EVENT_ARG(t); + + if (bs->echo_xmt_TO > 0) + ptm_sbfd_echo_xmt_TO(bs); +} + /* Was ptm_bfd_detect_TO() */ void bfd_recvtimer_cb(struct event *t) { @@ -638,6 +821,11 @@ void bfd_echo_recvtimer_cb(struct event *t) { struct bfd_session *bs = EVENT_ARG(t); + if (bglobal.debug_peer_event) { + zlog_debug("%s: time-out bfd: [%s] bfd'state is %s", __func__, bs_to_string(bs), + state_list[bs->ses_state].str); + } + switch (bs->ses_state) { case PTM_BFD_INIT: case PTM_BFD_UP: @@ -646,11 +834,49 @@ void bfd_echo_recvtimer_cb(struct event *t) } } -struct bfd_session *bfd_session_new(void) +void sbfd_init_recvtimer_cb(struct event *t) +{ + struct bfd_session *bs = EVENT_ARG(t); + + switch (bs->ses_state) { + case PTM_BFD_INIT: + case PTM_BFD_UP: + ptm_sbfd_init_sess_dn(bs, BD_PATH_DOWN); + break; + + default: + /* Second detect time expiration, zero remote discr (section + * 6.5.1) + */ + break; + } +} +void sbfd_echo_recvtimer_cb(struct event *t) +{ + struct bfd_session *bs = EVENT_ARG(t); + + if (bglobal.debug_peer_event) { + zlog_debug("%s: time-out bfd: [%s] bfd'state is %s", __func__, bs_to_string(bs), + state_list[bs->ses_state].str); + } + + switch (bs->ses_state) { + case PTM_BFD_INIT: + case PTM_BFD_UP: + ptm_sbfd_echo_sess_dn(bs, BD_PATH_DOWN); + break; + case PTM_BFD_DOWN: + break; + } +} + +struct bfd_session *bfd_session_new(enum bfd_mode_type mode) { struct bfd_session *bs; - bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(*bs)); + bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(struct bfd_session)); + bs->segnum = 0; + bs->bfd_mode = mode; /* Set peer session defaults. */ bfd_profile_set_default(&bs->peer_profile); @@ -788,7 +1014,7 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc) } /* Get BFD session storage with its defaults. */ - bfd = bfd_session_new(); + bfd = bfd_session_new(BFD_MODE_TYPE_BFD); /* * Store interface/VRF name in case we need to delay session @@ -970,6 +1196,30 @@ static void bs_down_handler(struct bfd_session *bs, int nstate) } } +static void sbfd_down_handler(struct bfd_session *bs, int nstate) +{ + switch (nstate) { + case PTM_BFD_ADM_DOWN: + /* + * Remote peer doesn't want to talk, so lets keep the + * connection down. + */ + break; + case PTM_BFD_UP: + /* down - > up*/ + ptm_sbfd_sess_up(bs); + break; + + case PTM_BFD_DOWN: + break; + + default: + if (bglobal.debug_peer_event) + zlog_err("state-change: unhandled sbfd state: %d", nstate); + break; + } +} + static void bs_init_handler(struct bfd_session *bs, int nstate) { switch (nstate) { @@ -1021,6 +1271,29 @@ static void bs_up_handler(struct bfd_session *bs, int nstate) } } +static void sbfd_up_handler(struct bfd_session *bs, int nstate) +{ + switch (nstate) { + case PTM_BFD_ADM_DOWN: + case PTM_BFD_DOWN: + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + ptm_sbfd_echo_sess_dn(bs, BD_ECHO_FAILED); + } else + ptm_sbfd_init_sess_dn(bs, BD_ECHO_FAILED); + + break; + + case PTM_BFD_UP: + /* Path is up and working. */ + break; + + default: + if (bglobal.debug_peer_event) + zlog_debug("state-change: unhandled neighbor state: %d", nstate); + break; + } +} + void bs_state_handler(struct bfd_session *bs, int nstate) { switch (bs->ses_state) { @@ -1045,6 +1318,58 @@ void bs_state_handler(struct bfd_session *bs, int nstate) } } +void sbfd_echo_state_handler(struct bfd_session *bs, int nstate) +{ + if (bglobal.debug_peer_event) + zlog_debug("%s: bfd(%u) state: %s , notify state: %s", __func__, + bs->discrs.my_discr, state_list[bs->ses_state].str, + state_list[nstate].str); + + switch (bs->ses_state) { + case PTM_BFD_ADM_DOWN: + // bs_admin_down_handler(bs, nstate); + break; + case PTM_BFD_DOWN: + sbfd_down_handler(bs, nstate); + break; + case PTM_BFD_UP: + sbfd_up_handler(bs, nstate); + break; + + default: + if (bglobal.debug_peer_event) + zlog_debug("state-change: [%s] is in invalid state: %d", bs_to_string(bs), + nstate); + break; + } +} + +void sbfd_initiator_state_handler(struct bfd_session *bs, int nstate) +{ + if (bglobal.debug_peer_event) + zlog_debug("%s: sbfd(%u) state: %s , notify state: %s", __func__, + bs->discrs.my_discr, state_list[bs->ses_state].str, + state_list[nstate].str); + + switch (bs->ses_state) { + case PTM_BFD_ADM_DOWN: + // bs_admin_down_handler(bs, nstate); + break; + case PTM_BFD_DOWN: + sbfd_down_handler(bs, nstate); + break; + case PTM_BFD_UP: + sbfd_up_handler(bs, nstate); + break; + + default: + if (bglobal.debug_peer_event) + zlog_debug("state-change: [%s] is in invalid state: %d", bs_to_string(bs), + nstate); + break; + } +} + /* * Handles echo timer manipulation after updating timer. */ @@ -1147,6 +1472,15 @@ void bs_set_slow_timers(struct bfd_session *bs) /* Set the appropriated timeouts for slow connection. */ bs->detect_TO = (BFD_DEFDETECTMULT * BFD_DEF_SLOWTX); bs->xmt_TO = BFD_DEF_SLOWTX; + + /* add for sbfd-echo slow connection */ + if (BFD_MODE_TYPE_SBFD_ECHO == bs->bfd_mode) { + bs->echo_xmt_TO = SBFD_ECHO_DEF_SLOWTX; + bs->timers.desired_min_echo_tx = BFD_DEFDESIREDMINTX; + bs->timers.required_min_echo_rx = BFD_DEFDESIREDMINTX; + bs->peer_profile.min_echo_rx = BFD_DEFDESIREDMINTX; + bs->peer_profile.min_echo_tx = BFD_DEFDESIREDMINTX; + } } void bfd_set_echo(struct bfd_session *bs, bool echo) @@ -1438,6 +1772,8 @@ const char *bs_to_string(const struct bfd_session *bs) if (bs->key.ifname[0]) pos += snprintf(buf + pos, sizeof(buf) - pos, " ifname:%s", bs->key.ifname); + if (bs->bfd_name[0]) + pos += snprintf(buf + pos, sizeof(buf) - pos, " bfd_name:%s", bs->bfd_name); (void)pos; @@ -1516,6 +1852,10 @@ void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc) static struct hash *bfd_id_hash; static struct hash *bfd_key_hash; +/*sbfd reflector discr hash*/ +static struct hash *sbfd_rflt_hash; +static unsigned int sbfd_discr_hash_do(const void *p); + static unsigned int bfd_id_hash_do(const void *p); static unsigned int bfd_key_hash_do(const void *p); @@ -1567,6 +1907,8 @@ static bool bfd_key_hash_cmp(const void *n1, const void *n2) if (memcmp(bs1->key.vrfname, bs2->key.vrfname, sizeof(bs1->key.vrfname))) return false; + if (memcmp(bs1->key.bfdname, bs2->key.bfdname, sizeof(bs1->key.bfdname))) + return false; /* * Local address is optional and can be empty. @@ -1591,6 +1933,20 @@ static bool bfd_key_hash_cmp(const void *n1, const void *n2) return true; } +/* SBFD disr hash . */ +static unsigned int sbfd_discr_hash_do(const void *p) +{ + const struct sbfd_reflector *sr = p; + + return jhash_1word(sr->discr, 0); +} + +static bool sbfd_discr_hash_cmp(const void *n1, const void *n2) +{ + const struct sbfd_reflector *sr1 = n1, *sr2 = n2; + + return sr1->discr == sr2->discr; +} /* * Hash public interface / exported functions. @@ -1615,6 +1971,15 @@ struct bfd_session *bfd_key_lookup(struct bfd_key key) return hash_lookup(bfd_key_hash, &bs); } +struct sbfd_reflector *sbfd_discr_lookup(uint32_t discr) +{ + struct sbfd_reflector sr; + + sr.discr = discr; + + return hash_lookup(sbfd_rflt_hash, &sr); +} + /* * Delete functions. * @@ -1643,6 +2008,15 @@ struct bfd_session *bfd_key_delete(struct bfd_key key) return hash_release(bfd_key_hash, &bs); } +struct sbfd_reflector *sbfd_discr_delete(uint32_t discr) +{ + struct sbfd_reflector sr; + + sr.discr = discr; + + return hash_release(sbfd_rflt_hash, &sr); +} + /* Iteration functions. */ void bfd_id_iterate(hash_iter_func hif, void *arg) { @@ -1654,6 +2028,11 @@ void bfd_key_iterate(hash_iter_func hif, void *arg) hash_iterate(bfd_key_hash, hif, arg); } +void sbfd_discr_iterate(hash_iter_func hif, void *arg) +{ + hash_iterate(sbfd_rflt_hash, hif, arg); +} + /* * Insert functions. * @@ -1670,12 +2049,24 @@ bool bfd_key_insert(struct bfd_session *bs) return (hash_get(bfd_key_hash, bs, hash_alloc_intern) == bs); } +bool sbfd_discr_insert(struct sbfd_reflector *sr) +{ + return (hash_get(sbfd_rflt_hash, sr, hash_alloc_intern) == sr); +} + +unsigned long sbfd_discr_get_count(void) +{ + return sbfd_rflt_hash->count; +} + void bfd_initialize(void) { bfd_id_hash = hash_create(bfd_id_hash_do, bfd_id_hash_cmp, "BFD session discriminator hash"); bfd_key_hash = hash_create(bfd_key_hash_do, bfd_key_hash_cmp, "BFD session hash"); + sbfd_rflt_hash = hash_create(sbfd_discr_hash_do, sbfd_discr_hash_cmp, + "SBFD reflector discriminator hash"); TAILQ_INIT(&bplist); } @@ -1687,6 +2078,14 @@ static void _bfd_free(struct hash_bucket *hb, bfd_session_free(bs); } +static void _sbfd_reflector_free(struct hash_bucket *hb, void *arg __attribute__((__unused__))) +{ + struct sbfd_reflector *sr = hb->data; + + + sbfd_reflector_free(sr->discr); +} + void bfd_shutdown(void) { struct bfd_profile *bp; @@ -1701,9 +2100,13 @@ void bfd_shutdown(void) bfd_id_iterate(_bfd_free, NULL); assert(bfd_key_hash->count == 0); + sbfd_discr_iterate(_sbfd_reflector_free, NULL); + assert(sbfd_rflt_hash->count == 0); + /* Now free the hashes themselves. */ hash_free(bfd_id_hash); hash_free(bfd_key_hash); + hash_free(sbfd_rflt_hash); /* Free all profile allocations. */ while ((bp = TAILQ_FIRST(&bplist)) != NULL) @@ -1713,6 +2116,7 @@ void bfd_shutdown(void) struct bfd_session_iterator { int bsi_stop; bool bsi_mhop; + uint32_t bsi_bfdmode; const struct bfd_session *bsi_bs; }; @@ -1724,7 +2128,7 @@ static int _bfd_session_next(struct hash_bucket *hb, void *arg) /* Previous entry signaled stop. */ if (bsi->bsi_stop == 1) { /* Match the single/multi hop sessions. */ - if (bs->key.mhop != bsi->bsi_mhop) + if ((bs->key.mhop != bsi->bsi_mhop) || (bs->bfd_mode != bsi->bsi_bfdmode)) return HASHWALK_CONTINUE; bsi->bsi_bs = bs; @@ -1736,7 +2140,8 @@ static int _bfd_session_next(struct hash_bucket *hb, void *arg) bsi->bsi_stop = 1; /* Set entry to NULL to signal end of list. */ bsi->bsi_bs = NULL; - } else if (bsi->bsi_bs == NULL && bsi->bsi_mhop == bs->key.mhop) { + } else if (bsi->bsi_bs == NULL && bsi->bsi_mhop == bs->key.mhop && + bsi->bsi_bfdmode == bs->bfd_mode) { /* We want the first list item. */ bsi->bsi_stop = 1; bsi->bsi_bs = hb->data; @@ -1751,14 +2156,15 @@ static int _bfd_session_next(struct hash_bucket *hb, void *arg) * * `bs` might point to NULL to get the first item of the data structure. */ -const struct bfd_session *bfd_session_next(const struct bfd_session *bs, - bool mhop) +const struct bfd_session *bfd_session_next(const struct bfd_session *bs, bool mhop, + uint32_t bfd_mode) { struct bfd_session_iterator bsi; bsi.bsi_stop = 0; bsi.bsi_bs = bs; bsi.bsi_mhop = mhop; + bsi.bsi_bfdmode = bfd_mode; hash_walk(bfd_key_hash, _bfd_session_next, &bsi); if (bsi.bsi_stop == 0) return NULL; @@ -1924,6 +2330,7 @@ static int bfd_vrf_new(struct vrf *vrf) bvrf->bg_mhop6 = -1; bvrf->bg_echo = -1; bvrf->bg_echov6 = -1; + bvrf->bg_initv6 = -1; return 0; } @@ -1957,6 +2364,8 @@ static int bfd_vrf_enable(struct vrf *vrf) bvrf->bg_shop6 = bp_udp6_shop(vrf); if (bvrf->bg_mhop6 == -1) bvrf->bg_mhop6 = bp_udp6_mhop(vrf); + if (bvrf->bg_initv6 == -1) + bvrf->bg_initv6 = bp_initv6_socket(vrf); if (bvrf->bg_ev[0] == NULL && bvrf->bg_shop != -1) event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop, @@ -1970,6 +2379,8 @@ static int bfd_vrf_enable(struct vrf *vrf) if (bvrf->bg_ev[3] == NULL && bvrf->bg_mhop6 != -1) event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6, &bvrf->bg_ev[3]); + if (bvrf->bg_ev[6] == NULL && bvrf->bg_initv6 != -1) + event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_initv6, &bvrf->bg_ev[6]); /* Toggle echo if VRF was disabled. */ bfd_vrf_toggle_echo(bvrf); @@ -2006,6 +2417,7 @@ static int bfd_vrf_disable(struct vrf *vrf) EVENT_OFF(bvrf->bg_ev[3]); EVENT_OFF(bvrf->bg_ev[4]); EVENT_OFF(bvrf->bg_ev[5]); + EVENT_OFF(bvrf->bg_ev[6]); /* Close all descriptors. */ socket_close(&bvrf->bg_echo); @@ -2014,6 +2426,7 @@ static int bfd_vrf_disable(struct vrf *vrf) socket_close(&bvrf->bg_shop6); socket_close(&bvrf->bg_mhop6); socket_close(&bvrf->bg_echov6); + socket_close(&bvrf->bg_initv6); return 0; } @@ -2050,6 +2463,79 @@ unsigned long bfd_get_session_count(void) return bfd_key_hash->count; } +struct sbfd_reflector *sbfd_reflector_new(const uint32_t discr, struct in6_addr *sip) +{ + struct sbfd_reflector *sr; + + sr = sbfd_discr_lookup(discr); + if (sr) + return sr; + + sr = XCALLOC(MTYPE_SBFD_REFLECTOR, sizeof(*sr)); + sr->discr = discr; + memcpy(&sr->local, sip, sizeof(struct in6_addr)); + + sbfd_discr_insert(sr); + + + return sr; +} + +void sbfd_reflector_free(const uint32_t discr) +{ + struct sbfd_reflector *sr; + + sr = sbfd_discr_lookup(discr); + if (!sr) + return; + + sbfd_discr_delete(discr); + XFREE(MTYPE_SBFD_REFLECTOR, sr); + + return; +} + +void sbfd_reflector_flush() +{ + sbfd_discr_iterate(_sbfd_reflector_free, NULL); + return; +} + +struct bfd_session_name_match_unique { + const char *bfd_name; + struct bfd_session *bfd_found; +}; + +static int _bfd_session_name_cmp(struct hash_bucket *hb, void *arg) +{ + struct bfd_session *bs = hb->data; + struct bfd_session_name_match_unique *match = (struct bfd_session_name_match_unique *)arg; + + if (strlen(bs->bfd_name) != strlen(match->bfd_name)) { + return HASHWALK_CONTINUE; + } + + if (!strncmp(bs->bfd_name, match->bfd_name, strlen(bs->bfd_name))) { + match->bfd_found = bs; + return HASHWALK_ABORT; + } + return HASHWALK_CONTINUE; +} + +struct bfd_session *bfd_session_get_by_name(const char *name) +{ + if (!name || name[0] == '\0') + return NULL; + + struct bfd_session_name_match_unique match; + match.bfd_name = name; + match.bfd_found = NULL; + + hash_walk(bfd_key_hash, _bfd_session_name_cmp, &match); + + return match.bfd_found; +} + void bfd_rtt_init(struct bfd_session *bfd) { uint8_t i; diff --git a/bfdd/bfd.h b/bfdd/bfd.h index d4d14ffce6..d9119d16c2 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -19,6 +19,7 @@ #include "lib/qobj.h" #include "lib/queue.h" #include "lib/vrf.h" +#include "lib/bfd.h" #ifdef BFD_DEBUG #define BFDD_JSON_CONV_OPTIONS (JSON_C_TO_STRING_PRETTY) @@ -86,6 +87,10 @@ struct bfd_peer_cfg { bool bpc_has_profile; char bpc_profile[64]; + + vrf_id_t vrf_id; + char bfd_name[BFD_NAME_SIZE + 1]; + uint8_t bfd_name_len; }; /* bfd Authentication Type. */ @@ -147,7 +152,6 @@ struct bfd_echo_pkt { uint64_t time_sent_usec; }; - /* Macros for manipulating control packets */ #define BFD_VERMASK 0x07 #define BFD_DIAGMASK 0x1F @@ -194,6 +198,8 @@ struct bfd_echo_pkt { #define BFD_ECHO_VERSION 1 #define BFD_ECHO_PKT_LEN sizeof(struct bfd_echo_pkt) +#define RTH_BASE_HEADER_LEN 8 +#define GET_RTH_HDR_LEN(size) (((size) >> 3) - 1) enum bfd_diagnosticis { BD_OK = 0, /* Control Detection Time Expired. */ @@ -235,6 +241,12 @@ enum bfd_session_flags { BFD_SESS_FLAG_MAC_SET = 1 << 11, /* MAC of peer known */ }; +enum bfd_mode_type { + BFD_MODE_TYPE_BFD = 0, + BFD_MODE_TYPE_SBFD_ECHO = 1, + BFD_MODE_TYPE_SBFD_INIT = 2, +}; + /* * BFD session hash key. * @@ -254,6 +266,7 @@ struct bfd_key { struct in6_addr local; char ifname[IFNAMSIZ]; char vrfname[VRF_NAMSIZ]; + char bfdname[BFD_NAME_SIZE + 1]; } __attribute__((packed)); struct bfd_session_stats { @@ -264,6 +277,7 @@ struct bfd_session_stats { uint64_t session_up; uint64_t session_down; uint64_t znotification; + uint64_t tx_fail_pkt; }; /** @@ -375,6 +389,12 @@ struct bfd_session { uint8_t rtt_valid; /* number of valid samples */ uint8_t rtt_index; /* last index added */ uint64_t rtt[BFD_RTT_SAMPLE]; /* RRT in usec for echo to be looped */ + char bfd_name[BFD_NAME_SIZE + 1]; + + uint32_t bfd_mode; + uint8_t segnum; + struct in6_addr out_sip6; + struct in6_addr seg_list[SRV6_MAX_SEGS]; }; struct bfd_diag_str_list { @@ -396,6 +416,11 @@ struct bfd_session_observer { }; TAILQ_HEAD(obslist, bfd_session_observer); +/*sbfd reflector struct*/ +struct sbfd_reflector { + uint32_t discr; + struct in6_addr local; +}; /* States defined per 4.1 */ #define PTM_BFD_ADM_DOWN 0 @@ -413,6 +438,7 @@ TAILQ_HEAD(obslist, bfd_session_observer); #define BFD_DEF_DES_MIN_ECHO_TX (50 * 1000) /* microseconds. */ #define BFD_DEF_REQ_MIN_ECHO_RX (50 * 1000) /* microseconds. */ #define BFD_DEF_SLOWTX (1000 * 1000) /* microseconds. */ +#define SBFD_ECHO_DEF_SLOWTX (1000 * 1000) /* microseconds. */ /** Minimum multi hop TTL. */ #define BFD_DEF_MHOP_TTL 254 #define BFD_PKT_LEN 24 /* Length of control packet */ @@ -427,7 +453,9 @@ TAILQ_HEAD(obslist, bfd_session_observer); #define BFD_DEFDESTPORT 3784 #define BFD_DEF_ECHO_PORT 3785 #define BFD_DEF_MHOP_DEST_PORT 4784 +#define BFD_DEF_SBFD_DEST_PORT 7784 +#define BFD_SBFD_INITIATOR_DEMAND 1 /* * bfdd.c @@ -441,9 +469,10 @@ struct bfd_vrf_global { int bg_mhop6; int bg_echo; int bg_echov6; + int bg_initv6; struct vrf *vrf; - struct event *bg_ev[6]; + struct event *bg_ev[7]; }; /* Forward declaration of data plane context struct. */ @@ -519,6 +548,7 @@ int bp_set_ttl(int sd, uint8_t value); int bp_set_tosv6(int sd, uint8_t value); int bp_set_tos(int sd, uint8_t value); int bp_bind_dev(int sd, const char *dev); +void bp_set_prio(int sd, int value); int bp_udp_shop(const struct vrf *vrf); int bp_udp_mhop(const struct vrf *vrf); @@ -528,10 +558,15 @@ int bp_peer_socket(const struct bfd_session *bs); int bp_peer_socketv6(const struct bfd_session *bs); int bp_echo_socket(const struct vrf *vrf); int bp_echov6_socket(const struct vrf *vrf); +int bp_peer_srh_socketv6(struct bfd_session *bs); +int bp_sbfd_socket(const struct vrf *vrf); +int bp_initv6_socket(const struct vrf *vrf); void ptm_bfd_snd(struct bfd_session *bfd, int fbit); void ptm_bfd_echo_snd(struct bfd_session *bfd); void ptm_bfd_echo_fp_snd(struct bfd_session *bfd); +void ptm_sbfd_echo_snd(struct bfd_session *bfd); +void ptm_sbfd_initiator_snd(struct bfd_session *bfd, int fbit); void bfd_recv_cb(struct event *t); @@ -545,13 +580,21 @@ typedef void (*bfd_ev_cb)(struct event *t); void bfd_recvtimer_update(struct bfd_session *bs); void bfd_echo_recvtimer_update(struct bfd_session *bs); +void sbfd_init_recvtimer_update(struct bfd_session *bs); +void sbfd_echo_recvtimer_update(struct bfd_session *bs); void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter); void bfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter); +void sbfd_init_xmttimer_update(struct bfd_session *bs, uint64_t jitter); +void sbfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter); void bfd_xmttimer_delete(struct bfd_session *bs); void bfd_echo_xmttimer_delete(struct bfd_session *bs); +void sbfd_init_xmttimer_delete(struct bfd_session *bs); +void sbfd_echo_xmttimer_delete(struct bfd_session *bs); void bfd_recvtimer_delete(struct bfd_session *bs); void bfd_echo_recvtimer_delete(struct bfd_session *bs); +void sbfd_init_recvtimer_delete(struct bfd_session *bs); +void sbfd_echo_recvtimer_delete(struct bfd_session *bs); void bfd_recvtimer_assign(struct bfd_session *bs, bfd_ev_cb cb, int sd); void bfd_echo_recvtimer_assign(struct bfd_session *bs, bfd_ev_cb cb, int sd); @@ -574,6 +617,9 @@ void ptm_bfd_echo_stop(struct bfd_session *bfd); void ptm_bfd_echo_start(struct bfd_session *bfd); void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit); void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo); +void ptm_sbfd_init_xmt_TO(struct bfd_session *bfd, int fbit); +void ptm_sbfd_init_reset(struct bfd_session *bfd); +void ptm_sbfd_echo_reset(struct bfd_session *bfd); struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp, struct sockaddr_any *peer, struct sockaddr_any *local, @@ -598,16 +644,16 @@ void bs_observer_del(struct bfd_session_observer *bso); void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc); -void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, - struct sockaddr_any *local, bool mhop, const char *ifname, - const char *vrfname); -struct bfd_session *bfd_session_new(void); +void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, struct sockaddr_any *local, + bool mhop, const char *ifname, const char *vrfname, const char *bfdname); +struct bfd_session *bfd_session_new(enum bfd_mode_type mode); struct bfd_session *bs_registrate(struct bfd_session *bs); void bfd_session_free(struct bfd_session *bs); -const struct bfd_session *bfd_session_next(const struct bfd_session *bs, - bool mhop); +const struct bfd_session *bfd_session_next(const struct bfd_session *bs, bool mhop, + uint32_t bfd_mode); void bfd_sessions_remove_manual(void); void bfd_profiles_remove(void); +void bs_sbfd_echo_timer_handler(struct bfd_session *bs); void bfd_rtt_init(struct bfd_session *bfd); extern void bfd_vrf_toggle_echo(struct bfd_vrf_global *bfd_vrf); @@ -653,18 +699,22 @@ void bfd_vrf_terminate(void); struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd); struct bfd_session *bfd_id_lookup(uint32_t id); struct bfd_session *bfd_key_lookup(struct bfd_key key); - +struct sbfd_reflector *sbfd_discr_lookup(uint32_t discr); struct bfd_session *bfd_id_delete(uint32_t id); struct bfd_session *bfd_key_delete(struct bfd_key key); +struct sbfd_reflector *sbfd_discr_delete(uint32_t discr); bool bfd_id_insert(struct bfd_session *bs); bool bfd_key_insert(struct bfd_session *bs); +bool sbfd_discr_insert(struct sbfd_reflector *sr); typedef void (*hash_iter_func)(struct hash_bucket *hb, void *arg); void bfd_id_iterate(hash_iter_func hif, void *arg); void bfd_key_iterate(hash_iter_func hif, void *arg); +void sbfd_discr_iterate(hash_iter_func hif, void *arg); unsigned long bfd_get_session_count(void); +unsigned long sbfd_discr_get_count(void); /* Export callback functions for `event.c`. */ extern struct event_loop *master; @@ -674,6 +724,11 @@ void bfd_echo_recvtimer_cb(struct event *t); void bfd_xmt_cb(struct event *t); void bfd_echo_xmt_cb(struct event *t); +void sbfd_init_recvtimer_cb(struct event *t); +void sbfd_echo_recvtimer_cb(struct event *t); +void sbfd_init_xmt_cb(struct event *t); +void sbfd_echo_xmt_cb(struct event *t); + extern struct in6_addr zero_addr; /** @@ -809,4 +864,17 @@ int bfd_dplane_update_session_counters(struct bfd_session *bs); void bfd_dplane_show_counters(struct vty *vty); +/*sbfd relfector*/ +struct sbfd_reflector *sbfd_reflector_new(const uint32_t discr, struct in6_addr *sip); +void sbfd_reflector_free(const uint32_t discr); +void sbfd_reflector_flush(void); + +/*sbfd*/ +void ptm_sbfd_echo_sess_dn(struct bfd_session *bfd, uint8_t diag); +void ptm_sbfd_init_sess_dn(struct bfd_session *bfd, uint8_t diag); +void ptm_sbfd_sess_up(struct bfd_session *bfd); +void sbfd_echo_state_handler(struct bfd_session *bs, int nstate); +void sbfd_initiator_state_handler(struct bfd_session *bs, int nstate); + +struct bfd_session *bfd_session_get_by_name(const char *name); #endif /* _BFD_H_ */ diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index f9397fa128..cecced3ccc 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -16,20 +16,28 @@ #ifdef GNU_LINUX #include <linux/filter.h> +#include <linux/seg6.h> +#include <linux/ipv6.h> #endif #ifdef BFD_LINUX #include <linux/if_packet.h> +#include <linux/seg6.h> +#include <linux/ipv6.h> #endif /* BFD_LINUX */ #include <netinet/if_ether.h> #include <netinet/udp.h> +#include <netinet/ip6.h> +#include <ifaddrs.h> #include "lib/sockopt.h" #include "lib/checksum.h" #include "lib/network.h" #include "bfd.h" +#define BUF_SIZ 1024 +#define SOCK_OPT_PRIO_HIGH 6 /* * Prototypes @@ -49,12 +57,19 @@ int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen, struct sockaddr *to, socklen_t tolen); int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl, uint32_t *my_discr, uint64_t *my_rtt); +static int ptm_bfd_reflector_process_init_packet(struct bfd_vrf_global *bvrf, int s); +int _ptm_sbfd_init_send(struct bfd_session *bs, const void *data, size_t datalen); + #ifdef BFD_LINUX -ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen, - uint8_t *ttl, ifindex_t *ifindex, - struct sockaddr_any *local, struct sockaddr_any *peer); -void bfd_peer_mac_set(int sd, struct bfd_session *bfd, - struct sockaddr_any *peer, struct interface *ifp); +static int bp_raw_sbfd_red_send(int sd, uint8_t *data, size_t datalen, uint16_t family, + struct in6_addr *out_sip, struct in6_addr *sip, + struct in6_addr *dip, uint16_t src_port, uint16_t dst_port, + uint8_t seg_num, struct in6_addr *segment_list); +static ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, + ifindex_t *ifindex, struct sockaddr_any *local, + struct sockaddr_any *peer); +static void bfd_peer_mac_set(int sd, struct bfd_session *bfd, struct sockaddr_any *peer, + struct interface *ifp); int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen, struct bfd_session *bfd); ssize_t bfd_recv_fp_echo(int sd, uint8_t *msgbuf, size_t msgbuflen, @@ -374,7 +389,24 @@ static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s) /* Compute detect time */ bfd->echo_detect_TO = bfd->remote_detect_mult * bfd->echo_xmt_TO; - /* Update echo receive timeout. */ + /* Update sbfd-echo session state */ + if (bfd->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + sbfd_echo_state_handler(bfd, PTM_BFD_UP); + + if (bfd->echo_xmt_TO != bfd->timers.desired_min_echo_tx) { + bfd->echo_xmt_TO = bfd->timers.desired_min_echo_tx; + //reset xmt timer TO after UP + ptm_bfd_start_xmt_timer(bfd, true); + } + + bfd->echo_detect_TO = bfd->detect_mult * bfd->echo_xmt_TO; + /* Update sbfd echo receive timeout. */ + if (bfd->echo_detect_TO > 0) + sbfd_echo_recvtimer_update(bfd); + return 0; + } + + /* Update bfd-echo receive timeout. */ if (bfd->echo_detect_TO > 0) bfd_echo_recvtimer_update(bfd); @@ -438,9 +470,9 @@ void ptm_bfd_snd(struct bfd_session *bfd, int fbit) /* * receive the ipv4 echo packet that was loopback in the peers forwarding plane */ -ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen, - uint8_t *ttl, ifindex_t *ifindex, - struct sockaddr_any *local, struct sockaddr_any *peer) +static ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, + ifindex_t *ifindex, struct sockaddr_any *local, + struct sockaddr_any *peer) { ssize_t mlen; struct sockaddr_ll msgaddr; @@ -729,6 +761,9 @@ static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd) EVENT_OFF(bvrf->bg_ev[5]); event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6, &bvrf->bg_ev[5]); + } else if (sd == bvrf->bg_initv6) { + EVENT_OFF(bvrf->bg_ev[6]); + event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_initv6, &bvrf->bg_ev[6]); } } @@ -822,6 +857,11 @@ void bfd_recv_cb(struct event *t) /* Schedule next read. */ bfd_sd_reschedule(bvrf, sd); + /* The reflector handle SBFD init packets. */ + if (sd == bvrf->bg_initv6) { + ptm_bfd_reflector_process_init_packet(bvrf, sd); + return; + } /* Handle echo packets. */ if (sd == bvrf->bg_echo || sd == bvrf->bg_echov6) { ptm_bfd_process_echo_pkt(bvrf, sd); @@ -996,6 +1036,29 @@ void bfd_recv_cb(struct event *t) else bfd->remote_cbit = 0; + /* The initiator handle SBFD reflect packet. */ + if (bfd->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + sbfd_initiator_state_handler(bfd, PTM_BFD_UP); + if (bfd->xmt_TO != bfd->timers.desired_min_tx) { + bfd->xmt_TO = bfd->timers.desired_min_tx; + //reset xmt timer TO after UP + ptm_bfd_start_xmt_timer(bfd, false); + } + + bfd->detect_TO = bfd->detect_mult * bfd->xmt_TO; + sbfd_init_recvtimer_update(bfd); + + if (bfd->polling && BFD_GETFBIT(cp->flags)) { + /* Disable polling. */ + bfd->polling = 0; + /* Start using our new timers. */ + bfd->cur_timers.desired_min_tx = bfd->timers.desired_min_tx; + bfd->cur_timers.required_min_rx = bfd->timers.required_min_rx; + } + + return; + } + /* State switch from section 6.2. */ bs_state_handler(bfd, BFD_GETSTATE(cp->flags)); @@ -1352,6 +1415,16 @@ static void bp_bind_ip(int sd, uint16_t port) zlog_fatal("bind-ip: bind: %s", strerror(errno)); } +void bp_set_prio(int sd, int value) +{ +#if defined(GNU_LINUX) + int priority = value; + + if (setsockopt(sd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) + zlog_warn("set_prio: setsockopt(SO_PRIORITY, %d): %s", value, strerror(errno)); +#endif +} + int bp_udp_shop(const struct vrf *vrf) { int sd; @@ -1421,6 +1494,8 @@ int bp_peer_socket(const struct bfd_session *bs) return -1; } + bp_set_prio(sd, SOCK_OPT_PRIO_HIGH); + /* Find an available source port in the proper range */ memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; @@ -1487,6 +1562,8 @@ int bp_peer_socketv6(const struct bfd_session *bs) return -1; } + bp_set_prio(sd, SOCK_OPT_PRIO_HIGH); + /* Find an available source port in the proper range */ memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; @@ -1731,8 +1808,8 @@ int bp_echov6_socket(const struct vrf *vrf) /* get peer's mac address to be used with Echo packets when they are looped in * peers forwarding plane */ -void bfd_peer_mac_set(int sd, struct bfd_session *bfd, - struct sockaddr_any *peer, struct interface *ifp) +static void bfd_peer_mac_set(int sd, struct bfd_session *bfd, struct sockaddr_any *peer, + struct interface *ifp) { struct arpreq arpreq_; @@ -1768,3 +1845,727 @@ void bfd_peer_mac_set(int sd, struct bfd_session *bfd, } } #endif + +int _ptm_sbfd_init_send(struct bfd_session *bfd, const void *data, size_t datalen) +{ +#ifdef BFD_LINUX + int sd = -1; + struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd); + + int seg_num; + struct in6_addr *segment_list = NULL; + struct in6_addr peer; + struct in6_addr local; + + if (!bvrf) + return -1; + + seg_num = bfd->segnum; + if (seg_num > 0) + segment_list = bfd->seg_list; + + sd = bfd->sock; + + local = bfd->key.local; + peer = bfd->key.peer; + + /*SBFD Control pkt dst port should be 7784, src port can be any but NOT 7784 according to RFC7881 */ + if (bp_raw_sbfd_red_send(sd, (uint8_t *)data, datalen, bfd->key.family, &bfd->out_sip6, + &local, &peer, + CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MH) ? BFD_DEF_MHOP_DEST_PORT + : BFD_DEFDESTPORT, + BFD_DEF_SBFD_DEST_PORT, seg_num, segment_list) < 0) { + if (bfd->stats.tx_fail_pkt <= 1) { + char dst[INET6_ADDRSTRLEN] = { 0 }; + + inet_ntop(AF_INET6, seg_num > 0 ? segment_list : (&bfd->key.peer), dst, + sizeof(dst)); + zlog_err("sbfd initiator send failed, dst:%s, errno:%s", dst, + safe_strerror(errno)); + } + + bfd->stats.tx_fail_pkt++; + return -1; + } + + if (bfd->stats.tx_fail_pkt > 0) { + char dst[INET6_ADDRSTRLEN] = { 0 }; + + inet_ntop(AF_INET6, seg_num > 0 ? segment_list : (&bfd->key.peer), dst, sizeof(dst)); + zlog_warn("sbfd initiator send success, dst:%s, previous tx_fail_pkt:%d", dst, + (int)bfd->stats.tx_fail_pkt); + } + bfd->stats.tx_fail_pkt = 0; + + bfd->stats.tx_ctrl_pkt++; +#endif + return 0; +} + +static int _ptm_sbfd_echo_send(struct bfd_session *bfd, const void *data, size_t datalen) +{ +#ifdef BFD_LINUX + int sd = -1; + struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd); + + int seg_num; + struct in6_addr *segment_list = NULL; + struct in6_addr peer; + struct in6_addr local; + + if (!bvrf) + return -1; + + seg_num = bfd->segnum; + if (seg_num > 0) + segment_list = bfd->seg_list; + + sd = bfd->sock; + + local = bfd->key.local; + peer = bfd->key.peer; + + /*SBFD echo pkt dst port should use BFD Echo port 3785, src port can be any according to RFC7881*/ + if (bp_raw_sbfd_red_send(sd, (uint8_t *)data, datalen, bfd->key.family, &bfd->out_sip6, + &local, &peer, BFD_DEF_ECHO_PORT, BFD_DEF_ECHO_PORT, seg_num, + segment_list) < 0) { + if (bfd->stats.tx_fail_pkt <= 1) { + char dst[INET6_ADDRSTRLEN] = { 0 }; + + inet_ntop(AF_INET6, seg_num > 0 ? segment_list : (&bfd->key.peer), dst, + sizeof(dst)); + zlog_err("sbfd echo send failed, bfd_name:%s, dst:%s, errno:%s", + bfd->bfd_name, dst, safe_strerror(errno)); + } + + bfd->stats.tx_fail_pkt++; + return -1; + } + + if (bfd->stats.tx_fail_pkt > 0) { + char dst[INET6_ADDRSTRLEN] = { 0 }; + + inet_ntop(AF_INET6, seg_num > 0 ? segment_list : (&bfd->key.peer), dst, sizeof(dst)); + zlog_warn("sbfd echo send success, bfd_name:%s, dst:%s, previous tx_fail_pkt:%d", + bfd->bfd_name, dst, (int)bfd->stats.tx_fail_pkt); + } + bfd->stats.tx_fail_pkt = 0; + + bfd->stats.tx_echo_pkt++; +#endif + return 0; +} + +void ptm_sbfd_initiator_snd(struct bfd_session *bfd, int fbit) +{ + struct bfd_pkt cp = {}; + + /* Set fields according to section 6.5.7 */ + cp.diag = bfd->local_diag; + BFD_SETVER(cp.diag, BFD_VERSION); + cp.flags = 0; + BFD_SETSTATE(cp.flags, bfd->ses_state); + + if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_CBIT)) + BFD_SETCBIT(cp.flags, BFD_CBIT); + + BFD_SETDEMANDBIT(cp.flags, BFD_SBFD_INITIATOR_DEMAND); + + /* + * Polling and Final can't be set at the same time. + * + * RFC 5880, Section 6.5. + */ + BFD_SETFBIT(cp.flags, fbit); + if (fbit == 0) + BFD_SETPBIT(cp.flags, bfd->polling); + + cp.detect_mult = bfd->detect_mult; + cp.len = BFD_PKT_LEN; + cp.discrs.my_discr = htonl(bfd->discrs.my_discr); + cp.discrs.remote_discr = htonl(bfd->discrs.remote_discr); + if (bfd->polling) { + cp.timers.desired_min_tx = htonl(bfd->timers.desired_min_tx); + } else { + /* + * We can only announce current setting on poll, this + * avoids timing mismatch with our peer and give it + * the oportunity to learn. See `bs_final_handler` for + * more information. + */ + cp.timers.desired_min_tx = htonl(bfd->cur_timers.desired_min_tx); + } + cp.timers.required_min_rx = 0; + cp.timers.required_min_echo = 0; + + if (_ptm_sbfd_init_send(bfd, &cp, BFD_PKT_LEN) != 0) + return; + + bfd->stats.tx_ctrl_pkt++; +} +void ptm_sbfd_echo_snd(struct bfd_session *bfd) +{ + struct bfd_echo_pkt bep; + + memset(&bep, 0, sizeof(bep)); + BFD_SETVER(bep.ver, BFD_ECHO_VERSION); + bep.len = BFD_ECHO_PKT_LEN; + bep.my_discr = htonl(bfd->discrs.my_discr); + + if (_ptm_sbfd_echo_send(bfd, &bep, BFD_ECHO_PKT_LEN) != 0) + return; + + if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) + SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE); +} + +static int ptm_bfd_reflector_process_init_packet(struct bfd_vrf_global *bvrf, int sd) +{ + //uint32_t my_discr = 0; + //uint32_t remote_discr = 0; + uint8_t ttl = 0; + struct sockaddr *sa; + struct sbfd_reflector *sr; + /* Receive and parse echo packet. */ + struct bfd_pkt *cp; + ssize_t rlen; + struct sockaddr_any local, peer; + ifindex_t ifindex = IFINDEX_INTERNAL; + //vrf_id_t vrfid = VRF_DEFAULT; + uint8_t msgbuf[1516]; + + rlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex, &local, &peer); + /* Short packet, better not risk reading it. */ + if (rlen < (ssize_t)sizeof(*cp)) { + zlog_debug("small bfd packet"); + return 0; + } + cp = (struct bfd_pkt *)(msgbuf); + if (!CHECK_FLAG(cp->flags, BFD_DEMANDBIT)) { + /*Control Packet from SBFDInitiator should have Demand bit set to 1 according to RFC7880*/ + return 0; + } + + sr = sbfd_discr_lookup(ntohl(cp->discrs.remote_discr)); + if (sr) { + uint32_t temp = cp->discrs.my_discr; + + cp->discrs.my_discr = cp->discrs.remote_discr; + cp->discrs.remote_discr = temp; + UNSET_FLAG(cp->flags, BFD_DEMANDBIT); + BFD_SETSTATE(cp->flags, PTM_BFD_UP); + if (CHECK_FLAG(cp->flags, BFD_PBIT)) { + UNSET_FLAG(cp->flags, BFD_PBIT); + SET_FLAG(cp->flags, BFD_FBIT); + } + + sa = (struct sockaddr *)&peer.sa_sin6; + + if (sendto(sd, msgbuf, rlen, 0, sa, sizeof(peer.sa_sin6)) <= 0) { + zlog_debug("packet-send: send failure: %s", strerror(errno)); + return -1; + } + } else { + zlog_debug("no reflector found in %u", cp->discrs.remote_discr); + } + return 0; +} + +int bp_peer_srh_socketv6(struct bfd_session *bs) +{ + int sd; //, pcount; + //struct sockaddr_in6 sin6; + //static int srcPort = BFD_SRCPORTINIT; + const char *device_to_bind = NULL; + + if (bs->key.ifname[0]) { + device_to_bind = (const char *)bs->key.ifname; + zlog_debug("device_to_bind to ifname:%s", device_to_bind); + } else if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) && bs->key.vrfname[0]) { + device_to_bind = (const char *)bs->key.vrfname; + zlog_debug("device_to_bind to vrf:%s", device_to_bind); + } else { + zlog_debug("device_to_bind to NULL"); + } + + frr_with_privs (&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET6, SOCK_RAW, IPPROTO_RAW, bs->vrf->vrf_id, device_to_bind); + } + if (sd == -1) { + zlog_err("ipv6-new: failed to create socket: %s", strerror(errno)); + return -1; + } + + /* Set TTL to 255 for all transmitted packets */ + if (bp_set_ttlv6(sd, BFD_TTL_VAL) != 0) { + close(sd); + return -1; + } + + /* Set TOS to CS6 for all transmitted packets */ + if (bp_set_tosv6(sd, BFD_TOS_VAL) != 0) { + close(sd); + return -1; + } +#ifdef IPV6_HDRINCL + int on = 1; + + /*manage the IP6 header all on own onwn*/ + if (setsockopt(sd, IPPROTO_IPV6, IPV6_HDRINCL, &on, sizeof(on))) { +#else + if (true) { +#endif + zlog_err("setsockopt IPV6_HDRINCL error: %s", strerror(errno)); + close(sd); + return -1; + } + + return sd; +} + +int bp_initv6_socket(const struct vrf *vrf) +{ + int sd; + + frr_with_privs (&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET6, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name); + } + if (sd == -1) { + if (errno != EAFNOSUPPORT) + zlog_fatal("echov6-socket: socket: %s", strerror(errno)); + else + zlog_warn("echov6-socket: V6 is not supported, continuing"); + + return -1; + } + + bp_set_ipv6opts(sd); + bp_bind_ipv6(sd, BFD_DEF_SBFD_DEST_PORT); + + return sd; +} + +#ifdef BFD_LINUX +static uint16_t checksum(uint16_t *addr, int len) +{ + int count = len; + uint16_t answer = 0; + + register uint32_t sum = 0; + + // Sum up 2-byte values until none or only one byte left. + while (count > 1) { + sum += *(addr++); + count -= 2; + } + + // Add left-over byte, if any. + if (count > 0) + sum += *(uint8_t *)addr; + + // Fold 32-bit sum into 16 bits; we lose information by doing this, + // increasing the chances of a collision. + // sum = (lower 16 bits) + (upper 16 bits shifted right 16 bits) + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + // Checksum is one's compliment of sum. + answer = ~sum; + + return answer; +} + +static uint16_t udp6_checksum(struct ip6_hdr iphdr, struct udphdr udp_hdr, uint8_t *payload, + int payloadlen) +{ + char buf[IP_MAXPACKET]; + char *ptr; + int chksumlen = 0; + int i; + + ptr = &buf[0]; // ptr points to beginning of buffer buf + + // Copy source IP address into buf (128 bits) + memcpy(ptr, &iphdr.ip6_src.s6_addr, sizeof(iphdr.ip6_src.s6_addr)); + ptr += sizeof(iphdr.ip6_src.s6_addr); + chksumlen += sizeof(iphdr.ip6_src.s6_addr); + + // Copy destination IP address into buf (128 bits) + memcpy(ptr, &iphdr.ip6_dst.s6_addr, sizeof(iphdr.ip6_dst.s6_addr)); + ptr += sizeof(iphdr.ip6_dst.s6_addr); + chksumlen += sizeof(iphdr.ip6_dst.s6_addr); + + // Copy UDP length into buf (32 bits) + memcpy(ptr, &udp_hdr.len, sizeof(udp_hdr.len)); + ptr += sizeof(udp_hdr.len); + chksumlen += sizeof(udp_hdr.len); + + // Copy zero field to buf (24 bits) + *ptr = 0; + ptr++; + *ptr = 0; + ptr++; + *ptr = 0; + ptr++; + chksumlen += 3; + + // Copy next header field to buf (8 bits) + memcpy(ptr, &iphdr.ip6_nxt, sizeof(iphdr.ip6_nxt)); + ptr += sizeof(iphdr.ip6_nxt); + chksumlen += sizeof(iphdr.ip6_nxt); + + // Copy UDP source port to buf (16 bits) + memcpy(ptr, &udp_hdr.source, sizeof(udp_hdr.source)); + ptr += sizeof(udp_hdr.source); + chksumlen += sizeof(udp_hdr.source); + + // Copy UDP destination port to buf (16 bits) + memcpy(ptr, &udp_hdr.dest, sizeof(udp_hdr.dest)); + ptr += sizeof(udp_hdr.dest); + chksumlen += sizeof(udp_hdr.dest); + + // Copy UDP length again to buf (16 bits) + memcpy(ptr, &udp_hdr.len, sizeof(udp_hdr.len)); + ptr += sizeof(udp_hdr.len); + chksumlen += sizeof(udp_hdr.len); + + // Copy UDP checksum to buf (16 bits) + // Zero, since we don't know it yet + *ptr = 0; + ptr++; + *ptr = 0; + ptr++; + chksumlen += 2; + + // Copy payload to buf + memcpy(ptr, payload, payloadlen * sizeof(uint8_t)); + ptr += payloadlen; + chksumlen += payloadlen; + + // Pad to the next 16-bit boundary + for (i = 0; i < payloadlen % 2; i++, ptr++) { + *ptr = 0; + ptr++; + chksumlen++; + } + + return checksum((uint16_t *)buf, chksumlen); +} + +// Build IPv4 UDP pseudo-header and call checksum function. +static uint16_t udp4_checksum(struct ip iphdr, struct udphdr udp_hdr, uint8_t *payload, + int payloadlen) +{ + char buf[IP_MAXPACKET]; + char *ptr; + int chksumlen = 0; + int i; + + ptr = &buf[0]; // ptr points to beginning of buffer buf + + // Copy source IP address into buf (32 bits) + memcpy(ptr, &iphdr.ip_src.s_addr, sizeof(iphdr.ip_src.s_addr)); + ptr += sizeof(iphdr.ip_src.s_addr); + chksumlen += sizeof(iphdr.ip_src.s_addr); + + // Copy destination IP address into buf (32 bits) + memcpy(ptr, &iphdr.ip_dst.s_addr, sizeof(iphdr.ip_dst.s_addr)); + ptr += sizeof(iphdr.ip_dst.s_addr); + chksumlen += sizeof(iphdr.ip_dst.s_addr); + + // Copy zero field to buf (8 bits) + *ptr = 0; + ptr++; + chksumlen += 1; + + // Copy transport layer protocol to buf (8 bits) + memcpy(ptr, &iphdr.ip_p, sizeof(iphdr.ip_p)); + ptr += sizeof(iphdr.ip_p); + chksumlen += sizeof(iphdr.ip_p); + + // Copy UDP length to buf (16 bits) + memcpy(ptr, &udp_hdr.len, sizeof(udp_hdr.len)); + ptr += sizeof(udp_hdr.len); + chksumlen += sizeof(udp_hdr.len); + + // Copy UDP source port to buf (16 bits) + memcpy(ptr, &udp_hdr.source, sizeof(udp_hdr.source)); + ptr += sizeof(udp_hdr.source); + chksumlen += sizeof(udp_hdr.source); + + // Copy UDP destination port to buf (16 bits) + memcpy(ptr, &udp_hdr.dest, sizeof(udp_hdr.dest)); + ptr += sizeof(udp_hdr.dest); + chksumlen += sizeof(udp_hdr.dest); + + // Copy UDP length again to buf (16 bits) + memcpy(ptr, &udp_hdr.len, sizeof(udp_hdr.len)); + ptr += sizeof(udp_hdr.len); + chksumlen += sizeof(udp_hdr.len); + + // Copy UDP checksum to buf (16 bits) + // Zero, since we don't know it yet + *ptr = 0; + ptr++; + *ptr = 0; + ptr++; + chksumlen += 2; + + // Copy payload to buf + memcpy(ptr, payload, payloadlen); + ptr += payloadlen; + chksumlen += payloadlen; + + // Pad to the next 16-bit boundary + for (i = 0; i < payloadlen % 2; i++, ptr++) { + *ptr = 0; + ptr++; + chksumlen++; + } + + return checksum((uint16_t *)buf, chksumlen); +} +#endif + +int bp_sbfd_socket(const struct vrf *vrf) +{ + int s; + + frr_with_privs (&bglobal.bfdd_privs) { + s = vrf_socket(AF_INET6, SOCK_RAW, IPPROTO_RAW, vrf->vrf_id, vrf->name); + } + if (s == -1) { + if (errno != EAFNOSUPPORT) + zlog_fatal("sbfdv6-socket: socket: %s", strerror(errno)); + else + zlog_warn("sbfdv6-socket: V6 is not supported, continuing"); + + return -1; + } + + bp_set_prio(s, SOCK_OPT_PRIO_HIGH); + + return s; +} + +#ifdef BFD_LINUX +static void bp_sbfd_encap_srh_ip6h_red(struct ip6_hdr *srh_ip6h, struct in6_addr *sip, + struct in6_addr *dip, uint8_t seg_num, size_t datalen, + uint16_t family) +{ + /* SRH IPv6 Header */ + srh_ip6h->ip6_flow = (BFD_TOS_VAL << 20); + srh_ip6h->ip6_vfc = 6 << 4; + + if (seg_num == 1) { + if (family == AF_INET6) { + srh_ip6h->ip6_plen = + htons(sizeof(struct ip6_hdr) + sizeof(struct udphdr) + datalen); + srh_ip6h->ip6_nxt = IPPROTO_IPV6; + } else { + srh_ip6h->ip6_plen = + htons(sizeof(struct ip) + sizeof(struct udphdr) + datalen); + srh_ip6h->ip6_nxt = IPPROTO_IPIP; + } + + } else { + srh_ip6h->ip6_plen = htons(sizeof(struct ip6_hdr) + sizeof(struct udphdr) + + sizeof(struct ipv6_sr_hdr) + + sizeof(struct in6_addr) * (seg_num - 1) + datalen); + srh_ip6h->ip6_nxt = IPPROTO_ROUTING; + } + srh_ip6h->ip6_hlim = BFD_TTL_VAL; + + memcpy(&(srh_ip6h->ip6_src), sip, sizeof(struct in6_addr)); + memcpy(&(srh_ip6h->ip6_dst), dip, sizeof(struct in6_addr)); +} + +static void bp_sbfd_encap_srh_rth_red(struct ipv6_sr_hdr *srv6h, struct in6_addr *segment_list, + uint8_t seg_num) +{ + //caller should make sure: seg_num > 1 + srv6h->nexthdr = IPPROTO_IPV6; + srv6h->hdrlen = + GET_RTH_HDR_LEN(RTH_BASE_HEADER_LEN + sizeof(struct in6_addr) * (seg_num - 1)); + srv6h->type = IPV6_SRCRT_TYPE_4; + srv6h->segments_left = seg_num - 1; //if encap reduce mode , seg_num-1 + srv6h->first_segment = seg_num - 2; //if encap reduce mode , seg_num-2 + srv6h->flags = 0; + srv6h->tag = 0; + + for (int i = 0; i < seg_num - 1; i++) + memcpy(&srv6h->segments[i], &segment_list[seg_num - 1 - i], sizeof(struct in6_addr)); +} + +static void bp_sbfd_encap_inner_ip6h(struct ip6_hdr *ip6h, struct in6_addr *sip, + struct in6_addr *dip, size_t datalen) +{ + /* IPv6 Header */ + ip6h->ip6_flow = (BFD_TOS_VAL << 20); + ip6h->ip6_vfc = 6 << 4; + ip6h->ip6_plen = htons(sizeof(struct udphdr) + datalen); + ip6h->ip6_nxt = IPPROTO_UDP; + ip6h->ip6_hlim = BFD_TTL_VAL; + + memcpy(&(ip6h->ip6_src), sip, sizeof(struct in6_addr)); + memcpy(&(ip6h->ip6_dst), dip, sizeof(struct in6_addr)); +} + +static void bp_sbfd_encap_inner_iph(struct ip *iph, struct in6_addr *sip, struct in6_addr *dip, + size_t datalen) +{ + /* IPv4 Header */ + iph->ip_v = 4; + iph->ip_hl = 5; + iph->ip_tos = BFD_TOS_VAL; + iph->ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + datalen); + iph->ip_id = (uint16_t)frr_weak_random(); + iph->ip_ttl = BFD_TTL_VAL; + iph->ip_p = IPPROTO_UDP; + iph->ip_sum = 0; + memcpy(&iph->ip_src, sip, sizeof(iph->ip_src)); + memcpy(&iph->ip_dst, dip, sizeof(iph->ip_dst)); +} + +static void bp_sbfd_encap_udp6(struct udphdr *udph, struct ip6_hdr *ip6h, uint16_t src_port, + uint16_t dst_port, uint8_t *payload, int payloadlen) +{ + udph->source = htons(src_port); + udph->dest = htons(dst_port); + udph->len = htons(sizeof(struct udphdr) + payloadlen); + udph->check = udp6_checksum(*ip6h, *udph, payload, payloadlen); +} + +static void bp_sbfd_encap_udp4(struct udphdr *udph, struct ip *iph, uint16_t src_port, + uint16_t dst_port, uint8_t *payload, int payloadlen) +{ + udph->source = htons(src_port); + udph->dest = htons(dst_port); + udph->len = htons(sizeof(struct udphdr) + payloadlen); + udph->check = udp4_checksum(*iph, *udph, payload, payloadlen); +} + +/** + * @brief encap srv6 to send raw socker red mode, just support ecore 2.5 case + * + * @param sd sokcet + * @param data actual data, e.g. bfd packet or bfd echo packet + * @param datalen actual data length + * @param sip source ip address of outer ipv6 header and inner ipv6 header + * @param dip destination ip address of inner ipv6 header + * @param src_port source port of udp + * @param dst_port destination port of udp + * @param seg_num segment number of srh header + * @param segment_list segment list of srh header and the last one segment is destination ip address of outer ipv6 header + * @param ifname out interface name + * @param vrfname vrf name + * @param nhp specified nexthop + * @return int + */ +static int bp_raw_sbfd_red_send(int sd, uint8_t *data, size_t datalen, uint16_t family, + struct in6_addr *out_sip, struct in6_addr *sip, + struct in6_addr *dip, uint16_t src_port, uint16_t dst_port, + uint8_t seg_num, struct in6_addr *segment_list) +{ + static uint8_t sendbuf[BUF_SIZ]; + struct msghdr msg = { 0 }; + struct iovec iov; + int flags = 0; + int ret = 0; + + struct ip6_hdr *srh_ip6h; + struct ipv6_sr_hdr *psrv6h; // srh Routing header + struct ip6_hdr *ip6h; + struct ip *iph; + struct udphdr *udp; + uint8_t *payload; + + struct ipaddr out_sip_addr = { 0 }; + struct sockaddr_in6 dst_sin6 = { 0 }; + char buf_addr[INET6_ADDRSTRLEN] = { 0 }; + + memset(sendbuf, 0, sizeof(sendbuf)); + int total_len = 0; + + /* SRH IPv6 Header */ + if (seg_num > 0) { + memcpy(&out_sip_addr.ipaddr_v6, out_sip, sizeof(struct in6_addr)); + + srh_ip6h = (struct ip6_hdr *)(sendbuf + total_len); + bp_sbfd_encap_srh_ip6h_red(srh_ip6h, &out_sip_addr.ipaddr_v6, &segment_list[0], + seg_num, datalen, family); + total_len += sizeof(struct ip6_hdr); + + memcpy(&dst_sin6.sin6_addr, &segment_list[0], sizeof(struct in6_addr)); + } + + //case with srh header + if (seg_num > 1) { + psrv6h = (struct ipv6_sr_hdr *)(sendbuf + total_len); + bp_sbfd_encap_srh_rth_red(psrv6h, segment_list, seg_num); + total_len += sizeof(struct ipv6_sr_hdr) + sizeof(struct in6_addr) * (seg_num - 1); + } + + if (family == AF_INET6) { + if (seg_num == 0) + memcpy(&dst_sin6.sin6_addr, dip, sizeof(struct in6_addr)); + + /* Inner IPv6 Header */ + ip6h = (struct ip6_hdr *)(sendbuf + total_len); + bp_sbfd_encap_inner_ip6h(ip6h, sip, dip, datalen); + total_len += sizeof(struct ip6_hdr); + + /* UDP Header */ + udp = (struct udphdr *)(sendbuf + total_len); + bp_sbfd_encap_udp6(udp, ip6h, src_port, dst_port, data, datalen); + total_len += sizeof(struct udphdr); + } else { + if (seg_num == 0) { + //should never come to here, just print a error hint + zlog_err("%s error, empty sidlist for ipv4 bfd", __func__); + } + + /* Inner IPv4 Header */ + iph = (struct ip *)(sendbuf + total_len); + bp_sbfd_encap_inner_iph(iph, sip, dip, datalen); + total_len += sizeof(struct ip); + + /* UDP Header */ + udp = (struct udphdr *)(sendbuf + total_len); + bp_sbfd_encap_udp4(udp, iph, src_port, dst_port, data, datalen); + total_len += sizeof(struct udphdr); + + iph->ip_sum = in_cksum((const void *)iph, sizeof(struct ip)); + } + + /* BFD payload*/ + payload = (uint8_t *)(sendbuf + total_len); + memcpy(payload, data, datalen); + total_len += datalen; + + dst_sin6.sin6_family = AF_INET6; + dst_sin6.sin6_port = 0; //we don't use sin6_port in raw, but should set to 0!! + + /* message data. */ + iov.iov_base = (uint8_t *)sendbuf; + iov.iov_len = total_len; + + msg.msg_name = &dst_sin6; + msg.msg_namelen = sizeof(struct sockaddr_in6); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + /* sendmsg */ + ret = sendmsg(sd, &msg, flags); + if (ret < 0) { + inet_ntop(AF_INET6, &dst_sin6.sin6_addr, buf_addr, INET6_ADDRSTRLEN); + zlog_debug("sbfd send to:%s failed , ret:%d, errno:%s", buf_addr, ret, + safe_strerror(errno)); + + return ret; + } + + return 0; +} + +#endif diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c index 6527ec5f41..a1710ec127 100644 --- a/bfdd/bfdd_cli.c +++ b/bfdd/bfdd_cli.c @@ -11,8 +11,12 @@ #include "lib/command.h" #include "lib/log.h" #include "lib/northbound_cli.h" +#include "lib/termtable.h" +#include "lib/ipaddr.h" +#ifndef VTYSH_EXTRACT_PL #include "bfdd/bfdd_cli_clippy.c" +#endif /* VTYSH_EXTRACT_PL */ #include "bfd.h" #include "bfdd_nb.h" @@ -31,6 +35,10 @@ #define LOCAL_INTF_STR "Configure local interface name to use\n" #define VRF_STR "Configure VRF\n" #define VRF_NAME_STR "Configure VRF name\n" +#define SESSION_NAME_STR "Specify bfd session name\n" +#define SET_SESSION_NAME_STR "bfd session name\n" +#define SESSION_MODE_STR "Specify bfd session mode\n" +#define APPLY_SESSION_MODE_STR "Enable bfd mode\n" /* * Prototypes. @@ -41,6 +49,12 @@ bfd_cli_is_single_hop(struct vty *vty) return strstr(VTY_CURR_XPATH, "/single-hop") != NULL; } +static bool bfd_cli_is_sbfd_echo(struct vty *vty) +{ + return strstr(VTY_CURR_XPATH, "/sbfd-echo") != NULL; +} + + static bool bfd_cli_is_profile(struct vty *vty) { @@ -215,45 +229,486 @@ DEFPY_YANG( return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG_NOSH( + sbfd_echo_peer_enter, sbfd_echo_peer_enter_cmd, + "peer <A.B.C.D|X:X::X:X> bfd-mode sbfd-echo bfd-name BFDNAME$bfdname \ + [multihop$multihop] local-address <A.B.C.D|X:X::X:X> [vrf NAME] \ + srv6-source-ipv6 X:X::X:X srv6-encap-data X:X::X:X...", + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + SESSION_MODE_STR + "Enable sbfd-echo mode\n" + SESSION_NAME_STR + SET_SESSION_NAME_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + VRF_STR + VRF_NAME_STR + "Configure source ipv6 address for srv6 encap\n" + LOCAL_IPV6_STR + "Configure sidlist data for srv6 encap\n" + "X:X::X:X IPv6 sid address\n") +{ + int ret, slen, data_idx = 11; + char xpath[XPATH_MAXLEN], xpath_sl[XPATH_MAXLEN + 32], xpath_mh[XPATH_MAXLEN + 32]; + + if (!bfdname) { + vty_out(vty, "%% ERROR: bfd name is required\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (strcmp(peer_str, local_address_str)) { + vty_out(vty, + "%% ERROR: peer and local-address must be the same in sbfd-echo mode\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo[source-addr='%s'][bfd-name='%s']", + local_address_str, bfdname); + + if (vrf) { + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + data_idx += 2; + } else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + if (multihop) { + snprintf(xpath_mh, sizeof(xpath_mh), "%s/multi-hop", xpath); + nb_cli_enqueue_change(vty, xpath_mh, NB_OP_MODIFY, "true"); + data_idx += 1; + } + + for (int i = data_idx; i < argc; i++) { + snprintf(xpath_sl, sizeof(xpath_sl), "%s/srv6-encap-data", xpath); + nb_cli_enqueue_change(vty, xpath_sl, NB_OP_CREATE, argv[i]->arg); + } + + snprintf(xpath_sl, sizeof(xpath_sl), "%s/srv6-source-ipv6", xpath); + nb_cli_enqueue_change(vty, xpath_sl, NB_OP_MODIFY, srv6_source_ipv6_str); + + snprintf(xpath_sl, sizeof(xpath_sl), "%s/dest-addr", xpath); + nb_cli_enqueue_change(vty, xpath_sl, NB_OP_MODIFY, peer_str); + + /* Apply settings immediately. */ + ret = nb_cli_apply_changes(vty, NULL); + if (ret == CMD_SUCCESS) + VTY_PUSH_XPATH(BFD_PEER_NODE, xpath); + + return ret; +} + +DEFPY_YANG( + sbfd_echo_no_peer, sbfd_echo_no_peer_cmd, + "no peer <A.B.C.D|X:X::X:X> bfd-mode sbfd-echo bfd-name BFDNAME$bfdname \ + [multihop$multihop] local-address <A.B.C.D|X:X::X:X> [vrf NAME] \ + srv6-source-ipv6 X:X::X:X srv6-encap-data X:X::X:X...", + NO_STR + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + SESSION_MODE_STR + "Enable sbfd-echo mode\n" + SESSION_NAME_STR + SET_SESSION_NAME_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + VRF_STR + VRF_NAME_STR + "Configure source ipv6 address for srv6 encap\n" + LOCAL_IPV6_STR + "Configure sidlist data for srv6 encap\n" + "X:X::X:X IPv6 sid address\n") +{ + int slen; + char xpath[XPATH_MAXLEN]; + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo[source-addr='%s'][bfd-name='%s']", + local_address_str, bfdname); + + if (vrf) + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + /* Apply settings immediatly. */ + return nb_cli_apply_changes(vty, NULL); +} + + +DEFPY_YANG_NOSH( + sbfd_init_peer_enter, sbfd_init_peer_enter_cmd, + "peer <A.B.C.D|X:X::X:X> bfd-mode sbfd-init bfd-name BFDNAME$bfdname \ + [multihop$multihop] local-address <A.B.C.D|X:X::X:X> [vrf NAME] \ + remote-discr (1-4294967295)$discr srv6-source-ipv6 X:X::X:X srv6-encap-data X:X::X:X...", + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + SESSION_MODE_STR + "Enable sbfd-init mode\n" + SESSION_NAME_STR + SET_SESSION_NAME_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + VRF_STR + VRF_NAME_STR + "Configure bfd session remote discriminator\n" + "Configure remote discriminator\n" + "Configure source ipv6 address for srv6 encap\n" + LOCAL_IPV6_STR + "Configure sidlist data for srv6 encap\n" + "X:X::X:X IPv6 sid address\n") +{ + int ret, slen, data_idx = 13; + char xpath[XPATH_MAXLEN], xpath_sl[XPATH_MAXLEN + 32], xpath_rd[XPATH_MAXLEN + 32], + xpath_mh[XPATH_MAXLEN + 32]; + struct ipaddr peer_addr = { 0 }; + struct ipaddr local_addr = { 0 }; + + if (!bfdname) { + vty_out(vty, "%% ERROR: bfd name is required\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + str2ipaddr(peer_str, &peer_addr); + if (peer_addr.ipa_type == AF_UNSPEC) { + vty_out(vty, "%% ERROR: peer is invalid address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + str2ipaddr(local_address_str, &local_addr); + if (local_addr.ipa_type == AF_UNSPEC) { + vty_out(vty, "%% ERROR: local_address is invalid address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (peer_addr.ipa_type != local_addr.ipa_type) { + vty_out(vty, "%% ERROR: peer and local_address are not the same ip version\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/sbfd-init[source-addr='%s'][dest-addr='%s'][bfd-name='%s']", + local_address_str, peer_str, bfdname); + + if (vrf) { + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + data_idx += 2; + } else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + if (multihop) { + snprintf(xpath_mh, sizeof(xpath_mh), "%s/multi-hop", xpath); + nb_cli_enqueue_change(vty, xpath_mh, NB_OP_MODIFY, "true"); + data_idx += 1; + } + + if (srv6_source_ipv6_str) { + for (int i = data_idx; i < argc; i++) { + snprintf(xpath_sl, sizeof(xpath_sl), "%s/srv6-encap-data", xpath); + nb_cli_enqueue_change(vty, xpath_sl, NB_OP_CREATE, argv[i]->arg); + } + + snprintf(xpath_sl, sizeof(xpath_sl), "%s/srv6-source-ipv6", xpath); + nb_cli_enqueue_change(vty, xpath_sl, NB_OP_MODIFY, srv6_source_ipv6_str); + } + + snprintf(xpath_rd, sizeof(xpath_rd), "%s/remote-discr", xpath); + nb_cli_enqueue_change(vty, xpath_rd, NB_OP_MODIFY, discr_str); + + /* Apply settings immediately. */ + ret = nb_cli_apply_changes(vty, NULL); + if (ret == CMD_SUCCESS) + VTY_PUSH_XPATH(BFD_PEER_NODE, xpath); + + return ret; +} + +DEFPY_YANG( + sbfd_init_no_peer, sbfd_init_no_peer_cmd, + "no peer <A.B.C.D|X:X::X:X> bfd-mode sbfd-init bfd-name BFDNAME$bfdname \ + [multihop$multihop] local-address <A.B.C.D|X:X::X:X> [vrf NAME] \ + remote-discr (0-4294967295)$discr srv6-source-ipv6 X:X::X:X srv6-encap-data X:X::X:X...", + NO_STR + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + SESSION_MODE_STR + "Enable sbfd-init mode\n" + SESSION_NAME_STR + SET_SESSION_NAME_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + VRF_STR + VRF_NAME_STR + "Configure bfd session remote discriminator\n" + "Configure remote discriminator\n" + "Configure source ipv6 address for srv6 encap\n" + LOCAL_IPV6_STR + "Configure sidlist data for srv6 encap\n" + "X:X::X:X IPv6 sid address\n") +{ + int slen; + char xpath[XPATH_MAXLEN]; + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/sbfd-init[source-addr='%s'][dest-addr='%s'][bfd-name='%s']", + local_address_str, peer_str, bfdname); + + if (vrf) + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + /* Apply settings immediatly. */ + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG_NOSH( + sbfd_init_peer_raw_enter, sbfd_init_peer_raw_enter_cmd, + "peer <A.B.C.D|X:X::X:X> bfd-mode sbfd-init bfd-name BFDNAME$bfdname \ + [multihop$multihop] local-address <A.B.C.D|X:X::X:X> [vrf NAME] \ + remote-discr (1-4294967295)$discr", + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + SESSION_MODE_STR + "Enable sbfd-init mode\n" + SESSION_NAME_STR + SET_SESSION_NAME_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + VRF_STR + VRF_NAME_STR + "Configure bfd session remote discriminator\n" + "Configure remote discriminator\n") +{ + int ret, slen; + char xpath[XPATH_MAXLEN], xpath_rd[XPATH_MAXLEN + 32], xpath_mh[XPATH_MAXLEN + 32]; + struct ipaddr peer_addr = { 0 }; + struct ipaddr local_addr = { 0 }; + + if (!bfdname) { + vty_out(vty, "%% ERROR: bfd name is required\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + str2ipaddr(peer_str, &peer_addr); + if (peer_addr.ipa_type == AF_UNSPEC) { + vty_out(vty, "%% ERROR: peer is invalid address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + str2ipaddr(local_address_str, &local_addr); + if (local_addr.ipa_type == AF_UNSPEC) { + vty_out(vty, "%% ERROR: local_address is invalid address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (peer_addr.ipa_type != local_addr.ipa_type) { + vty_out(vty, "%% ERROR: peer and local_address are not the same ip version\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/sbfd-init[source-addr='%s'][dest-addr='%s'][bfd-name='%s']", + local_address_str, peer_str, bfdname); + + if (vrf) + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + if (multihop) { + snprintf(xpath_mh, sizeof(xpath_mh), "%s/multi-hop", xpath); + nb_cli_enqueue_change(vty, xpath_mh, NB_OP_MODIFY, "true"); + } + + snprintf(xpath_rd, sizeof(xpath_rd), "%s/remote-discr", xpath); + nb_cli_enqueue_change(vty, xpath_rd, NB_OP_MODIFY, discr_str); + + /* Apply settings immediately. */ + ret = nb_cli_apply_changes(vty, NULL); + if (ret == CMD_SUCCESS) + VTY_PUSH_XPATH(BFD_PEER_NODE, xpath); + + return ret; +} + +DEFPY_YANG( + sbfd_init_no_peer_raw, sbfd_init_no_peer_raw_cmd, + "no peer <A.B.C.D|X:X::X:X> bfd-mode sbfd-init bfd-name BFDNAME$bfdname \ + [multihop$multihop] local-address <A.B.C.D|X:X::X:X> [vrf NAME] \ + remote-discr (0-4294967295)$discr", + NO_STR + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + SESSION_MODE_STR + "Enable sbfd-init mode\n" + SESSION_NAME_STR + SET_SESSION_NAME_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + VRF_STR + VRF_NAME_STR + "Configure bfd session remote discriminator\n" + "Configure remote discriminator\n") +{ + int slen; + char xpath[XPATH_MAXLEN]; + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/sbfd-init[source-addr='%s'][dest-addr='%s'][bfd-name='%s']", + local_address_str, peer_str, bfdname); + + if (vrf) + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + /* Apply settings immediatly. */ + return nb_cli_apply_changes(vty, NULL); +} + +static const char *_bfd_cli_bfd_mode_type_to_string(enum bfd_mode_type mode) +{ + switch (mode) { + case BFD_MODE_TYPE_BFD: + return "bfd"; + case BFD_MODE_TYPE_SBFD_ECHO: + return "sbfd-echo"; + case BFD_MODE_TYPE_SBFD_INIT: + return "sbfd-init"; + default: + return "Unknown"; + } +} + +struct sidlist_show_iter { + char buf[INET6_ADDRSTRLEN * SRV6_MAX_SEGS]; +}; + +static int sidlist_show_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct sidlist_show_iter *iter = arg; + const char *addr; + + addr = yang_dnode_get_string(dnode, NULL); + + strlcat(iter->buf, addr, INET6_ADDRSTRLEN * SRV6_MAX_SEGS); + strlcat(iter->buf, " ", INET6_ADDRSTRLEN * SRV6_MAX_SEGS); + + return YANG_ITER_CONTINUE; +} + static void _bfd_cli_show_peer(struct vty *vty, const struct lyd_node *dnode, - bool show_defaults __attribute__((__unused__)), - bool mhop) + bool show_defaults __attribute__((__unused__)), bool mhop, + uint32_t bfd_mode) { const char *vrf = yang_dnode_get_string(dnode, "vrf"); + struct sidlist_show_iter iter = { 0 }; - vty_out(vty, " peer %s", - yang_dnode_get_string(dnode, "dest-addr")); + vty_out(vty, " peer %s", yang_dnode_get_string(dnode, "./dest-addr")); + if (bfd_mode == BFD_MODE_TYPE_BFD) { + if (mhop) + vty_out(vty, " multihop"); - if (mhop) - vty_out(vty, " multihop"); + if (yang_dnode_exists(dnode, "./source-addr")) + vty_out(vty, " local-address %s", + yang_dnode_get_string(dnode, "./source-addr")); - if (yang_dnode_exists(dnode, "source-addr")) - vty_out(vty, " local-address %s", - yang_dnode_get_string(dnode, "source-addr")); + if (strcmp(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", vrf); - if (strcmp(vrf, VRF_DEFAULT_NAME)) - vty_out(vty, " vrf %s", vrf); + if (!mhop) { + const char *ifname = yang_dnode_get_string(dnode, "./interface"); - if (!mhop) { - const char *ifname = - yang_dnode_get_string(dnode, "interface"); - if (strcmp(ifname, "*")) - vty_out(vty, " interface %s", ifname); - } + if (strcmp(ifname, "*")) + vty_out(vty, " interface %s", ifname); + } + vty_out(vty, "\n"); + } else if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + vty_out(vty, " bfd-mode %s", _bfd_cli_bfd_mode_type_to_string(bfd_mode)); + + if (yang_dnode_exists(dnode, "bfd-name")) + vty_out(vty, " bfd-name %s", yang_dnode_get_string(dnode, "bfd-name")); - vty_out(vty, "\n"); + if (mhop) + vty_out(vty, " multihop"); + + if (yang_dnode_exists(dnode, "source-addr")) + vty_out(vty, " local-address %s", + yang_dnode_get_string(dnode, "source-addr")); + + if (strcmp(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", vrf); + + if (bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + if (yang_dnode_exists(dnode, "remote-discr")) + vty_out(vty, " remote-discr %u", + yang_dnode_get_uint32(dnode, "remote-discr")); + } + + if (yang_dnode_exists(dnode, "srv6-source-ipv6")) + vty_out(vty, " srv6-source-ipv6 %s", + yang_dnode_get_string(dnode, "srv6-source-ipv6")); + + if (yang_dnode_exists(dnode, "srv6-encap-data")) { + yang_dnode_iterate(sidlist_show_iter_cb, &iter, dnode, "./srv6-encap-data"); + vty_out(vty, " srv6-encap-data %s", iter.buf); + } + + vty_out(vty, "\n"); + } } void bfd_cli_show_single_hop_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - _bfd_cli_show_peer(vty, dnode, show_defaults, false); + _bfd_cli_show_peer(vty, dnode, show_defaults, false, BFD_MODE_TYPE_BFD); } void bfd_cli_show_multi_hop_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - _bfd_cli_show_peer(vty, dnode, show_defaults, true); + _bfd_cli_show_peer(vty, dnode, show_defaults, true, BFD_MODE_TYPE_BFD); +} + +void bfd_cli_show_sbfd_echo_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + _bfd_cli_show_peer(vty, dnode, show_defaults, false, BFD_MODE_TYPE_SBFD_ECHO); +} + +void bfd_cli_show_sbfd_init_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + _bfd_cli_show_peer(vty, dnode, show_defaults, true, BFD_MODE_TYPE_SBFD_INIT); } void bfd_cli_show_peer_end(struct vty *vty, const struct lyd_node *dnode @@ -446,8 +901,9 @@ DEFPY_YANG( { char value[32]; - if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { - vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); + if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty) && !bfd_cli_is_sbfd_echo(vty)) { + vty_out(vty, + "%% Echo mode is only available for single hop or sbfd echo sessions.\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -470,8 +926,9 @@ DEFPY_YANG( { char value[32]; - if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { - vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); + if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty) && !bfd_cli_is_sbfd_echo(vty)) { + vty_out(vty, + "%% Echo mode is only available for single hop or sbfd echo sessions.\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -501,11 +958,13 @@ DEFPY_YANG( { char value[32]; - if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { - vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); + if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty) && !bfd_cli_is_sbfd_echo(vty)) { + vty_out(vty, + "%% Echo mode is only available for single hop or sbfd echo sessions.\n"); return CMD_WARNING_CONFIG_FAILED; } + if (disabled) snprintf(value, sizeof(value), "0"); else @@ -657,6 +1116,160 @@ DEFPY_YANG(bfd_peer_profile, bfd_peer_profile_cmd, return nb_cli_apply_changes(vty, NULL); } +DEFPY( + sbfd_reflector, sbfd_reflector_cmd, + "sbfd reflector source-address X:X::X:X$srcip discriminator WORD...", + "seamless BFD\n" + "sbfd reflector\n" + "binding source ip address\n" + IPV6_STR + "discriminator\n" + "discriminator value or range (e.g. 100 or 100 200 300 or 100-300)\n") +{ + int idx_discr = 5; + int i; + uint32_t j; + uint32_t discr = 0; + uint32_t discr_from = 0; + uint32_t discr_to = 0; + + for (i = idx_discr; i < argc; i++) { + /* check validity*/ + char *pstr = argv[i]->arg; + + /*single discr*/ + if (strspn(pstr, "0123456789") == strlen(pstr)) { + discr = atol(pstr); + sbfd_reflector_new(discr, &srcip); + } + /*discr segment*/ + else if (strspn(pstr, "0123456789-") == strlen(pstr)) { + char *token = strtok(argv[i]->arg, "-"); + + if (token) + discr_from = atol(token); + + token = strtok(NULL, "-"); + if (token) + discr_to = atol(token); + + if (discr_from >= discr_to) { + vty_out(vty, "input discriminator range %u-%u is illegal\n", + discr_from, discr_to); + } + + for (j = discr_from; j <= discr_to; j++) + sbfd_reflector_new(j, &srcip); + } + /*illegal input*/ + else + vty_out(vty, "input discriminator %s is illegal\n", (char *)argv[i]); + } + + return CMD_SUCCESS; +} + +DEFPY( + no_sbfd_reflector_all, no_sbfd_reflector_all_cmd, + "no sbfd reflector [all]", + NO_STR + "seamless BFD\n" + "sbfd reflector\n" + "all\n") +{ + sbfd_reflector_flush(); + + if (sbfd_discr_get_count()) { + vty_out(vty, "delete all reflector discriminator failed.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFPY( + no_sbfd_reflector, no_sbfd_reflector_cmd, + "no sbfd reflector (0-4294967295)$start_discr [(0-4294967295)$end_discr]", + NO_STR + "seamless BFD\n" + "sbfd reflector\n" + "start discriminator\n" + "end discriminator\n") +{ + struct sbfd_reflector *sr; + int32_t i; + + if (end_discr == 0) { + if (start_discr == 0) { + vty_out(vty, "input reflector discriminator is illegal.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + sr = sbfd_discr_lookup(start_discr); + if (!sr) { + vty_out(vty, "input reflector discriminator does not exist.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + // notify bfdsyncd + //bfd_fpm_sbfd_reflector_sendmsg(sr, false); + sbfd_reflector_free(start_discr); + + } else { + if (end_discr <= start_discr) { + vty_out(vty, "input reflector discriminator is illegal.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + for (i = start_discr; i <= end_discr; i++) { + sr = sbfd_discr_lookup(i); + if (sr) { + // notify bfdsyncd + //bfd_fpm_sbfd_reflector_sendmsg(sr, false); + sbfd_reflector_free(i); + } + } + } + + return CMD_SUCCESS; +} + +static void _sbfd_reflector_show(struct hash_bucket *hb, void *arg) +{ + struct sbfd_reflector *sr = hb->data; + struct ttable *tt; + char buf[INET6_ADDRSTRLEN]; + + tt = (struct ttable *)arg; + + ttable_add_row(tt, "%u|%s|%s|%s", sr->discr, + inet_ntop(AF_INET6, &sr->local, buf, sizeof(buf)), "Active", "Software"); +} + +DEFPY( + sbfd_reflector_show_info, sbfd_reflector_show_info_cmd, + "show sbfd reflector", + "show\n" + "seamless BFD\n" + "sbfd reflector\n") +{ + struct ttable *tt; + char *out; + + vty_out(vty, "sbfd reflector discriminator :\n"); + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "SBFD-Discr|SourceIP|State|CreateType"); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + sbfd_discr_iterate(_sbfd_reflector_show, tt); + + out = ttable_dump(tt, "\n"); + vty_out(vty, "%s", out); + XFREE(MTYPE_TMP, out); + ttable_del(tt); + + return CMD_SUCCESS; +} void bfd_cli_peer_profile_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { @@ -694,6 +1307,18 @@ bfdd_cli_init(void) install_element(BFD_NODE, &bfd_peer_enter_cmd); install_element(BFD_NODE, &bfd_no_peer_cmd); + install_element(BFD_NODE, &sbfd_echo_peer_enter_cmd); + install_element(BFD_NODE, &sbfd_echo_no_peer_cmd); + + install_element(BFD_NODE, &sbfd_init_peer_enter_cmd); + install_element(BFD_NODE, &sbfd_init_no_peer_cmd); + install_element(BFD_NODE, &sbfd_init_peer_raw_enter_cmd); + install_element(BFD_NODE, &sbfd_init_no_peer_raw_cmd); + + install_element(BFD_NODE, &sbfd_reflector_cmd); + install_element(BFD_NODE, &no_sbfd_reflector_all_cmd); + install_element(BFD_NODE, &no_sbfd_reflector_cmd); + install_element(VIEW_NODE, &sbfd_reflector_show_info_cmd); install_element(BFD_PEER_NODE, &bfd_peer_shutdown_cmd); install_element(BFD_PEER_NODE, &bfd_peer_mult_cmd); install_element(BFD_PEER_NODE, &bfd_peer_rx_cmd); diff --git a/bfdd/bfdd_nb.c b/bfdd/bfdd_nb.c index 114fbc2bdd..f60d8397bb 100644 --- a/bfdd/bfdd_nb.c +++ b/bfdd/bfdd_nb.c @@ -484,6 +484,459 @@ const struct frr_yang_module_info frr_bfdd_info = { } }, { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo", + .cbs = { + .create = bfdd_bfd_sessions_sbfd_echo_create, + .destroy = bfdd_bfd_sessions_sbfd_echo_destroy, + .get_next = bfdd_bfd_sessions_sbfd_echo_get_next, + .get_keys = bfdd_bfd_sessions_sbfd_echo_get_keys, + .lookup_entry = bfdd_bfd_sessions_sbfd_echo_lookup_entry, + .cli_show = bfd_cli_show_sbfd_echo_peer, /* TODO */ + .cli_show_end = bfd_cli_show_peer_end, /* TODO */ + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/dest-addr", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_echo_dest_addr_modify, + .destroy = bfdd_bfd_sessions_sbfd_echo_dest_addr_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/srv6-source-ipv6", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_srv6_source_ipv6_modify, + .destroy = bfdd_bfd_sessions_sbfd_srv6_source_ipv6_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/profile", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_profile_modify, + .destroy = bfdd_bfd_sessions_single_hop_profile_destroy, + .cli_show = bfd_cli_peer_profile_show, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/detection-multiplier", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_detection_multiplier_modify, + .cli_show = bfd_cli_show_mult, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/desired-transmission-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify, + .cli_show = bfd_cli_show_tx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/required-receive-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_required_receive_interval_modify, + .cli_show = bfd_cli_show_rx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/echo-mode", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_echo_mode_modify, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/desired-echo-transmission-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify, + .cli_show = bfd_cli_show_desired_echo_transmission_interval, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/required-echo-receive-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify, + .cli_show = bfd_cli_show_required_echo_receive_interval, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/administrative-down", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_administrative_down_modify, + .cli_show = bfd_cli_show_shutdown, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/passive-mode", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_passive_mode_modify, + .cli_show = bfd_cli_show_passive, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/bfd-mode", + .cbs = { + .modify = bfdd_bfd_sessions_bfd_mode_modify, + .destroy = bfdd_bfd_sessions_bfd_mode_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/srv6-encap-data", + .cbs = { + .create = bfdd_bfd_sessions_segment_list_create, + .destroy = bfdd_bfd_sessions_segment_list_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/minimum-ttl", + .cbs = { + .modify = bfdd_bfd_sessions_multi_hop_minimum_ttl_modify, + .cli_show = bfd_cli_show_minimum_ttl, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/multi-hop", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_multi_hop_modify, + .destroy = bfdd_bfd_sessions_sbfd_multi_hop_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/local-discriminator", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/local-state", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_state_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/local-diagnostic", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/local-multiplier", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/remote-discriminator", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/remote-state", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/remote-diagnostic", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/remote-multiplier", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/negotiated-transmission-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/negotiated-receive-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/detection-mode", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/last-down-time", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/last-up-time", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/session-down-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/session-up-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/control-packet-input-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/control-packet-output-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/negotiated-echo-transmission-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/echo-packet-input-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-echo/stats/echo-packet-output-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init", + .cbs = { + .create = bfdd_bfd_sessions_sbfd_init_create, + .destroy = bfdd_bfd_sessions_sbfd_init_destroy, + .get_next = bfdd_bfd_sessions_sbfd_init_get_next, + .get_keys = bfdd_bfd_sessions_sbfd_init_get_keys, + .lookup_entry = bfdd_bfd_sessions_sbfd_init_lookup_entry, + .cli_show = bfd_cli_show_sbfd_init_peer, /* TODO */ + .cli_show_end = bfd_cli_show_peer_end, /* TODO */ + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/srv6-source-ipv6", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_srv6_source_ipv6_modify, + .destroy = bfdd_bfd_sessions_sbfd_srv6_source_ipv6_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/remote-discr", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_init_remote_discr_modify, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/profile", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_profile_modify, + .destroy = bfdd_bfd_sessions_single_hop_profile_destroy, + .cli_show = bfd_cli_peer_profile_show, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/detection-multiplier", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_detection_multiplier_modify, + .cli_show = bfd_cli_show_mult, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/desired-transmission-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify, + .cli_show = bfd_cli_show_tx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/required-receive-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_required_receive_interval_modify, + .cli_show = bfd_cli_show_rx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/administrative-down", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_administrative_down_modify, + .cli_show = bfd_cli_show_shutdown, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/passive-mode", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_passive_mode_modify, + .cli_show = bfd_cli_show_passive, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/bfd-mode", + .cbs = { + .modify = bfdd_bfd_sessions_bfd_mode_modify, + .destroy = bfdd_bfd_sessions_bfd_mode_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/srv6-encap-data", + .cbs = { + .create = bfdd_bfd_sessions_segment_list_create, + .destroy = bfdd_bfd_sessions_segment_list_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/minimum-ttl", + .cbs = { + .modify = bfdd_bfd_sessions_multi_hop_minimum_ttl_modify, + .cli_show = bfd_cli_show_minimum_ttl, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/multi-hop", + .cbs = { + .modify = bfdd_bfd_sessions_sbfd_multi_hop_modify, + .destroy = bfdd_bfd_sessions_sbfd_multi_hop_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/local-discriminator", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/local-state", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_state_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/local-diagnostic", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/local-multiplier", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/remote-discriminator", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/remote-state", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/remote-diagnostic", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/remote-multiplier", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/negotiated-transmission-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/negotiated-receive-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/detection-mode", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/last-down-time", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/last-up-time", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/session-down-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/session-up-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/control-packet-input-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/control-packet-output-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/negotiated-echo-transmission-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/echo-packet-input-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/sbfd-init/stats/echo-packet-output-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem, + } + }, + { .xpath = NULL, }, } diff --git a/bfdd/bfdd_nb.h b/bfdd/bfdd_nb.h index b5b00b57e4..6621973ae3 100644 --- a/bfdd/bfdd_nb.h +++ b/bfdd/bfdd_nb.h @@ -112,6 +112,26 @@ bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem( struct nb_cb_get_elem_args *args); int bfdd_bfd_sessions_multi_hop_create(struct nb_cb_create_args *args); int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args); +int bfdd_bfd_sessions_sbfd_echo_create(struct nb_cb_create_args *args); +int bfdd_bfd_sessions_sbfd_echo_destroy(struct nb_cb_destroy_args *args); +int bfdd_bfd_sessions_sbfd_echo_dest_addr_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_sbfd_echo_mode_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_sbfd_echo_dest_addr_destroy(struct nb_cb_destroy_args *args); +int bfdd_bfd_sessions_sbfd_srv6_source_ipv6_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_sbfd_srv6_source_ipv6_destroy(struct nb_cb_destroy_args *args); +int bfdd_bfd_sessions_sbfd_init_remote_discr_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_sbfd_multi_hop_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_sbfd_multi_hop_destroy(struct nb_cb_destroy_args *args); + +int bfdd_bfd_sessions_sbfd_init_create(struct nb_cb_create_args *args); +int bfdd_bfd_sessions_sbfd_init_destroy(struct nb_cb_destroy_args *args); +const void *bfdd_bfd_sessions_sbfd_echo_get_next(struct nb_cb_get_next_args *args); +int bfdd_bfd_sessions_sbfd_echo_get_keys(struct nb_cb_get_keys_args *args); +const void *bfdd_bfd_sessions_sbfd_echo_lookup_entry(struct nb_cb_lookup_entry_args *args); +const void *bfdd_bfd_sessions_sbfd_init_get_next(struct nb_cb_get_next_args *args); +int bfdd_bfd_sessions_sbfd_init_get_keys(struct nb_cb_get_keys_args *args); +const void *bfdd_bfd_sessions_sbfd_init_lookup_entry(struct nb_cb_lookup_entry_args *args); + const void * bfdd_bfd_sessions_multi_hop_get_next(struct nb_cb_get_next_args *args); int bfdd_bfd_sessions_multi_hop_get_keys(struct nb_cb_get_keys_args *args); @@ -185,6 +205,8 @@ void bfd_cli_show_single_hop_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void bfd_cli_show_multi_hop_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void bfd_cli_show_sbfd_echo_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void bfd_cli_show_sbfd_init_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void bfd_cli_show_peer_end(struct vty *vty, const struct lyd_node *dnode); void bfd_cli_show_mult(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); @@ -210,4 +232,10 @@ void bfd_cli_show_passive(struct vty *vty, const struct lyd_node *dnode, void bfd_cli_show_minimum_ttl(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +int bfdd_bfd_sessions_bfd_mode_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_bfd_mode_destroy(struct nb_cb_destroy_args *args); + +int bfdd_bfd_sessions_segment_list_create(struct nb_cb_create_args *args); +int bfdd_bfd_sessions_segment_list_destroy(struct nb_cb_destroy_args *args); + #endif /* _FRR_BFDD_NB_H_ */ diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c index 48fbe7139c..15da1e2440 100644 --- a/bfdd/bfdd_nb_config.c +++ b/bfdd/bfdd_nb_config.c @@ -13,14 +13,50 @@ #include "bfd.h" #include "bfdd_nb.h" +#include <ifaddrs.h> /* * Helpers. */ +static void get_ip_by_interface(const char *ifname, const char *vrfname, int family, char *ifip) +{ + char intfip[INET6_ADDRSTRLEN]; + const struct interface *interface; + const struct connected *connected; + struct vrf *vrf; + + vrf = vrf_lookup_by_name(vrfname ? vrfname : VRF_DEFAULT_NAME); + if (!vrf) + return; + + interface = if_lookup_by_name_vrf(ifname, vrf); + if (interface == NULL) + return; + + frr_each (if_connected_const, interface->connected, connected) { + if (!connected->address) + continue; + + if (family != connected->address->family) + continue; + + if (family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&connected->address->u.prefix6)) + continue; + + inet_ntop(family, + family == AF_INET ? (void *)(&connected->address->u.prefix4) + : (void *)(&connected->address->u.prefix6), + intfip, INET6_ADDRSTRLEN); + strlcpy(ifip, intfip, INET6_ADDRSTRLEN - 1); + break; + } +} + static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode, struct bfd_key *bk) { const char *ifname = NULL, *vrfname = NULL; + char ifip[INET6_ADDRSTRLEN] = { 0 }; struct sockaddr_any psa, lsa; /* Required destination parameter. */ @@ -37,10 +73,36 @@ static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode, ifname = yang_dnode_get_string(dnode, "interface"); if (strcmp(ifname, "*") == 0) ifname = NULL; + + if (ifname != NULL && !yang_dnode_exists(dnode, "source-addr") && + psa.sa_sin.sin_family != 0) { + get_ip_by_interface(ifname, vrfname, psa.sa_sin.sin_family, ifip); + strtosa(ifip, &lsa); + } } /* Generate the corresponding key. */ - gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname); + gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname, NULL); +} + +static void sbfd_session_get_key(bool mhop, const struct lyd_node *dnode, struct bfd_key *bk) +{ + const char *ifname = NULL, *vrfname = NULL, *bfdname = NULL; + struct sockaddr_any psa, lsa; + + /* Required source parameter. */ + strtosa(yang_dnode_get_string(dnode, "source-addr"), &lsa); + + strtosa(yang_dnode_get_string(dnode, "dest-addr"), &psa); + + if (yang_dnode_exists(dnode, "bfd-name")) + bfdname = yang_dnode_get_string(dnode, "bfd-name"); + + if (yang_dnode_exists(dnode, "vrf")) + vrfname = yang_dnode_get_string(dnode, "vrf"); + + /* Generate the corresponding key. */ + gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname, bfdname); } struct session_iter { @@ -63,7 +125,25 @@ static int session_iter_cb(const struct lyd_node *dnode, void *arg) return YANG_ITER_CONTINUE; } -static int bfd_session_create(struct nb_cb_create_args *args, bool mhop) +static int segment_list_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct bfd_session *bs = arg; + uint8_t segnum = bs->segnum; + const char *addr; + struct sockaddr_any sa; + + addr = yang_dnode_get_string(dnode, NULL); + + if (strtosa(addr, &sa) < 0 || sa.sa_sin6.sin6_family != AF_INET6) + return YANG_ITER_STOP; + + memcpy(&bs->seg_list[segnum], &sa.sa_sin6.sin6_addr, sizeof(struct in6_addr)); + bs->segnum = segnum + 1; + + return YANG_ITER_CONTINUE; +} + +static int bfd_session_create(struct nb_cb_create_args *args, bool mhop, uint32_t bfd_mode) { const struct lyd_node *sess_dnode; struct session_iter iter; @@ -73,10 +153,20 @@ static int bfd_session_create(struct nb_cb_create_args *args, bool mhop) const char *vrfname; struct bfd_key bk; struct prefix p; + const char *bfd_name = NULL; + struct sockaddr_any out_sip6; switch (args->event) { case NB_EV_VALIDATE: - yang_dnode_get_prefix(&p, args->dnode, "dest-addr"); + if ((bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) || (bfd_mode == BFD_MODE_TYPE_SBFD_INIT)) { + if (bfd_session_get_by_name(yang_dnode_get_string(args->dnode, "bfd-name"))) { + snprintf(args->errmsg, args->errmsg_len, "bfd name already exist."); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + yang_dnode_get_prefix(&p, args->dnode, "./dest-addr"); if (mhop) { /* @@ -128,34 +218,123 @@ static int bfd_session_create(struct nb_cb_create_args *args, bool mhop) break; case NB_EV_PREPARE: - bfd_session_get_key(mhop, args->dnode, &bk); - bs = bfd_key_lookup(bk); + if (bfd_mode == BFD_MODE_TYPE_BFD) { + bfd_session_get_key(mhop, args->dnode, &bk); + bs = bfd_key_lookup(bk); + + /* This session was already configured by another daemon. */ + if (bs != NULL) { + /* Now it is configured also by CLI. */ + SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + bs->refcount++; + + args->resource->ptr = bs; + break; + } + + bs = bfd_session_new(BFD_MODE_TYPE_BFD); - /* This session was already configured by another daemon. */ - if (bs != NULL) { - /* Now it is configured also by CLI. */ + /* Fill the session key. */ + bfd_session_get_key(mhop, args->dnode, &bs->key); + /* Set configuration flags. */ + bs->refcount = 1; SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); - bs->refcount++; + if (mhop) + SET_FLAG(bs->flags, BFD_SESS_FLAG_MH); + if (bs->key.family == AF_INET6) + SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6); args->resource->ptr = bs; break; - } + } else if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || + bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + sbfd_session_get_key(mhop, args->dnode, &bk); + bs = bfd_key_lookup(bk); + + /* This session was already configured by another daemon. */ + if (bs != NULL) { + /* Now it is configured also by CLI. */ + SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + bs->refcount++; + + args->resource->ptr = bs; + break; + } - bs = bfd_session_new(); + if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO && + !yang_dnode_exists(args->dnode, "srv6-encap-data")) { + //srv6-encap-data should not be null for sbfd echo + snprintf(args->errmsg, args->errmsg_len, + "srv6-encap-data should not be null"); + return NB_ERR_RESOURCE; + } - /* Fill the session key. */ - bfd_session_get_key(mhop, args->dnode, &bs->key); + if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO && + !yang_dnode_exists(args->dnode, "srv6-source-ipv6")) { + snprintf(args->errmsg, args->errmsg_len, + "source_ipv6 should not be null"); + return NB_ERR_RESOURCE; + } - /* Set configuration flags. */ - bs->refcount = 1; - SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); - if (mhop) - SET_FLAG(bs->flags, BFD_SESS_FLAG_MH); - if (bs->key.family == AF_INET6) - SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6); + if (bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + if (!yang_dnode_exists(args->dnode, "remote-discr")) { + snprintf(args->errmsg, args->errmsg_len, + "remote-discr should not be null"); + return NB_ERR_RESOURCE; + } + } - args->resource->ptr = bs; - break; + bfd_name = yang_dnode_get_string(args->dnode, "bfd-name"); + + bs = bfd_session_new(bfd_mode); + if (bs == NULL) { + snprintf(args->errmsg, args->errmsg_len, + "session-new: allocation failed"); + return NB_ERR_RESOURCE; + } + /* Fill the session key. */ + sbfd_session_get_key(mhop, args->dnode, &bs->key); + strlcpy(bs->bfd_name, bfd_name, BFD_NAME_SIZE); + + if (yang_dnode_exists(args->dnode, "srv6-encap-data")) { + yang_dnode_iterate(segment_list_iter_cb, bs, args->dnode, + "./srv6-encap-data"); + + + strtosa(yang_dnode_get_string(args->dnode, "./srv6-source-ipv6"), + &out_sip6); + memcpy(&bs->out_sip6, &out_sip6.sa_sin6.sin6_addr, + sizeof(struct in6_addr)); + } + + if (bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + bs->discrs.remote_discr = yang_dnode_get_uint32(args->dnode, + "./remote-discr"); + } + + /* Set configuration flags. */ + bs->refcount = 1; + SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + if (mhop) + SET_FLAG(bs->flags, BFD_SESS_FLAG_MH); + + if (bs->key.family == AF_INET6) + SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6); + + if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + memcpy(&bs->key.peer, &bs->key.local, sizeof(struct in6_addr)); + } else { + bs->xmt_TO = bs->timers.desired_min_tx; + bs->detect_TO = bs->detect_mult * bs->xmt_TO; + } + + args->resource->ptr = bs; + break; + + } else { + snprintf(args->errmsg, args->errmsg_len, "bfd mode must be bfd or sbfd."); + return NB_ERR_VALIDATION; + } case NB_EV_APPLY: bs = args->resource->ptr; @@ -177,15 +356,19 @@ static int bfd_session_create(struct nb_cb_create_args *args, bool mhop) return NB_OK; } -static int bfd_session_destroy(enum nb_event event, - const struct lyd_node *dnode, bool mhop) +static int bfd_session_destroy(enum nb_event event, const struct lyd_node *dnode, bool mhop, + uint32_t bfd_mode) { struct bfd_session *bs; struct bfd_key bk; switch (event) { case NB_EV_VALIDATE: - bfd_session_get_key(mhop, dnode, &bk); + if (bfd_mode == BFD_MODE_TYPE_BFD) + bfd_session_get_key(mhop, dnode, &bk); + else + sbfd_session_get_key(mhop, dnode, &bk); + if (bfd_key_lookup(bk) == NULL) return NB_ERR_INCONSISTENCY; break; @@ -206,6 +389,12 @@ static int bfd_session_destroy(enum nb_event event, if (bs->refcount > 0) break; + if (bglobal.debug_peer_event) + zlog_info("session destroy: %s", bs_to_string(bs)); + + if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bfd_mode == BFD_MODE_TYPE_SBFD_INIT) + ptm_bfd_notify(bs, PTM_BFD_DOWN); + bfd_session_free(bs); break; @@ -510,12 +699,12 @@ int bfdd_bfd_profile_required_echo_receive_interval_modify( */ int bfdd_bfd_sessions_single_hop_create(struct nb_cb_create_args *args) { - return bfd_session_create(args, false); + return bfd_session_create(args, false, BFD_MODE_TYPE_BFD); } int bfdd_bfd_sessions_single_hop_destroy(struct nb_cb_destroy_args *args) { - return bfd_session_destroy(args->event, args->dnode, false); + return bfd_session_destroy(args->event, args->dnode, false, BFD_MODE_TYPE_BFD); } /* @@ -715,6 +904,45 @@ int bfdd_bfd_sessions_single_hop_passive_mode_modify( } /* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init/bfd-mode + * /frr-bfdd:bfdd/bfd/sessions/sbfd-echo/bfd-mode + */ +int bfdd_bfd_sessions_bfd_mode_modify(struct nb_cb_modify_args *args) +{ + uint32_t bfd_mode = yang_dnode_get_uint32(args->dnode, NULL); + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + if ((bfd_mode != BFD_MODE_TYPE_BFD) && (bfd_mode != BFD_MODE_TYPE_SBFD_ECHO) && + (bfd_mode != BFD_MODE_TYPE_SBFD_INIT)) { + snprintf(args->errmsg, args->errmsg_len, "bfd mode is invalid."); + return NB_ERR_VALIDATION; + } + return NB_OK; + case NB_EV_PREPARE: + return NB_OK; + + case NB_EV_APPLY: + break; + + case NB_EV_ABORT: + return NB_OK; + } + + bs = nb_running_get_entry(args->dnode, NULL, true); + bs->bfd_mode = bfd_mode; + bfd_session_apply(bs); + + return NB_OK; +} + +int bfdd_bfd_sessions_bfd_mode_destroy(struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode */ int bfdd_bfd_sessions_single_hop_echo_mode_modify( @@ -811,12 +1039,12 @@ int bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify( */ int bfdd_bfd_sessions_multi_hop_create(struct nb_cb_create_args *args) { - return bfd_session_create(args, true); + return bfd_session_create(args, true, BFD_MODE_TYPE_BFD); } int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args) { - return bfd_session_destroy(args->event, args->dnode, true); + return bfd_session_destroy(args->event, args->dnode, true, BFD_MODE_TYPE_BFD); } /* @@ -845,3 +1073,106 @@ int bfdd_bfd_sessions_multi_hop_minimum_ttl_modify( return NB_OK; } + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo + */ +int bfdd_bfd_sessions_sbfd_echo_create(struct nb_cb_create_args *args) +{ + return bfd_session_create(args, yang_dnode_exists(args->dnode, "multi-hop"), + BFD_MODE_TYPE_SBFD_ECHO); +} + +int bfdd_bfd_sessions_sbfd_echo_destroy(struct nb_cb_destroy_args *args) +{ + return bfd_session_destroy(args->event, args->dnode, + yang_dnode_exists(args->dnode, "multi-hop"), + BFD_MODE_TYPE_SBFD_ECHO); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo/srv6-encap-data + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init/srv6-encap-data + */ +int bfdd_bfd_sessions_segment_list_create(struct nb_cb_create_args *args) +{ + return NB_OK; +} + +int bfdd_bfd_sessions_segment_list_destroy(struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo/dest-addr + */ +int bfdd_bfd_sessions_sbfd_echo_dest_addr_modify(struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +int bfdd_bfd_sessions_sbfd_echo_dest_addr_destroy(struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo/echo-mode + */ +int bfdd_bfd_sessions_sbfd_echo_mode_modify(struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo/srv6-source-ipv6 + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init/srv6-source-ipv6 + */ +int bfdd_bfd_sessions_sbfd_srv6_source_ipv6_modify(struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +int bfdd_bfd_sessions_sbfd_srv6_source_ipv6_destroy(struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init + */ +int bfdd_bfd_sessions_sbfd_init_create(struct nb_cb_create_args *args) +{ + return bfd_session_create(args, yang_dnode_exists(args->dnode, "multi-hop"), + BFD_MODE_TYPE_SBFD_INIT); +} + +int bfdd_bfd_sessions_sbfd_init_destroy(struct nb_cb_destroy_args *args) +{ + return bfd_session_destroy(args->event, args->dnode, + yang_dnode_exists(args->dnode, "multi-hop"), + BFD_MODE_TYPE_SBFD_INIT); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init/remote-discr + */ +int bfdd_bfd_sessions_sbfd_init_remote_discr_modify(struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo/multi-hop + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init/multi-hop + */ +int bfdd_bfd_sessions_sbfd_multi_hop_modify(struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +int bfdd_bfd_sessions_sbfd_multi_hop_destroy(struct nb_cb_destroy_args *args) +{ + return NB_OK; +} diff --git a/bfdd/bfdd_nb_state.c b/bfdd/bfdd_nb_state.c index 12acda8fd8..c528478231 100644 --- a/bfdd/bfdd_nb_state.c +++ b/bfdd/bfdd_nb_state.c @@ -20,7 +20,7 @@ const void * bfdd_bfd_sessions_single_hop_get_next(struct nb_cb_get_next_args *args) { - return bfd_session_next(args->list_entry, false); + return bfd_session_next(args->list_entry, false, BFD_MODE_TYPE_BFD); } int bfdd_bfd_sessions_single_hop_get_keys(struct nb_cb_get_keys_args *args) @@ -50,7 +50,7 @@ bfdd_bfd_sessions_single_hop_lookup_entry(struct nb_cb_lookup_entry_args *args) strtosa(dest_addr, &psa); memset(&lsa, 0, sizeof(lsa)); - gen_bfd_key(&bk, &psa, &lsa, false, ifname, vrf); + gen_bfd_key(&bk, &psa, &lsa, false, ifname, vrf, NULL); return bfd_key_lookup(bk); } @@ -323,7 +323,7 @@ bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem( const void * bfdd_bfd_sessions_multi_hop_get_next(struct nb_cb_get_next_args *args) { - return bfd_session_next(args->list_entry, true); + return bfd_session_next(args->list_entry, true, BFD_MODE_TYPE_BFD); } int bfdd_bfd_sessions_multi_hop_get_keys(struct nb_cb_get_keys_args *args) @@ -354,7 +354,87 @@ bfdd_bfd_sessions_multi_hop_lookup_entry(struct nb_cb_lookup_entry_args *args) strtosa(dest_addr, &psa); strtosa(source_addr, &lsa); - gen_bfd_key(&bk, &psa, &lsa, true, NULL, vrf); + gen_bfd_key(&bk, &psa, &lsa, true, NULL, vrf, NULL); + + return bfd_key_lookup(bk); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-echo + */ +const void *bfdd_bfd_sessions_sbfd_echo_get_next(struct nb_cb_get_next_args *args) +{ + return bfd_session_next(args->list_entry, true, BFD_MODE_TYPE_SBFD_ECHO); +} + +int bfdd_bfd_sessions_sbfd_echo_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct bfd_session *bs = args->list_entry; + char srcbuf[INET6_ADDRSTRLEN]; + + inet_ntop(bs->key.family, &bs->key.local, srcbuf, sizeof(srcbuf)); + + args->keys->num = 3; + strlcpy(args->keys->key[0], srcbuf, sizeof(args->keys->key[0])); + strlcpy(args->keys->key[1], bs->key.bfdname, sizeof(args->keys->key[1])); + strlcpy(args->keys->key[2], bs->key.vrfname, sizeof(args->keys->key[2])); + + return NB_OK; +} + +const void *bfdd_bfd_sessions_sbfd_echo_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const char *source_addr = args->keys->key[0]; + const char *bfdname = args->keys->key[1]; + const char *vrf = args->keys->key[2]; + struct sockaddr_any psa, lsa; + struct bfd_key bk; + + strtosa(source_addr, &lsa); + memset(&psa, 0, sizeof(psa)); + gen_bfd_key(&bk, &psa, &lsa, true, NULL, vrf, bfdname); + + return bfd_key_lookup(bk); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/sbfd-init + */ +const void *bfdd_bfd_sessions_sbfd_init_get_next(struct nb_cb_get_next_args *args) +{ + return bfd_session_next(args->list_entry, true, BFD_MODE_TYPE_SBFD_INIT); +} + +int bfdd_bfd_sessions_sbfd_init_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct bfd_session *bs = args->list_entry; + char srcbuf[INET6_ADDRSTRLEN]; + char dstbuf[INET6_ADDRSTRLEN]; + + inet_ntop(bs->key.family, &bs->key.local, srcbuf, sizeof(srcbuf)); + inet_ntop(bs->key.family, &bs->key.peer, dstbuf, sizeof(dstbuf)); + + args->keys->num = 4; + strlcpy(args->keys->key[0], srcbuf, sizeof(args->keys->key[0])); + strlcpy(args->keys->key[1], dstbuf, sizeof(args->keys->key[1])); + strlcpy(args->keys->key[2], bs->key.bfdname, sizeof(args->keys->key[2])); + strlcpy(args->keys->key[3], bs->key.vrfname, sizeof(args->keys->key[3])); + + return NB_OK; +} + +const void *bfdd_bfd_sessions_sbfd_init_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const char *source_addr = args->keys->key[0]; + const char *dest_addr = args->keys->key[1]; + const char *bfdname = args->keys->key[2]; + const char *vrf = args->keys->key[3]; + struct sockaddr_any psa, lsa; + struct bfd_key bk; + + strtosa(source_addr, &lsa); + strtosa(dest_addr, &psa); + gen_bfd_key(&bk, &psa, &lsa, true, NULL, vrf, bfdname); return bfd_key_lookup(bk); } diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c index 26554e1496..c281197849 100644 --- a/bfdd/bfdd_vty.c +++ b/bfdd/bfdd_vty.c @@ -58,7 +58,49 @@ _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv, const char *label, const char *peer_str, const char *local_str, const char *ifname, const char *vrfname); +static void _display_bfd_by_bfdname_json_iter(struct hash_bucket *hb, void *arg); +static void _display_bfd_by_bfdname_iter(struct hash_bucket *hb, void *arg); +static void _display_bfd_by_bfdname(struct vty *vty, const char *vrfname, const char *bfdname, + bool use_json); +static void _display_bfd_counters_by_bfdname_iter(struct hash_bucket *hb, void *arg); +static void _display_bfd_counters_json_by_bfdname_iter(struct hash_bucket *hb, void *arg); +static void _display_bfd_counters_by_bfdname(struct vty *vty, const char *vrfname, + const char *bfdname, bool use_json); +static void _clear_bfd_counters_by_bfdname(const char *vrfname, const char *bfdname); +static void _clear_peer_counter(struct bfd_session *bs); + +static const char *bfd_mode_type_to_string(enum bfd_mode_type mode) +{ + switch (mode) { + case BFD_MODE_TYPE_BFD: + return "bfd"; + case BFD_MODE_TYPE_SBFD_ECHO: + return "sbfd-echo"; + case BFD_MODE_TYPE_SBFD_INIT: + return "sbfd-init"; + default: + return "Unknown"; + } +} + +static char *sbfd_sidlist_to_string(struct in6_addr *sidlist, uint8_t segnum) +{ + static char buf[INET6_ADDRSTRLEN * SRV6_MAX_SEGS]; + int pos = 0; + uint8_t i = 0; + char addr_buf[INET6_ADDRSTRLEN]; + memset(buf, 0, 256); + + pos = snprintf(buf, sizeof(buf), "%s", + inet_ntop(AF_INET6, &sidlist[0], addr_buf, sizeof(addr_buf))); + + for (i = 1; i < segnum; i++) + pos += snprintf(buf + pos, sizeof(buf) - pos, " %s", + inet_ntop(AF_INET6, &sidlist[i], addr_buf, sizeof(addr_buf))); + + return buf; +} /* * Show commands helper functions @@ -71,6 +113,12 @@ static void _display_peer_header(struct vty *vty, struct bfd_session *bs) inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf))); + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) + vty_out(vty, " bfd-mode %s", bfd_mode_type_to_string(bs->bfd_mode)); + + if (bs->bfd_name[0]) + vty_out(vty, " bfd-name %s", bs->bfd_name); + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) vty_out(vty, " multihop"); @@ -83,6 +131,20 @@ static void _display_peer_header(struct vty *vty, struct bfd_session *bs) vty_out(vty, " vrf %s", bs->key.vrfname); if (bs->key.ifname[0]) vty_out(vty, " interface %s", bs->key.ifname); + + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) + vty_out(vty, " remote-discr %u", bs->discrs.remote_discr); + + if (bs->bfd_name[0] && bs->segnum) { + vty_out(vty, " srv6-source-ipv6 %s", + inet_ntop(AF_INET6, &bs->out_sip6, addr_buf, sizeof(addr_buf))); + + vty_out(vty, " srv6-encap-data %s", + sbfd_sidlist_to_string(bs->seg_list, bs->segnum)); + } + } + vty_out(vty, "\n"); } @@ -135,10 +197,16 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs) vty_out(vty, "\t\tDiagnostics: %s\n", diag2str(bs->local_diag)); vty_out(vty, "\t\tRemote diagnostics: %s\n", diag2str(bs->remote_diag)); - vty_out(vty, "\t\tPeer Type: %s\n", - CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) ? "configured" : "dynamic"); - _display_rtt(&min, &avg, &max, bs); - vty_out(vty, "\t\tRTT min/avg/max: %u/%u/%u usec\n", min, avg, max); + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) { + vty_out(vty, "\t\tPeer Type: sbfd initiator\n"); + } else if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + vty_out(vty, "\t\tPeer Type: echo\n"); + } else { + vty_out(vty, "\t\tPeer Type: %s\n", + CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) ? "configured" : "dynamic"); + _display_rtt(&min, &avg, &max, bs); + vty_out(vty, "\t\tRTT min/avg/max: %u/%u/%u usec\n", min, avg, max); + } vty_out(vty, "\t\tLocal timers:\n"); vty_out(vty, "\t\t\tDetect-multiplier: %u\n", @@ -152,24 +220,32 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs) bs->timers.required_min_echo_rx / 1000); else vty_out(vty, "\t\t\tEcho receive interval: disabled\n"); - if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO) || bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) vty_out(vty, "\t\t\tEcho transmission interval: %ums\n", bs->timers.desired_min_echo_tx / 1000); else vty_out(vty, "\t\t\tEcho transmission interval: disabled\n"); - vty_out(vty, "\t\tRemote timers:\n"); - vty_out(vty, "\t\t\tDetect-multiplier: %u\n", - bs->remote_detect_mult); - vty_out(vty, "\t\t\tReceive interval: %ums\n", - bs->remote_timers.required_min_rx / 1000); - vty_out(vty, "\t\t\tTransmission interval: %ums\n", - bs->remote_timers.desired_min_tx / 1000); - if (bs->remote_timers.required_min_echo != 0) - vty_out(vty, "\t\t\tEcho receive interval: %ums\n", - bs->remote_timers.required_min_echo / 1000); - else - vty_out(vty, "\t\t\tEcho receive interval: disabled\n"); + + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT || bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + vty_out(vty, "\t\tRemote timers:\n"); + vty_out(vty, "\t\t\tDetect-multiplier: -\n"); + vty_out(vty, "\t\t\tReceive interval: -\n"); + vty_out(vty, "\t\t\tTransmission interval: -\n"); + vty_out(vty, "\t\t\tEcho receive interval: -\n"); + } else { + vty_out(vty, "\t\tRemote timers:\n"); + vty_out(vty, "\t\t\tDetect-multiplier: %u\n", bs->remote_detect_mult); + vty_out(vty, "\t\t\tReceive interval: %ums\n", + bs->remote_timers.required_min_rx / 1000); + vty_out(vty, "\t\t\tTransmission interval: %ums\n", + bs->remote_timers.desired_min_tx / 1000); + if (bs->remote_timers.required_min_echo != 0) + vty_out(vty, "\t\t\tEcho receive interval: %ums\n", + bs->remote_timers.required_min_echo / 1000); + else + vty_out(vty, "\t\t\tEcho receive interval: disabled\n"); + } vty_out(vty, "\n"); } @@ -216,6 +292,9 @@ static struct json_object *__display_peer_json(struct bfd_session *bs) if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) json_object_int_add(jo, "minimum-ttl", bs->mh_ttl); + if (bs->bfd_mode != BFD_MODE_TYPE_BFD) + json_object_string_add(jo, "bfd-name", bs->bfd_name); + switch (bs->ses_state) { case PTM_BFD_ADM_DOWN: json_object_string_add(jo, "status", "shutdown"); @@ -253,7 +332,13 @@ static struct json_object *__display_peer_json(struct bfd_session *bs) bs->timers.desired_min_tx / 1000); json_object_int_add(jo, "echo-receive-interval", bs->timers.required_min_echo_rx / 1000); - if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT || bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) { + json_object_int_add(jo, "configured-echo-transmit-interval", + bs->timers.desired_min_echo_tx / 1000); + json_object_int_add(jo, "current-echo-transmit-interval", bs->echo_xmt_TO / 1000); + json_object_int_add(jo, "current-detect-echo-receive-interval", + bs->echo_detect_TO / 1000); + } else if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) json_object_int_add(jo, "echo-transmit-interval", bs->timers.desired_min_echo_tx / 1000); else @@ -289,6 +374,7 @@ struct bfd_vrf_tuple { const char *vrfname; struct vty *vty; struct json_object *jo; + const char *bfdname; }; static void _display_peer_iter(struct hash_bucket *hb, void *arg) @@ -309,6 +395,30 @@ static void _display_peer_iter(struct hash_bucket *hb, void *arg) _display_peer(vty, bs); } +static void _display_bfd_by_bfdname_iter(struct hash_bucket *hb, void *arg) +{ + struct bfd_vrf_tuple *bvt = (struct bfd_vrf_tuple *)arg; + struct vty *vty; + struct bfd_session *bs = hb->data; + + if (!bvt) + return; + vty = bvt->vty; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + + if (bvt->bfdname) { + if ((!bs->key.bfdname[0] || !strmatch(bs->key.bfdname, bvt->bfdname)) && + (!bs->bfd_name[0] || !strmatch(bs->bfd_name, bvt->bfdname))) + return; + } + + _display_peer(vty, bs); +} + static void _display_peer_json_iter(struct hash_bucket *hb, void *arg) { struct bfd_vrf_tuple *bvt = (struct bfd_vrf_tuple *)arg; @@ -334,6 +444,58 @@ static void _display_peer_json_iter(struct hash_bucket *hb, void *arg) json_object_array_add(jo, jon); } +static void _display_bfd_by_bfdname_json_iter(struct hash_bucket *hb, void *arg) +{ + struct bfd_vrf_tuple *bvt = (struct bfd_vrf_tuple *)arg; + struct json_object *jo, *jon = NULL; + struct bfd_session *bs = hb->data; + + if (!bvt) + return; + jo = bvt->jo; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + + if (bvt->bfdname) { + if ((!bs->key.bfdname[0] || !strmatch(bs->key.bfdname, bvt->bfdname)) && + (!bs->bfd_name[0] || !strmatch(bs->bfd_name, bvt->bfdname))) + return; + } + + jon = __display_peer_json(bs); + if (jon == NULL) { + zlog_warn("%s: not enough memory", __func__); + return; + } + + json_object_array_add(jo, jon); +} +static void _display_bfd_by_bfdname(struct vty *vty, const char *vrfname, const char *bfdname, + bool use_json) +{ + struct json_object *jo; + struct bfd_vrf_tuple bvt = { 0 }; + + bvt.vrfname = vrfname; + bvt.bfdname = bfdname; + + if (!use_json) { + bvt.vty = vty; + vty_out(vty, "BFD Peers:\n"); + bfd_id_iterate(_display_bfd_by_bfdname_iter, &bvt); + return; + } + + jo = json_object_new_array(); + bvt.jo = jo; + bfd_id_iterate(_display_bfd_by_bfdname_json_iter, &bvt); + + vty_json(vty, jo); +} + static void _display_all_peers(struct vty *vty, char *vrfname, bool use_json) { struct json_object *jo; @@ -378,6 +540,7 @@ static void _display_peer_counter(struct vty *vty, struct bfd_session *bs) bs->stats.session_down); vty_out(vty, "\t\tZebra notifications: %" PRIu64 "\n", bs->stats.znotification); + vty_out(vty, "\t\tTx fail packet: %" PRIu64 "\n", bs->stats.tx_fail_pkt); vty_out(vty, "\n"); } @@ -398,6 +561,9 @@ static struct json_object *__display_peer_counters_json(struct bfd_session *bs) json_object_int_add(jo, "session-down", bs->stats.session_down); json_object_int_add(jo, "zebra-notifications", bs->stats.znotification); + if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT || bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO) + json_object_int_add(jo, "tx-fail-packet", bs->stats.tx_fail_pkt); + return jo; } @@ -472,6 +638,109 @@ static void _display_peers_counter(struct vty *vty, char *vrfname, bool use_json vty_json(vty, jo); } +static void _display_bfd_counters_by_bfdname_iter(struct hash_bucket *hb, void *arg) +{ + struct bfd_vrf_tuple *bvt = arg; + struct vty *vty; + struct bfd_session *bs = hb->data; + + if (!bvt) + return; + vty = bvt->vty; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + + if (bvt->bfdname) { + if (!bs->key.bfdname[0] || !strmatch(bs->key.bfdname, bvt->bfdname)) + return; + } + + _display_peer_counter(vty, bs); +} +static void _display_bfd_counters_json_by_bfdname_iter(struct hash_bucket *hb, void *arg) +{ + struct json_object *jo, *jon = NULL; + struct bfd_session *bs = hb->data; + struct bfd_vrf_tuple *bvt = arg; + + if (!bvt) + return; + jo = bvt->jo; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + + if (bvt->bfdname) { + if (!bs->key.bfdname[0] || !strmatch(bs->key.bfdname, bvt->bfdname)) + return; + } + + jon = __display_peer_counters_json(bs); + if (jon == NULL) { + zlog_warn("%s: not enough memory", __func__); + return; + } + + json_object_array_add(jo, jon); +} +static void _display_bfd_counters_by_bfdname(struct vty *vty, const char *vrfname, + const char *bfdname, bool use_json) +{ + struct json_object *jo; + struct bfd_vrf_tuple bvt = { 0 }; + + bvt.vrfname = vrfname; + bvt.bfdname = bfdname; + + if (!use_json) { + bvt.vty = vty; + vty_out(vty, "BFD Peers:\n"); + bfd_id_iterate(_display_bfd_counters_by_bfdname_iter, &bvt); + return; + } + + jo = json_object_new_array(); + bvt.jo = jo; + bfd_id_iterate(_display_bfd_counters_json_by_bfdname_iter, &bvt); + + vty_json(vty, jo); +} +static void _clear_bfd_counters_by_bfdname_iter(struct hash_bucket *hb, void *arg) +{ + struct bfd_vrf_tuple *bvt = arg; + struct bfd_session *bs = hb->data; + + if (!bvt) + return; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + + if (bvt->bfdname) { + if (!bs->key.bfdname[0] || !strmatch(bs->key.bfdname, bvt->bfdname)) + return; + } + + _clear_peer_counter(bs); +} + +static void _clear_bfd_counters_by_bfdname(const char *vrfname, const char *bfdname) +{ + struct bfd_vrf_tuple bvt = { 0 }; + + bvt.vrfname = vrfname; + bvt.bfdname = bfdname; + + bfd_id_iterate(_clear_bfd_counters_by_bfdname_iter, &bvt); +} + static void _clear_peer_counter(struct bfd_session *bs) { /* Clear only pkt stats, intention is not to loose system @@ -486,12 +755,21 @@ static void _display_peer_brief(struct vty *vty, struct bfd_session *bs) { char addr_buf[INET6_ADDRSTRLEN]; - vty_out(vty, "%-10u", bs->discrs.my_discr); - inet_ntop(bs->key.family, &bs->key.local, addr_buf, sizeof(addr_buf)); - vty_out(vty, " %-40s", addr_buf); - inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf)); - vty_out(vty, " %-40s", addr_buf); - vty_out(vty, "%-15s\n", state_list[bs->ses_state].str); + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { + vty_out(vty, "%-10u", bs->discrs.my_discr); + inet_ntop(bs->key.family, &bs->key.local, addr_buf, sizeof(addr_buf)); + vty_out(vty, " %-40s", addr_buf); + inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf)); + vty_out(vty, " %-40s", addr_buf); + vty_out(vty, "%-15s\n", state_list[bs->ses_state].str); + } else { + vty_out(vty, "%-10u", bs->discrs.my_discr); + vty_out(vty, " %-40s", satostr(&bs->local_address)); + inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf)); + vty_out(vty, " %-40s", addr_buf); + + vty_out(vty, "%-15s\n", state_list[bs->ses_state].str); + } } static void _display_peer_brief_iter(struct hash_bucket *hb, void *arg) @@ -558,6 +836,8 @@ _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv, struct bfd_peer_cfg bpc; struct sockaddr_any psa, lsa, *lsap; char errormsg[128]; + struct vrf *vrf = NULL; + char *tmpName = NULL; if (peer_str) { strtosa(peer_str, &psa); @@ -570,9 +850,18 @@ _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv, idx = 0; mhop = argv_find(argv, argc, "multihop", &idx); - if (bfd_configure_peer(&bpc, mhop, &psa, lsap, ifname, vrfname, - errormsg, sizeof(errormsg)) - != 0) { + tmpName = (char *)vrfname; + if (vrfname) { + vrf = vrf_lookup_by_name(vrfname); + if (vrf == NULL) { + vty_out(vty, "%% Vrf is not exist: %s\n", vrfname); + return NULL; + } + tmpName = vrf->name; + } + + if (bfd_configure_peer(&bpc, mhop, &psa, lsap, ifname, tmpName, errormsg, + sizeof(errormsg)) != 0) { vty_out(vty, "%% Invalid peer configuration: %s\n", errormsg); return NULL; @@ -631,6 +920,49 @@ void _display_rtt(uint32_t *min, uint32_t *avg, uint32_t *max, /* * Show commands. */ +DEFPY(bfd_show_by_bfdname, bfd_show_by_bfdname_cmd, + "show bfd [vrf NAME$vrf_name] bfd-name BFDNAME$bfdname [json]", + SHOW_STR + "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR + "Specify bfd session name\n" + "bfd session name\n" + JSON_STR) +{ + _display_bfd_by_bfdname(vty, vrf_name, bfdname, use_json(argc, argv)); + + return CMD_SUCCESS; +} + +DEFPY(bfd_show_counters_by_bfdname, bfd_show_counters_by_bfdname_cmd, + "show bfd [vrf NAME$vrf_name] bfd-name BFDNAME$bfdname counters [json]", + SHOW_STR + "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR + "Specify bfd session name\n" + "bfd session name\n" + "Show BFD peer counters information\n" + JSON_STR) +{ + _display_bfd_counters_by_bfdname(vty, vrf_name, bfdname, use_json(argc, argv)); + + return CMD_SUCCESS; +} + +DEFPY(bfd_clear_counters_by_bfdname, bfd_clear_counters_by_bfdname_cmd, + "clear bfd [vrf NAME$vrfname] bfd-name BFDNAME$bfdname counters", + CLEAR_STR + "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR + "Specify bfd session name\n" + "bfd session name\n" + "clear BFD peer counters information\n") +{ + _clear_bfd_counters_by_bfdname(vrfname, bfdname); + + return CMD_SUCCESS; +} + DEFPY(bfd_show_peers, bfd_show_peers_cmd, "show bfd [vrf NAME] peers [json]", SHOW_STR "Bidirection Forwarding Detection\n" @@ -971,6 +1303,22 @@ struct cmd_node bfd_peer_node = { .prompt = "%s(config-bfd-peer)# ", }; +static void _sbfd_reflector_write_config(struct hash_bucket *hb, void *arg) +{ + struct sbfd_reflector *sr = hb->data; + char buf[INET6_ADDRSTRLEN]; + struct vty *vty; + + vty = (struct vty *)arg; + inet_ntop(AF_INET6, &sr->local, buf, sizeof(buf)); + vty_out(vty, " sbfd reflector source-address %s discriminator %u\n", buf, sr->discr); +} + +static void sbfd_reflector_write_config(struct vty *vty) +{ + sbfd_discr_iterate(_sbfd_reflector_write_config, vty); +} + static int bfdd_write_config(struct vty *vty) { struct lyd_node *dnode; @@ -1002,6 +1350,9 @@ static int bfdd_write_config(struct vty *vty) written = 1; } + /*sbfd config*/ + sbfd_reflector_write_config(vty); + return written; } @@ -1011,6 +1362,9 @@ void bfdd_vty_init(void) install_element(ENABLE_NODE, &bfd_show_peer_counters_cmd); install_element(ENABLE_NODE, &bfd_clear_peer_counters_cmd); install_element(ENABLE_NODE, &bfd_show_peers_cmd); + install_element(ENABLE_NODE, &bfd_show_by_bfdname_cmd); + install_element(ENABLE_NODE, &bfd_show_counters_by_bfdname_cmd); + install_element(ENABLE_NODE, &bfd_clear_counters_by_bfdname_cmd); install_element(ENABLE_NODE, &bfd_show_peer_cmd); install_element(ENABLE_NODE, &bfd_show_peers_brief_cmd); install_element(ENABLE_NODE, &show_bfd_distributed_cmd); diff --git a/bfdd/event.c b/bfdd/event.c index e797e71f05..e5f43b6cc6 100644 --- a/bfdd/event.c +++ b/bfdd/event.c @@ -58,6 +58,73 @@ void bfd_echo_recvtimer_update(struct bfd_session *bs) &bs->echo_recvtimer_ev); } +void sbfd_init_recvtimer_update(struct bfd_session *bs) +{ + struct timeval tv = { .tv_sec = 0, .tv_usec = bs->detect_TO }; + + /* Remove previous schedule if any. */ + sbfd_init_recvtimer_delete(bs); + + /* Don't add event if peer is deactivated. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || bs->sock == -1) + return; + + tv_normalize(&tv); +#ifdef BFD_EVENT_DEBUG + log_debug("%s: sec = %ld, usec = %ld", __func__, tv.tv_sec, tv.tv_usec); +#endif /* BFD_EVENT_DEBUG */ + + event_add_timer_tv(master, sbfd_init_recvtimer_cb, bs, &tv, &bs->recvtimer_ev); +} + +void sbfd_echo_recvtimer_update(struct bfd_session *bs) +{ + struct timeval tv = { .tv_sec = 0, .tv_usec = bs->echo_detect_TO }; + + /* Remove previous schedule if any. */ + sbfd_echo_recvtimer_delete(bs); + + /* Don't add event if peer is deactivated. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || bs->sock == -1) + return; + + tv_normalize(&tv); + + event_add_timer_tv(master, sbfd_echo_recvtimer_cb, bs, &tv, &bs->echo_recvtimer_ev); +} + +void sbfd_init_xmttimer_update(struct bfd_session *bs, uint64_t jitter) +{ + struct timeval tv = { .tv_sec = 0, .tv_usec = jitter }; + + /* Remove previous schedule if any. */ + sbfd_init_xmttimer_delete(bs); + + /* Don't add event if peer is deactivated. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || bs->sock == -1) + return; + + tv_normalize(&tv); + + event_add_timer_tv(master, sbfd_init_xmt_cb, bs, &tv, &bs->xmttimer_ev); +} + +void sbfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter) +{ + struct timeval tv = { .tv_sec = 0, .tv_usec = jitter }; + + /* Remove previous schedule if any. */ + sbfd_echo_xmttimer_delete(bs); + + /* Don't add event if peer is deactivated. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || bs->sock == -1) + return; + + tv_normalize(&tv); + + event_add_timer_tv(master, sbfd_echo_xmt_cb, bs, &tv, &bs->echo_xmttimer_ev); +} + void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter) { struct timeval tv = {.tv_sec = 0, .tv_usec = jitter}; @@ -112,3 +179,23 @@ void bfd_echo_xmttimer_delete(struct bfd_session *bs) { EVENT_OFF(bs->echo_xmttimer_ev); } + +void sbfd_init_recvtimer_delete(struct bfd_session *bs) +{ + EVENT_OFF(bs->recvtimer_ev); +} + +void sbfd_echo_recvtimer_delete(struct bfd_session *bs) +{ + EVENT_OFF(bs->echo_recvtimer_ev); +} + +void sbfd_init_xmttimer_delete(struct bfd_session *bs) +{ + EVENT_OFF(bs->xmttimer_ev); +} + +void sbfd_echo_xmttimer_delete(struct bfd_session *bs) +{ + EVENT_OFF(bs->echo_xmttimer_ev); +} diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c index f6ebefb7be..74f2d39878 100644 --- a/bfdd/ptm_adapter.c +++ b/bfdd/ptm_adapter.c @@ -70,10 +70,10 @@ static void bfdd_client_deregister(struct stream *msg); PRINTFRR(2, 3) static void debug_printbpc(const struct bfd_peer_cfg *bpc, const char *fmt, ...) { - char timers[3][128] = {}; + char timers[3][160] = {}; char minttl_str[32] = {}; - char addr[3][128] = {}; - char profile[128] = {}; + char addr[3][160] = {}; + char profile[160] = {}; char cbit_str[32]; char msgbuf[512]; va_list vl; @@ -134,7 +134,9 @@ static void _ptm_bfd_session_del(struct bfd_session *bs, uint8_t diag) /* Change state and notify peer. */ bs->ses_state = PTM_BFD_DOWN; bs->local_diag = diag; - ptm_bfd_snd(bs, 0); + + if (bs->bfd_mode == BFD_MODE_TYPE_BFD) + ptm_bfd_snd(bs, 0); /* Session reached refcount == 0, lets delete it. */ if (bs->refcount == 0) { @@ -200,6 +202,8 @@ int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state) * - 16 bytes: ipv6 * - c: prefix length * - c: cbit + * - c: bfd name len + * - Xbytes: bfd name * * Commands: ZEBRA_BFD_DEST_REPLAY * @@ -238,9 +242,12 @@ int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state) case PTM_BFD_DOWN: case PTM_BFD_INIT: - stream_putl(msg, BFD_STATUS_DOWN); - break; + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) + stream_putl(msg, BFD_STATUS_ADMIN_DOWN); + else + stream_putl(msg, BFD_STATUS_DOWN); + break; default: stream_putl(msg, BFD_STATUS_UNKNOWN); break; @@ -251,6 +258,9 @@ int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state) stream_putc(msg, bs->remote_cbit); + stream_putc(msg, strlen(bs->bfd_name)); + stream_put(msg, bs->bfd_name, strlen(bs->bfd_name)); + /* Write packet size. */ stream_putw_at(msg, 0, stream_get_endp(msg)); diff --git a/bgpd/bgp_addpath.c b/bgpd/bgp_addpath.c index aada6e555f..030db4b28e 100644 --- a/bgpd/bgp_addpath.c +++ b/bgpd/bgp_addpath.c @@ -10,8 +10,6 @@ #include "bgp_addpath.h" #include "bgp_route.h" -#include "bgp_open.h" -#include "bgp_packet.h" static const struct bgp_addpath_strategy_names strat_names[BGP_ADDPATH_MAX] = { { @@ -361,30 +359,6 @@ void bgp_addpath_type_changed(struct bgp *bgp) } } -int bgp_addpath_capability_action(enum bgp_addpath_strat addpath_type, uint16_t paths) -{ - int action = CAPABILITY_ACTION_UNSET; - - switch (addpath_type) { - case BGP_ADDPATH_ALL: - case BGP_ADDPATH_BEST_PER_AS: - action = CAPABILITY_ACTION_SET; - break; - case BGP_ADDPATH_BEST_SELECTED: - if (paths) - action = CAPABILITY_ACTION_SET; - else - action = CAPABILITY_ACTION_UNSET; - break; - case BGP_ADDPATH_NONE: - case BGP_ADDPATH_MAX: - action = CAPABILITY_ACTION_UNSET; - break; - } - - return action; -} - /* * Change the addpath type assigned to a peer, or peer group. In addition to * adjusting the counts, peer sessions will be reset as needed to make the @@ -398,7 +372,6 @@ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi, struct listnode *node, *nnode; struct peer *tmp_peer; struct peer_group *group; - int action = bgp_addpath_capability_action(addpath_type, paths); if (safi == SAFI_LABELED_UNICAST) safi = SAFI_UNICAST; @@ -456,12 +429,9 @@ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi, } } } else { - if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) && - !CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV)) - peer_change_action(peer, afi, safi, peer_change_reset); + peer_change_action(peer, afi, safi, peer_change_reset); } - bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action); } /* diff --git a/bgpd/bgp_addpath.h b/bgpd/bgp_addpath.h index f1ff98ea7a..c136671ea4 100644 --- a/bgpd/bgp_addpath.h +++ b/bgpd/bgp_addpath.h @@ -15,11 +15,7 @@ #include "bgpd/bgp_table.h" #include "lib/json.h" -struct bgp_addpath_capability { - uint16_t afi; - uint8_t safi; - uint8_t flags; -}; +#define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1 struct bgp_paths_limit_capability { uint16_t afi; @@ -27,8 +23,6 @@ struct bgp_paths_limit_capability { uint16_t paths_limit; } __attribute__((packed)); -#define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1 - void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d); bool bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi, @@ -68,5 +62,4 @@ void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_dest *dest, afi_t afi, safi_t safi); void bgp_addpath_type_changed(struct bgp *bgp); -extern int bgp_addpath_capability_action(enum bgp_addpath_strat addpath_type, uint16_t paths); #endif diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index 78759ae2b6..5437b67f34 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -114,6 +114,10 @@ void bgp_peer_config_apply(struct peer *p, struct peer_group *pg) */ gconfig = pg->conf; + if (CHECK_FLAG(gconfig->flags, PEER_FLAG_UPDATE_SOURCE) || + CHECK_FLAG(p->flags_override, PEER_FLAG_UPDATE_SOURCE)) + bgp_peer_bfd_update_source(p); + /* * If using default control plane independent configuration, * then prefer group's (e.g. it means it wasn't manually configured). diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index 21f9212556..e458e5e5ac 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -2451,7 +2451,7 @@ static void bmp_send_all_bgp(struct peer *peer, bool down) if (!bmpbgp) continue; frr_each (bmp_targets, &bmpbgp->targets, bt) { - if (bgp_vrf != peer->bgp && !bmp_imported_bgp_find(bt, peer->bgp->name)) + if (bgp_vrf == peer->bgp || !bmp_imported_bgp_find(bt, peer->bgp->name)) continue; bmp_send_bt(bt, s); } diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index e794ccb308..2c6ae65f85 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -1337,6 +1337,31 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) snprintf(encbuf, sizeof(encbuf), "DF: (alg: %u, pref: %u)", alg, pref); + } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_LAYER2_ATTR) { + uint16_t flags, l2mtu; + + ++pnt; + memcpy(&flags, pnt, 2); + ++pnt; + ++pnt; + + memcpy(&l2mtu, pnt, 2); + + snprintf(encbuf, sizeof(encbuf), + "L2: P flag:%c, B Flag %c, C word %c, MTU %d", + CHECK_FLAG(flags, + ECOMMUNITY_EVPN_SUBTYPE_LAYER2_ATTR_PRIMARY_PE_FLAG) + ? 'Y' + : 'N', + CHECK_FLAG(flags, + ECOMMUNITY_EVPN_SUBTYPE_LAYER2_ATTR_BACKUP_PE_FLAG) + ? 'Y' + : 'N', + CHECK_FLAG(flags, + ECOMMUNITY_EVPN_SUBTYPE_LAYER2_ATTR_CONTROL_WORD_FLAG) + ? 'Y' + : 'N', + l2mtu); } else unk_ecom = true; } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) { diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index af9d481c19..0e68b15807 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -68,12 +68,18 @@ #define ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL 0x01 #define ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT 0x02 #define ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC 0x03 +#define ECOMMUNITY_EVPN_SUBTYPE_LAYER2_ATTR 0x04 #define ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION 0x06 #define ECOMMUNITY_EVPN_SUBTYPE_DEF_GW 0x0d #define ECOMMUNITY_EVPN_SUBTYPE_ND 0x08 #define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY 0x01 +/* Layer2 Attributes: RFC8214 */ +#define ECOMMUNITY_EVPN_SUBTYPE_LAYER2_ATTR_PRIMARY_PE_FLAG 0x01 +#define ECOMMUNITY_EVPN_SUBTYPE_LAYER2_ATTR_BACKUP_PE_FLAG 0x02 +#define ECOMMUNITY_EVPN_SUBTYPE_LAYER2_ATTR_CONTROL_WORD_FLAG 0x04 + /* DF alg bits - only lower 5 bits are applicable */ #define ECOMMUNITY_EVPN_SUBTYPE_DF_ALG_BITS 0x1f diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index ad3625242e..b9861acad2 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -1201,6 +1201,7 @@ int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi, mpls_label_t label; struct in_addr vtep_ip; struct prefix_evpn p; + uint8_t num_labels = 0; if (psize != BGP_EVPN_TYPE1_PSIZE) { flog_err(EC_BGP_EVPN_ROUTE_INVALID, @@ -1225,6 +1226,7 @@ int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi, pfx += EVPN_ETH_TAG_BYTES; memcpy(&label, pfx, BGP_LABEL_BYTES); + num_labels++; /* EAD route prefix doesn't include the nexthop in the global * table @@ -1233,12 +1235,11 @@ int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi, build_evpn_type1_prefix(&p, eth_tag, &esi, vtep_ip); /* Process the route. */ if (attr) { - bgp_update(peer, (struct prefix *)&p, addpath_id, attr, afi, - safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, - 0, 0, NULL); + bgp_update(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP, + BGP_ROUTE_NORMAL, &prd, &label, num_labels, 0, NULL); } else { - bgp_withdraw(peer, (struct prefix *)&p, addpath_id, afi, safi, - ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0); + bgp_withdraw(peer, (struct prefix *)&p, addpath_id, afi, safi, ZEBRA_ROUTE_BGP, + BGP_ROUTE_NORMAL, &prd, &label, num_labels); } return 0; } @@ -4737,7 +4738,7 @@ bgp_evpn_path_nh_info_new(struct bgp_path_info *pi) struct bgp_path_mh_info *mh_info; struct bgp_path_evpn_nh_info *nh_info; - e = bgp_path_info_extra_get(pi); + e = bgp_evpn_path_info_extra_get(pi); /* If mh_info doesn't exist allocate it */ mh_info = e->evpn->mh_info; diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 0e3ed9f0d1..c7a4c6928a 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -525,8 +525,9 @@ static void bgp_holdtime_timer(struct event *thread) * for systems where we are heavily loaded for one * reason or another. */ - inq_count = atomic_load_explicit(&connection->ibuf->count, - memory_order_relaxed); + frr_with_mutex (&connection->io_mtx) { + inq_count = atomic_load_explicit(&connection->ibuf->count, memory_order_relaxed); + } if (inq_count) BGP_TIMER_ON(connection->t_holdtime, bgp_holdtime_timer, peer->v_holdtime); @@ -607,6 +608,7 @@ const char *const peer_down_str[] = { "Admin. shutdown (RTT)", "Suppress Fib Turned On or Off", "Password config change", + "Router ID is missing", }; static void bgp_graceful_restart_timer_off(struct peer_connection *connection, @@ -2163,6 +2165,9 @@ bgp_establish(struct peer_connection *connection) peer->established++; bgp_fsm_change_status(connection, Established); + if (peer->last_reset == PEER_DOWN_WAITING_OPEN) + peer->last_reset = 0; + /* bgp log-neighbor-changes of neighbor Up */ if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) { struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id); diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 9ca20c949a..9dbef791b0 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -327,7 +327,7 @@ static int bgp_vrf_disable(struct vrf *vrf) if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id); - bgp = bgp_lookup_by_name(vrf->name); + bgp = bgp_lookup_by_name_filter(vrf->name, false); if (bgp) { vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP); diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index af5d815d30..3df4aa286e 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -571,7 +571,7 @@ static void bgp_accept(struct event *thread) /* Do not try to reconnect if the peer reached maximum * prefixes, restart timer is still running or the peer - * is shutdown. + * is shutdown, or BGP identifier is not set (0.0.0.0). */ if (BGP_PEER_START_SUPPRESSED(peer1)) { if (bgp_debug_neighbor_events(peer1)) { @@ -588,6 +588,14 @@ static void bgp_accept(struct event *thread) return; } + if (peer1->bgp->router_id.s_addr == INADDR_ANY) { + zlog_warn("[Event] Incoming BGP connection rejected from %s due missing BGP identifier, set it with `bgp router-id`", + peer1->host); + peer1->last_reset = PEER_DOWN_ROUTER_ID_ZERO; + close(bgp_sock); + return; + } + if (bgp_debug_neighbor_events(peer1)) zlog_debug("[Event] connection from %s fd %d, active peer status %d fd %d", inet_sutop(&su, buf), bgp_sock, connection1->status, @@ -776,6 +784,13 @@ enum connect_result bgp_connect(struct peer_connection *connection) assert(!CHECK_FLAG(connection->thread_flags, PEER_THREAD_READS_ON)); ifindex_t ifindex = 0; + if (peer->bgp->router_id.s_addr == INADDR_ANY) { + peer->last_reset = PEER_DOWN_ROUTER_ID_ZERO; + zlog_warn("%s: BGP identifier is missing for peer %s, set it with `bgp router-id`", + __func__, peer->host); + return connect_error; + } + if (peer->conf_if && BGP_CONNECTION_SU_UNSPEC(connection)) { if (bgp_debug_neighbor_events(peer)) zlog_debug("Peer address not learnt: Returning from connect"); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 37c4021890..c8e15372b6 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -1284,6 +1284,25 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) } LIST_FOREACH (path, &(bnc->paths), nh_thread) { + /* + * Currently when a peer goes down, bgp immediately + * sees this via the interface events( if it is directly + * connected). And in this case it takes and puts on + * a special peer queue all path info's associated with + * but these items are not yet processed typically when + * the nexthop is being handled here. Thus we end + * up in a situation where the process Queue for BGP + * is being asked to look at the same path info multiple + * times. Let's just cut to the chase here and if + * the bnc has a peer associated with it and the path info + * being looked at uses that peer and the peer is no + * longer established we know the path_info is being + * handled elsewhere and we do not need to process + * it here at all since the pathinfo is going away + */ + if (peer && path->peer == peer && !peer_established(peer->connection)) + continue; + if (path->type == ZEBRA_ROUTE_BGP && (path->sub_type == BGP_ROUTE_NORMAL || path->sub_type == BGP_ROUTE_STATIC || diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index ca0d414dbb..3e90d7881c 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1218,7 +1218,6 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, uint16_t len; uint32_t gr_restart_time; uint8_t addpath_afi_safi_count = 0; - bool adv_addpath_tx = false; unsigned long number_of_orfs_p; uint8_t number_of_orfs = 0; const char *capability = lookup_msg(capcode_str, capability_code, @@ -1226,6 +1225,9 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, const char *hostname = cmd_hostname_get(); const char *domainname = cmd_domainname_get(); + if (!peer) + return; + if (!peer_established(peer->connection)) return; @@ -1395,87 +1397,6 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, COND_FLAG(peer->cap, PEER_CAP_LLGR_ADV, action == CAPABILITY_ACTION_SET); break; - case CAPABILITY_CODE_ADDPATH: - FOREACH_AFI_SAFI (afi, safi) { - if (peer->afc[afi][safi]) { - addpath_afi_safi_count++; - - /* Only advertise addpath TX if a feature that - * will use it is - * configured */ - if (peer->addpath_type[afi][safi] != - BGP_ADDPATH_NONE) - adv_addpath_tx = true; - - /* If we have enabled labeled unicast, we MUST check - * against unicast SAFI because addpath IDs are - * allocated under unicast SAFI, the same as the RIB - * is managed in unicast SAFI. - */ - if (safi == SAFI_LABELED_UNICAST) - if (peer->addpath_type[afi][SAFI_UNICAST] != - BGP_ADDPATH_NONE) - adv_addpath_tx = true; - } - } - - stream_putc(s, action); - stream_putc(s, CAPABILITY_CODE_ADDPATH); - stream_putc(s, CAPABILITY_CODE_ADDPATH_LEN * - addpath_afi_safi_count); - - FOREACH_AFI_SAFI (afi, safi) { - if (peer->afc[afi][safi]) { - bool adv_addpath_rx = - !CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_DISABLE_ADDPATH_RX); - uint8_t flags = 0; - - /* Convert AFI, SAFI to values for packet. */ - bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, - &pkt_safi); - - stream_putw(s, pkt_afi); - stream_putc(s, pkt_safi); - - if (adv_addpath_rx) { - SET_FLAG(flags, BGP_ADDPATH_RX); - SET_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ADDPATH_AF_RX_ADV); - } else { - UNSET_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ADDPATH_AF_RX_ADV); - } - - if (adv_addpath_tx) { - SET_FLAG(flags, BGP_ADDPATH_TX); - SET_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ADDPATH_AF_TX_ADV); - if (safi == SAFI_LABELED_UNICAST) - SET_FLAG(peer->af_cap[afi] - [SAFI_UNICAST], - PEER_CAP_ADDPATH_AF_TX_ADV); - } else { - UNSET_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ADDPATH_AF_TX_ADV); - } - - stream_putc(s, flags); - } - } - - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", - peer, - action == CAPABILITY_ACTION_SET - ? "Advertising" - : "Removing", - capability, iana_afi2str(pkt_afi), - iana_safi2str(pkt_safi)); - - COND_FLAG(peer->cap, PEER_CAP_ADDPATH_ADV, - action == CAPABILITY_ACTION_SET); - break; case CAPABILITY_CODE_PATHS_LIMIT: FOREACH_AFI_SAFI (afi, safi) { if (!peer->afc[afi][safi]) @@ -1631,6 +1552,7 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, case CAPABILITY_CODE_REFRESH: case CAPABILITY_CODE_AS4: case CAPABILITY_CODE_DYNAMIC: + case CAPABILITY_CODE_ADDPATH: case CAPABILITY_CODE_ENHANCED_RR: case CAPABILITY_CODE_EXT_MESSAGE: break; @@ -3186,102 +3108,6 @@ static int bgp_route_refresh_receive(struct peer_connection *connection, return BGP_PACKET_NOOP; } -static void bgp_dynamic_capability_addpath(uint8_t *pnt, int action, - struct capability_header *hdr, - struct peer *peer) -{ - uint8_t *data = pnt + 3; - uint8_t *end = data + hdr->length; - size_t len = end - data; - afi_t afi; - safi_t safi; - - if (action == CAPABILITY_ACTION_SET) { - if (len % CAPABILITY_CODE_ADDPATH_LEN) { - flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, - "Add Path: Received invalid length %zu, non-multiple of 4", - len); - return; - } - - SET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV); - - while (data + CAPABILITY_CODE_ADDPATH_LEN <= end) { - afi_t afi; - safi_t safi; - iana_afi_t pkt_afi; - iana_safi_t pkt_safi; - struct bgp_addpath_capability bac; - - memcpy(&bac, data, sizeof(bac)); - pkt_afi = ntohs(bac.afi); - pkt_safi = safi_int2iana(bac.safi); - - /* If any other value (other than 1-3) is received, - * then the capability SHOULD be treated as not - * understood and ignored. - */ - if (!bac.flags || bac.flags > 3) { - flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, - "Add Path: Received invalid send/receive value %u in Add Path capability", - bac.flags); - goto ignore; - } - - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s%s%s", - peer->host, lookup_msg(capcode_str, hdr->code, NULL), - iana_afi2str(pkt_afi), iana_safi2str(pkt_safi), - CHECK_FLAG(bac.flags, BGP_ADDPATH_RX) ? ", receive" : "", - CHECK_FLAG(bac.flags, BGP_ADDPATH_TX) ? ", transmit" - : ""); - - if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, - &safi)) { - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s Addr-family %s/%s(afi/safi) not supported. Ignore the Addpath Attribute for this AFI/SAFI", - peer->host, - iana_afi2str(pkt_afi), - iana_safi2str(pkt_safi)); - goto ignore; - } else if (!peer->afc[afi][safi]) { - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s Addr-family %s/%s(afi/safi) not enabled. Ignore the AddPath capability for this AFI/SAFI", - peer->host, - iana_afi2str(pkt_afi), - iana_safi2str(pkt_safi)); - goto ignore; - } - - if (CHECK_FLAG(bac.flags, BGP_ADDPATH_RX)) - SET_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ADDPATH_AF_RX_RCV); - else - UNSET_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ADDPATH_AF_RX_RCV); - - if (CHECK_FLAG(bac.flags, BGP_ADDPATH_TX)) - SET_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ADDPATH_AF_TX_RCV); - else - UNSET_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ADDPATH_AF_TX_RCV); - -ignore: - data += CAPABILITY_CODE_ADDPATH_LEN; - } - } else { - FOREACH_AFI_SAFI (afi, safi) { - UNSET_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ADDPATH_AF_RX_RCV); - UNSET_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ADDPATH_AF_TX_RCV); - } - - UNSET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV); - } -} - static void bgp_dynamic_capability_paths_limit(uint8_t *pnt, int action, struct capability_header *hdr, struct peer *peer) @@ -4043,9 +3869,6 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, case CAPABILITY_CODE_LLGR: bgp_dynamic_capability_llgr(pnt, action, hdr, peer); break; - case CAPABILITY_CODE_ADDPATH: - bgp_dynamic_capability_addpath(pnt, action, hdr, peer); - break; case CAPABILITY_CODE_PATHS_LIMIT: bgp_dynamic_capability_paths_limit(pnt, action, hdr, peer); @@ -4059,6 +3882,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, case CAPABILITY_CODE_REFRESH: case CAPABILITY_CODE_AS4: case CAPABILITY_CODE_DYNAMIC: + case CAPABILITY_CODE_ADDPATH: case CAPABILITY_CODE_ENHANCED_RR: case CAPABILITY_CODE_EXT_MESSAGE: break; diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 2d61c0f00a..b85a8e2254 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -2624,7 +2624,6 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, struct bgp_pbr_entry_main *api, bool add) { - struct nexthop nh; int i = 0; int continue_loop = 1; float rate = 0; @@ -2639,7 +2638,6 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, struct bgp_pbr_val_mask bpvm; memset(&range, 0, sizeof(range)); - memset(&nh, 0, sizeof(nh)); memset(&bpf, 0, sizeof(bpf)); memset(&bpof, 0, sizeof(bpof)); if (CHECK_FLAG(api->match_bitmask, PREFIX_SRC_PRESENT) || @@ -2652,8 +2650,6 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, dst = &api->dst_prefix; if (api->type == BGP_PBR_IPRULE) bpf.type = api->type; - memset(&nh, 0, sizeof(nh)); - nh.vrf_id = VRF_UNKNOWN; if (api->match_protocol_num) { proto = (uint8_t)api->protocol[0].value; if (api->afi == AF_INET6 && proto == IPPROTO_ICMPV6) @@ -2778,8 +2774,10 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, case ACTION_TRAFFICRATE: /* drop packet */ if (api->actions[i].u.r.rate == 0) { - nh.vrf_id = api->vrf_id; - nh.type = NEXTHOP_TYPE_BLACKHOLE; + struct nexthop nh = { + .vrf_id = api->vrf_id, + .type = NEXTHOP_TYPE_BLACKHOLE, + }; bgp_pbr_policyroute_add_to_zebra( bgp, path, &bpf, &bpof, &nh, &rate); } else { @@ -2802,18 +2800,15 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, /* terminate action: run other filters */ break; - case ACTION_REDIRECT_IP: - nh.vrf_id = api->vrf_id; + case ACTION_REDIRECT_IP: { + struct nexthop nh = { .vrf_id = api->vrf_id }; + if (api->afi == AFI_IP) { nh.type = NEXTHOP_TYPE_IPV4; - nh.gate.ipv4.s_addr = - api->actions[i].u.zr. - redirect_ip_v4.s_addr; + nh.gate.ipv4 = api->actions[i].u.zr.redirect_ip_v4; } else { nh.type = NEXTHOP_TYPE_IPV6; - memcpy(&nh.gate.ipv6, - &api->actions[i].u.zr.redirect_ip_v6, - sizeof(struct in6_addr)); + nh.gate.ipv6 = api->actions[i].u.zr.redirect_ip_v6; } bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof, &nh, &rate); @@ -2822,7 +2817,10 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, */ continue_loop = 0; break; - case ACTION_REDIRECT: + } + case ACTION_REDIRECT: { + struct nexthop nh = {}; + if (api->afi == AFI_IP) nh.type = NEXTHOP_TYPE_IPV4; else @@ -2832,6 +2830,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, &nh, &rate); continue_loop = 0; break; + } case ACTION_MARKING: if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 09420f2f6b..a1a5068a7f 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -328,6 +328,19 @@ struct bgp_path_info_extra *bgp_path_info_extra_get(struct bgp_path_info *pi) return pi->extra; } +/* Get bgp_path_info extra along with evpn information for the given bgp_path_info. + * This is used for EVPN imported routes like Type-5. + */ +struct bgp_path_info_extra *bgp_evpn_path_info_extra_get(struct bgp_path_info *pi) +{ + if (!pi->extra) + pi->extra = bgp_path_info_extra_new(); + if (!pi->extra->evpn) + pi->extra->evpn = XCALLOC(MTYPE_BGP_ROUTE_EXTRA_EVPN, + sizeof(struct bgp_path_info_extra_evpn)); + return pi->extra; +} + bool bgp_path_info_has_valid_label(const struct bgp_path_info *path) { if (!BGP_PATH_INFO_NUM_LABELS(path)) @@ -2846,8 +2859,17 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, * If the extended community is non-transitive, strip it off, * unless it's a locally originated route (static, aggregate, * redistributed, etc.). + * draft-uttaro-idr-bgp-oad says: + * Extended communities which are non-transitive across an AS + * boundary MAY be advertised over an EBGP-OAD session if allowed + * by explicit policy configuration. If allowed, all the members + * of the OAD SHOULD be configured to use the same criteria. + * For example, the Origin Validation State Extended Community, + * defined as non-transitive in [RFC8097], can be advertised to + * peers in the same OAD. */ - if (from->sort == BGP_PEER_EBGP && peer->sort == BGP_PEER_EBGP && + if (from->sort == BGP_PEER_EBGP && from->sub_sort != BGP_PEER_EBGP_OAD && + peer->sort == BGP_PEER_EBGP && peer->sub_sort != BGP_PEER_EBGP_OAD && pi->sub_type == BGP_ROUTE_NORMAL) { struct ecommunity *new_ecomm; struct ecommunity *old_ecomm; @@ -3880,6 +3902,12 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, BGP_PATH_ATTR_CHANGED); UNSET_FLAG(new_select->flags, BGP_PATH_MULTIPATH_CHG); UNSET_FLAG(new_select->flags, BGP_PATH_LINK_BW_CHG); + } else { + /* + * Ensure that on uninstall that the INSTALL_PENDING + * is no longer set + */ + UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING); } /* call bmp hook for loc-rib route update / withdraw after flags were @@ -4109,6 +4137,9 @@ static void process_eoiu_marker(struct bgp_dest *dest) subqueue2str(META_QUEUE_EOIU_MARKER)); bgp_process_main_one(info->bgp, NULL, 0, 0); + + XFREE(MTYPE_BGP_EOIU_MARKER_INFO, info); + XFREE(MTYPE_BGP_NODE, dest); } /* @@ -4299,6 +4330,7 @@ static void eoiu_marker_queue_free(struct meta_queue *mq, struct bgp_dest_queue XFREE(MTYPE_BGP_EOIU_MARKER_INFO, dest->info); STAILQ_REMOVE_HEAD(l, pq); STAILQ_NEXT(dest, pq) = NULL; /* complete unlink */ + XFREE(MTYPE_BGP_NODE, dest); mq->size--; } } @@ -4918,6 +4950,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bool force_evpn_import = false; safi_t orig_safi = safi; struct bgp_labels bgp_labels = {}; + struct bgp_route_evpn *p_evpn = evpn; uint8_t i; if (frrtrace_enabled(frr_bgp, process_update)) { @@ -4959,11 +4992,9 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, * will not be interned. In which case, it is ok to update the * attr->evpn_overlay, so that, this can be stored in adj_in. */ - if (evpn) { - if (afi == AFI_L2VPN) - bgp_attr_set_evpn_overlay(attr, evpn); - else - evpn_overlay_free(evpn); + if (evpn && afi == AFI_L2VPN) { + bgp_attr_set_evpn_overlay(attr, evpn); + p_evpn = NULL; } bgp_adj_in_set(dest, peer, attr, addpath_id, &bgp_labels); } @@ -5136,11 +5167,9 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, * attr->evpn_overlay with evpn directly. Instead memcpy * evpn to new_atr.evpn_overlay before it is interned. */ - if (soft_reconfig && evpn) { - if (afi == AFI_L2VPN) - bgp_attr_set_evpn_overlay(&new_attr, evpn); - else - evpn_overlay_free(evpn); + if (soft_reconfig && evpn && afi == AFI_L2VPN) { + bgp_attr_set_evpn_overlay(&new_attr, evpn); + p_evpn = NULL; } /* Apply incoming route-map. @@ -5309,7 +5338,8 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_dest_unlock_node(dest); bgp_attr_unintern(&attr_new); - + if (p_evpn) + evpn_overlay_free(p_evpn); return; } @@ -5474,6 +5504,8 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, ret = bgp_damp_update(pi, dest, afi, safi); if (ret == BGP_DAMP_SUPPRESSED) { bgp_dest_unlock_node(dest); + if (p_evpn) + evpn_overlay_free(p_evpn); return; } } @@ -5560,6 +5592,8 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, type, sub_type, NULL); } #endif + if (p_evpn) + evpn_overlay_free(p_evpn); return; } // End of implicit withdraw @@ -5654,6 +5688,8 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } #endif + if (p_evpn) + evpn_overlay_free(p_evpn); return; /* This BGP update is filtered. Log the reason then update BGP @@ -5717,6 +5753,8 @@ filtered: } #endif + if (p_evpn) + evpn_overlay_free(p_evpn); return; } @@ -9924,6 +9962,9 @@ void route_vty_out(struct vty *vty, const struct prefix *p, == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) || (path->peer->conf_if)) { json_nexthop_ll = json_object_new_object(); + if (path->peer->conf_if) + json_object_string_add(json_nexthop_ll, "interface", + path->peer->conf_if); json_object_string_addf( json_nexthop_ll, "ip", "%pI6", &attr->mp_nexthop_local); @@ -10940,6 +10981,12 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, else vty_out(vty, ", (stale)"); } + if (bgp_path_suppressed(path)) { + if (json_paths) + json_object_boolean_true_add(json_path, "suppressed"); + else + vty_out(vty, ", (suppressed)"); + } if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR))) { if (json_paths) { @@ -15230,7 +15277,7 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi, if (type == bgp_show_adj_route_advertised || type == bgp_show_adj_route_received) { if (first) { - vty_out(vty, "\"%s\":", rd_str); + vty_out(vty, "{\"%s\":", rd_str); first = false; } else { vty_out(vty, ",\"%s\":", rd_str); @@ -15244,6 +15291,8 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi, output_count += output_count_per_rd; filtered_count += filtered_count_per_rd; } + if (first == false && json_routes) + vty_out(vty, "}"); } else { show_adj_route(vty, peer, table, afi, safi, type, rmap_name, json, json_ar, show_flags, &header1, &header2, diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 7f4a3b918c..c4cbbee0c7 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -801,6 +801,7 @@ extern void bgp_path_info_delete(struct bgp_dest *dest, struct bgp_path_info *pi); extern struct bgp_path_info_extra * bgp_path_info_extra_get(struct bgp_path_info *path); +extern struct bgp_path_info_extra *bgp_evpn_path_info_extra_get(struct bgp_path_info *path); extern bool bgp_path_info_has_valid_label(const struct bgp_path_info *path); extern void bgp_path_info_set_flag(struct bgp_dest *dest, struct bgp_path_info *path, uint32_t flag); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index d8d1fa5ddc..fa8701dc50 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1303,6 +1303,61 @@ static const struct route_map_rule_cmd route_match_evpn_rd_cmd = { route_match_rd_free }; +/* `match community-limit' */ + +/* Match function should return : + * - RMAP_MATCH if the bgp update community list count + * is less or equal to the configured limit. + * - RMAP_NOMATCH if the community list count is greater than the + * configured limit. + */ +static enum route_map_cmd_result_t +route_match_community_limit(void *rule, const struct prefix *prefix, void *object) +{ + struct bgp_path_info *path = NULL; + struct community *picomm = NULL; + uint16_t count = 0; + uint16_t *limit_rule = rule; + + path = (struct bgp_path_info *)object; + + picomm = bgp_attr_get_community(path->attr); + if (picomm) + count = picomm->size; + + if (count <= *limit_rule) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +/* Route map `community-limit' match statement. */ +static void *route_match_community_limit_compile(const char *arg) +{ + uint16_t *limit = NULL; + char *end = NULL; + + limit = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint16_t)); + *limit = strtoul(arg, &end, 10); + if (*end != '\0') { + XFREE(MTYPE_ROUTE_MAP_COMPILED, limit); + return NULL; + } + return limit; +} + +/* Free route map's compiled `community-limit' value. */ +static void route_match_community_limit_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for community limit matching. */ +static const struct route_map_rule_cmd route_match_community_limit_cmd = { + "community-limit", route_match_community_limit, + route_match_community_limit_compile, route_match_community_limit_free +}; + static enum route_map_cmd_result_t route_set_evpn_gateway_ip(void *rule, const struct prefix *prefix, void *object) { @@ -5707,6 +5762,25 @@ DEFPY_YANG( return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG( + match_community_limit, match_community_limit_cmd, + "[no$no] match community-limit ![(0-65535)$limit]", + NO_STR + MATCH_STR + "Match BGP community limit\n" + "Community limit number\n") +{ + const char *xpath = "./match-condition[condition='frr-bgp-route-map:match-community-limit']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:community-limit", xpath); + + nb_cli_enqueue_change(vty, xpath_value, no ? NB_OP_DESTROY : NB_OP_MODIFY, limit_str); + return nb_cli_apply_changes(vty, NULL); +} + DEFUN_YANG( no_match_community, no_match_community_cmd, "no match community [<(1-99)|(100-500)|COMMUNITY_LIST_NAME> [<exact-match$exact|any$any>]]", @@ -7905,6 +7979,7 @@ void bgp_route_map_init(void) route_map_install_match(&route_match_evpn_vni_cmd); route_map_install_match(&route_match_evpn_route_type_cmd); route_map_install_match(&route_match_evpn_rd_cmd); + route_map_install_match(&route_match_community_limit_cmd); route_map_install_match(&route_match_evpn_default_route_cmd); route_map_install_match(&route_match_vrl_source_vrf_cmd); @@ -7977,6 +8052,7 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &no_match_alias_cmd); install_element(RMAP_NODE, &match_community_cmd); install_element(RMAP_NODE, &no_match_community_cmd); + install_element(RMAP_NODE, &match_community_limit_cmd); install_element(RMAP_NODE, &match_lcommunity_cmd); install_element(RMAP_NODE, &no_match_lcommunity_cmd); install_element(RMAP_NODE, &match_ecommunity_cmd); diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index d8fdb4fbc4..4645593441 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -166,6 +166,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { } }, { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:community-limit", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_community_limit_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_community_limit_destroy, + } + }, + { .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list", .cbs = { .create = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_create, diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index f59686f386..45689242a0 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -72,6 +72,10 @@ int lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_mod int lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_community_limit_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_community_limit_destroy( + struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_create( struct nb_cb_create_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_destroy( diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index 0dca196ed6..223c416dc5 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -1275,6 +1275,57 @@ lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_des } /* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:community-limit + */ +int lib_route_map_entry_match_condition_rmap_match_condition_community_limit_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *limit; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + limit = yang_dnode_get_string(args->dnode, NULL); + + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "community-limit"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "community-limit", limit, + RMAP_EVENT_MATCH_ADDED, args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_match_condition_rmap_match_condition_community_limit_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* * XPath = /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list */ int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_create( diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index 35ddfc34ff..c6b1ff1d2f 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -2037,13 +2037,16 @@ int update_group_adjust_soloness(struct peer *peer, int set) struct peer_group *group; struct listnode *node, *nnode; - peer_flag_set(peer, PEER_FLAG_LONESOUL); - if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { peer_lonesoul_or_not(peer, set); if (peer_established(peer->connection)) bgp_announce_route_all(peer); } else { + if (set) + peer_flag_set(peer, PEER_FLAG_LONESOUL); + else + peer_flag_unset(peer, PEER_FLAG_LONESOUL); + group = peer->group; for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { peer_lonesoul_or_not(peer, set); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 0c5d0b10b0..fd4fd8afd6 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -126,6 +126,12 @@ FRR_CFG_DEFAULT_BOOL(BGP_ENFORCE_FIRST_AS, { .val_bool = false, .match_version = "< 9.1", }, { .val_bool = true }, ); +FRR_CFG_DEFAULT_BOOL(BGP_RR_ALLOW_OUTBOUND_POLICY, + { .val_bool = false }, +); +FRR_CFG_DEFAULT_BOOL(BGP_COMPARE_AIGP, + { .val_bool = false }, +); DEFINE_HOOK(bgp_inst_config_write, (struct bgp *bgp, struct vty *vty), @@ -628,6 +634,10 @@ int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, BGP_FLAG_DYNAMIC_CAPABILITY); if (DFLT_BGP_ENFORCE_FIRST_AS) SET_FLAG((*bgp)->flags, BGP_FLAG_ENFORCE_FIRST_AS); + if (DFLT_BGP_RR_ALLOW_OUTBOUND_POLICY) + SET_FLAG((*bgp)->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY); + if (DFLT_BGP_COMPARE_AIGP) + SET_FLAG((*bgp)->flags, BGP_FLAG_COMPARE_AIGP); ret = BGP_SUCCESS; } @@ -1505,13 +1515,12 @@ DEFUN_NOSH (router_bgp, int idx_asn = 2; int idx_view_vrf = 3; int idx_vrf = 4; - int is_new_bgp = 0; int idx_asnotation = 3; int idx_asnotation_kind = 4; enum asnotation_mode asnotation = ASNOTATION_UNDEFINED; int ret; as_t as; - struct bgp *bgp; + struct bgp *bgp = NULL; const char *name = NULL; enum bgp_instance_type inst_type; @@ -1573,35 +1582,40 @@ DEFUN_NOSH (router_bgp, asnotation = ASNOTATION_PLAIN; } - if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) - is_new_bgp = (bgp_lookup(as, name) == NULL); - - ret = bgp_get_vty(&bgp, &as, name, inst_type, - argv[idx_asn]->arg, asnotation); + ret = bgp_lookup_by_as_name_type(&bgp, &as, argv[idx_asn]->arg, asnotation, name, + inst_type, true); + if (bgp && ret == BGP_INSTANCE_EXISTS) + ret = CMD_SUCCESS; + else if (bgp == NULL && ret == CMD_SUCCESS) + /* SUCCESS and bgp is NULL */ + ret = bgp_get_vty(&bgp, &as, name, inst_type, argv[idx_asn]->arg, + asnotation); switch (ret) { case BGP_ERR_AS_MISMATCH: vty_out(vty, "BGP is already running; AS is %s\n", - bgp->as_pretty); + bgp ? bgp->as_pretty : "unknown"); return CMD_WARNING_CONFIG_FAILED; case BGP_ERR_INSTANCE_MISMATCH: vty_out(vty, "BGP instance name and AS number mismatch\n"); - vty_out(vty, - "BGP instance is already running; AS is %s\n", - bgp->as_pretty); + vty_out(vty, "BGP instance is already running; AS is %s\n", + bgp ? bgp->as_pretty : "unknown"); return CMD_WARNING_CONFIG_FAILED; } + if (!bgp) { + vty_out(vty, "BGP instance not found\n"); + return CMD_WARNING_CONFIG_FAILED; + } /* * If we just instantiated the default instance, complete * any pending VRF-VPN leaking that was configured via * earlier "router bgp X vrf FOO" blocks. */ - if (is_new_bgp && inst_type == BGP_INSTANCE_TYPE_DEFAULT) + if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) vpn_leak_postchange_all(); - if (inst_type == BGP_INSTANCE_TYPE_VRF || - IS_BGP_INSTANCE_HIDDEN(bgp)) { + if (inst_type == BGP_INSTANCE_TYPE_VRF || IS_BGP_INSTANCE_HIDDEN(bgp)) { bgp_vpn_leak_export(bgp); UNSET_FLAG(bgp->flags, BGP_FLAG_INSTANCE_HIDDEN); UNSET_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS); @@ -9254,21 +9268,12 @@ DEFUN(neighbor_disable_addpath_rx, struct peer *peer; afi_t afi = bgp_node_afi(vty); safi_t safi = bgp_node_safi(vty); - int ret; - int action; peer = peer_and_group_lookup_vty(vty, peer_str); if (!peer) return CMD_WARNING_CONFIG_FAILED; - action = bgp_addpath_capability_action(peer->addpath_type[afi][safi], 0); - - ret = peer_af_flag_set_vty(vty, peer_str, afi, safi, - PEER_FLAG_DISABLE_ADDPATH_RX); - - bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action); - - return ret; + return peer_af_flag_set_vty(vty, peer_str, afi, safi, PEER_FLAG_DISABLE_ADDPATH_RX); } DEFUN(no_neighbor_disable_addpath_rx, @@ -9283,21 +9288,12 @@ DEFUN(no_neighbor_disable_addpath_rx, struct peer *peer; afi_t afi = bgp_node_afi(vty); safi_t safi = bgp_node_safi(vty); - int ret; - int action; peer = peer_and_group_lookup_vty(vty, peer_str); if (!peer) return CMD_WARNING_CONFIG_FAILED; - action = bgp_addpath_capability_action(peer->addpath_type[afi][safi], 0); - - ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi, - PEER_FLAG_DISABLE_ADDPATH_RX); - - bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action); - - return ret; + return peer_af_flag_unset_vty(vty, peer_str, afi, safi, PEER_FLAG_DISABLE_ADDPATH_RX); } DEFUN (neighbor_addpath_tx_all_paths, @@ -9309,15 +9305,12 @@ DEFUN (neighbor_addpath_tx_all_paths, { int idx_peer = 1; struct peer *peer; - afi_t afi = bgp_node_afi(vty); - safi_t safi = bgp_node_safi(vty); peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); if (!peer) return CMD_WARNING_CONFIG_FAILED; - bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_ALL, 0); - + bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty), BGP_ADDPATH_ALL, 0); return CMD_SUCCESS; } @@ -9337,20 +9330,18 @@ DEFUN (no_neighbor_addpath_tx_all_paths, { int idx_peer = 2; struct peer *peer; - afi_t afi = bgp_node_afi(vty); - safi_t safi = bgp_node_safi(vty); peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); if (!peer) return CMD_WARNING_CONFIG_FAILED; - if (peer->addpath_type[afi][safi] != BGP_ADDPATH_ALL) { + if (peer->addpath_type[bgp_node_afi(vty)][bgp_node_safi(vty)] != BGP_ADDPATH_ALL) { vty_out(vty, "%% Peer not currently configured to transmit all paths."); return CMD_WARNING_CONFIG_FAILED; } - bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE, 0); + bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty), BGP_ADDPATH_NONE, 0); return CMD_SUCCESS; } @@ -10611,7 +10602,7 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, SET_FLAG(bgp_default->flags, BGP_FLAG_INSTANCE_HIDDEN); } - vrf_bgp = bgp_lookup_by_name(import_name); + vrf_bgp = bgp_lookup_by_name_filter(import_name, false); if (!vrf_bgp) { if (strcmp(import_name, VRF_DEFAULT_NAME) == 0) { vrf_bgp = bgp_default; @@ -16777,9 +16768,9 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, json_object_new_string(vname)); json_object_object_add(json, "exportToVrfs", json_export_vrfs); - json_object_string_addf( - json, "routeDistinguisher", "%s", - bgp->vpn_policy[afi].tovpn_rd_pretty); + json_object_string_addf(json, "routeDistinguisher", + BGP_RD_AS_FORMAT(bgp->asnotation), + &bgp->vpn_policy[afi].tovpn_rd); dir = BGP_VPN_POLICY_DIR_TOVPN; if (bgp->vpn_policy[afi].rtlist[dir]) { ecom_str = ecommunity_ecom2str( @@ -17671,12 +17662,6 @@ DEFUN (bgp_redistribute_ipv4_ospf, if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { - if (bgp->vrf_id != VRF_DEFAULT) { - vty_out(vty, - "%% Only default BGP instance can use '%s'\n", - argv[idx_ospf_table]->arg); - return CMD_WARNING_CONFIG_FAILED; - } if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) { protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -17730,12 +17715,6 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap, if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { - if (bgp->vrf_id != VRF_DEFAULT) { - vty_out(vty, - "%% Only default BGP instance can use '%s'\n", - argv[idx_ospf_table]->arg); - return CMD_WARNING_CONFIG_FAILED; - } if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) { protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -17793,12 +17772,6 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric, if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { - if (bgp->vrf_id != VRF_DEFAULT) { - vty_out(vty, - "%% Only default BGP instance can use '%s'\n", - argv[idx_ospf_table]->arg); - return CMD_WARNING_CONFIG_FAILED; - } if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) { protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -17863,12 +17836,6 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap_metric, if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { - if (bgp->vrf_id != VRF_DEFAULT) { - vty_out(vty, - "%% Only default BGP instance can use '%s'\n", - argv[idx_ospf_table]->arg); - return CMD_WARNING_CONFIG_FAILED; - } if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) { protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -17938,13 +17905,7 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric_rmap, if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { - if (bgp->vrf_id != VRF_DEFAULT) { - vty_out(vty, - "%% Only default BGP instance can use '%s'\n", - argv[idx_ospf_table]->arg); - return CMD_WARNING_CONFIG_FAILED; - } else if (strncmp(argv[idx_ospf_table]->arg, "table-direct", - strlen("table-direct")) == 0) { + if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) { protocol = ZEBRA_ROUTE_TABLE_DIRECT; if (instance == RT_TABLE_MAIN || instance == RT_TABLE_LOCAL) { @@ -18007,12 +17968,6 @@ DEFUN (no_bgp_redistribute_ipv4_ospf, if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { - if (bgp->vrf_id != VRF_DEFAULT) { - vty_out(vty, - "%% Only default BGP instance can use '%s'\n", - argv[idx_ospf_table]->arg); - return CMD_WARNING_CONFIG_FAILED; - } if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) { protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -19926,14 +19881,19 @@ int bgp_config_write(struct vty *vty) } } - if (CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { - vty_out(vty, - " bgp route-reflector allow-outbound-policy\n"); - } + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY) != + SAVE_BGP_RR_ALLOW_OUTBOUND_POLICY) + vty_out(vty, " %sbgp route-reflector allow-outbound-policy\n", + CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY) ? "" + : "no "); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID)) vty_out(vty, " bgp bestpath compare-routerid\n"); - if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP)) - vty_out(vty, " bgp bestpath aigp\n"); + + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP) != SAVE_BGP_COMPARE_AIGP) + vty_out(vty, " %sbgp bestpath aigp\n", + CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP) ? "" : "no "); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED) || CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST)) { vty_out(vty, " bgp bestpath med"); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 146630ec6b..1669aabc60 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1674,11 +1674,23 @@ void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi) for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) && - (pi->type == ZEBRA_ROUTE_BGP - && (pi->sub_type == BGP_ROUTE_NORMAL - || pi->sub_type == BGP_ROUTE_IMPORTED))) - bgp_zebra_route_install(dest, pi, bgp, true, - NULL, false); + (pi->type == ZEBRA_ROUTE_BGP && (pi->sub_type == BGP_ROUTE_NORMAL || + pi->sub_type == BGP_ROUTE_IMPORTED))) { + bool is_add = true; + + if (bgp->table_map[afi][safi].name) { + struct attr local_attr = *pi->attr; + struct bgp_path_info local_info = *pi; + + local_info.attr = &local_attr; + + is_add = bgp_table_map_apply(bgp->table_map[afi][safi].map, + bgp_dest_get_prefix(dest), + &local_info); + } + + bgp_zebra_route_install(dest, pi, bgp, is_add, NULL, false); + } } /* Announce routes of any bgp subtype of a table to zebra */ @@ -2042,11 +2054,34 @@ int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type, /* Return if already redistribute flag is set. */ if (instance) { - if (redist_check_instance(&zclient->mi_redist[afi][type], - instance)) - return CMD_WARNING; + if (type == ZEBRA_ROUTE_TABLE_DIRECT) { + /* + * When redistribution type is `table-direct` the + * instance means `table identification`. + * + * `table_id` support 32bit integers, however since + * `instance` is being overloaded to `table_id` it + * will only be possible to use the first 65535 + * entries. + * + * Also the ZAPI must also support `int` + * (see `zebra_redistribute_add`). + */ + struct redist_table_direct table = { + .table_id = instance, + .vrf_id = bgp->vrf_id, + }; + if (redist_lookup_table_direct(&zclient->mi_redist[afi][type], &table) != + NULL) + return CMD_WARNING; + + redist_add_table_direct(&zclient->mi_redist[afi][type], &table); + } else { + if (redist_check_instance(&zclient->mi_redist[afi][type], instance)) + return CMD_WARNING; - redist_add_instance(&zclient->mi_redist[afi][type], instance); + redist_add_instance(&zclient->mi_redist[afi][type], instance); + } } else { if (vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id)) return CMD_WARNING; @@ -2174,10 +2209,22 @@ int bgp_redistribute_unreg(struct bgp *bgp, afi_t afi, int type, /* Return if zebra connection is disabled. */ if (instance) { - if (!redist_check_instance(&zclient->mi_redist[afi][type], - instance)) - return CMD_WARNING; - redist_del_instance(&zclient->mi_redist[afi][type], instance); + if (type == ZEBRA_ROUTE_TABLE_DIRECT) { + struct redist_table_direct table = { + .table_id = instance, + .vrf_id = bgp->vrf_id, + }; + if (redist_lookup_table_direct(&zclient->mi_redist[afi][type], &table) == + NULL) + return CMD_WARNING; + + redist_del_table_direct(&zclient->mi_redist[afi][type], &table); + } else { + if (!redist_check_instance(&zclient->mi_redist[afi][type], instance)) + return CMD_WARNING; + + redist_del_instance(&zclient->mi_redist[afi][type], instance); + } } else { if (!vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id)) return CMD_WARNING; @@ -3375,12 +3422,15 @@ static int bgp_ifp_create(struct interface *ifp) zlog_debug("Rx Intf add VRF %s IF %s", ifp->vrf->name, ifp->name); + /* We don't need to check for vrf->bgp link to add this local MAC + * to the hash table as the tenant VRF might not have the BGP instance. + */ + bgp_mac_add_mac_entry(ifp); + bgp = ifp->vrf->info; if (!bgp) return 0; - bgp_mac_add_mac_entry(ifp); - bgp_update_interface_nbrs(bgp, ifp, ifp); hook_call(bgp_vrf_status_changed, bgp, ifp); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index a2b24b5f18..1034e49bae 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2029,8 +2029,11 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, if (bgp->autoshutdown) peer_flag_set(peer, PEER_FLAG_SHUTDOWN); /* Set up peer's events and timers. */ - else if (!active && peer_active(peer->connection)) + else if (!active && peer_active(peer->connection)) { + if (peer->last_reset == PEER_DOWN_NOAFI_ACTIVATED) + peer->last_reset = 0; bgp_timer_set(peer->connection); + } bgp_peer_gr_flags_update(peer); BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer); @@ -3642,13 +3645,13 @@ struct bgp *bgp_lookup(as_t as, const char *name) } /* Lookup BGP structure by view name. */ -struct bgp *bgp_lookup_by_name(const char *name) +struct bgp *bgp_lookup_by_name_filter(const char *name, bool filter_auto) { struct bgp *bgp; struct listnode *node, *nnode; for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { - if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) + if (filter_auto && CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) continue; if ((bgp->name == NULL && name == NULL) || (bgp->name && name && strcmp(bgp->name, name) == 0)) @@ -3657,6 +3660,11 @@ struct bgp *bgp_lookup_by_name(const char *name) return NULL; } +struct bgp *bgp_lookup_by_name(const char *name) +{ + return bgp_lookup_by_name_filter(name, true); +} + /* Lookup BGP instance based on VRF id. */ /* Note: Only to be used for incoming messages from Zebra. */ struct bgp *bgp_lookup_by_vrf_id(vrf_id_t vrf_id) @@ -3742,10 +3750,9 @@ int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, vrf_id_t old_vrf_id, return bgp_check_main_socket(create, bgp); } -int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as, - const char *as_pretty, +int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as, const char *as_pretty, enum asnotation_mode asnotation, const char *name, - enum bgp_instance_type inst_type) + enum bgp_instance_type inst_type, bool force_config) { struct bgp *bgp; struct peer *peer = NULL; @@ -3754,7 +3761,7 @@ int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as, /* Multiple instance check. */ if (name) - bgp = bgp_lookup_by_name(name); + bgp = bgp_lookup_by_name_filter(name, !force_config); else bgp = bgp_get_default(); @@ -3764,7 +3771,7 @@ int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as, /* Handle AS number change */ if (bgp->as != *as) { if (hidden || CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) { - if (hidden) { + if (force_config == false && hidden) { bgp_create(as, name, inst_type, as_pretty, asnotation, bgp, hidden); @@ -3772,7 +3779,8 @@ int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as, BGP_FLAG_INSTANCE_HIDDEN); } else { bgp->as = *as; - UNSET_FLAG(bgp->vrf_flags, BGP_VRF_AUTO); + if (force_config == false) + UNSET_FLAG(bgp->vrf_flags, BGP_VRF_AUTO); } /* Set all peer's local AS with this ASN */ @@ -3809,8 +3817,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, struct vrf *vrf = NULL; int ret = 0; - ret = bgp_lookup_by_as_name_type(bgp_val, as, as_pretty, asnotation, - name, inst_type); + ret = bgp_lookup_by_as_name_type(bgp_val, as, as_pretty, asnotation, name, inst_type, false); if (ret || *bgp_val) return ret; @@ -3989,6 +3996,7 @@ int bgp_delete(struct bgp *bgp) uint32_t a_ann_cnt = 0, a_l2_cnt = 0, a_l3_cnt = 0; struct bgp *bgp_to_proc = NULL; struct bgp *bgp_to_proc_next = NULL; + struct bgp *bgp_default = bgp_get_default(); assert(bgp); @@ -4042,13 +4050,26 @@ int bgp_delete(struct bgp *bgp) bgp_soft_reconfig_table_task_cancel(bgp, NULL, NULL); /* make sure we withdraw any exported routes */ - vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp_get_default(), - bgp); - vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, bgp_get_default(), - bgp); + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp_default, bgp); + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, bgp_default, bgp); bgp_vpn_leak_unimport(bgp); + /* + * Release SRv6 SIDs, like it's done in `vpn_leak_postchange()` + * and bgp_sid_vpn_export_cmd/af_sid_vpn_export_cmd commands. + */ + bgp->tovpn_sid_index = 0; + UNSET_FLAG(bgp->vrf_flags, BGP_VRF_TOVPN_SID_AUTO); + delete_vrf_tovpn_sid_per_vrf(bgp_default, bgp); + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + bgp->vpn_policy[afi].tovpn_sid_index = 0; + UNSET_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_SID_AUTO); + delete_vrf_tovpn_sid_per_af(bgp_default, bgp, afi); + + vpn_leak_zebra_vrf_sid_withdraw(bgp, afi); + } + bgp_vpn_release_label(bgp, AFI_IP, true); bgp_vpn_release_label(bgp, AFI_IP6, true); @@ -4825,39 +4846,40 @@ static const struct peer_flag_action peer_flag_action_list[] = { {0, 0, 0}}; static const struct peer_flag_action peer_af_flag_action_list[] = { - {PEER_FLAG_SEND_COMMUNITY, 1, peer_change_reset_out}, - {PEER_FLAG_SEND_EXT_COMMUNITY, 1, peer_change_reset_out}, - {PEER_FLAG_SEND_LARGE_COMMUNITY, 1, peer_change_reset_out}, - {PEER_FLAG_NEXTHOP_SELF, 1, peer_change_reset_out}, - {PEER_FLAG_REFLECTOR_CLIENT, 1, peer_change_reset}, - {PEER_FLAG_RSERVER_CLIENT, 1, peer_change_reset}, - {PEER_FLAG_SOFT_RECONFIG, 0, peer_change_reset_in}, - {PEER_FLAG_AS_PATH_UNCHANGED, 1, peer_change_reset_out}, - {PEER_FLAG_NEXTHOP_UNCHANGED, 1, peer_change_reset_out}, - {PEER_FLAG_MED_UNCHANGED, 1, peer_change_reset_out}, - {PEER_FLAG_DEFAULT_ORIGINATE, 0, peer_change_none}, - {PEER_FLAG_REMOVE_PRIVATE_AS, 1, peer_change_reset_out}, - {PEER_FLAG_ALLOWAS_IN, 0, peer_change_reset_in}, - {PEER_FLAG_ALLOWAS_IN_ORIGIN, 0, peer_change_reset_in}, - {PEER_FLAG_ORF_PREFIX_SM, 1, peer_change_reset}, - {PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset}, - {PEER_FLAG_MAX_PREFIX, 0, peer_change_none}, - {PEER_FLAG_MAX_PREFIX_WARNING, 0, peer_change_none}, - {PEER_FLAG_MAX_PREFIX_FORCE, 0, peer_change_none}, - {PEER_FLAG_MAX_PREFIX_OUT, 0, peer_change_none}, - {PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out}, - {PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out}, - {PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out}, - {PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE, 1, peer_change_reset_out}, - {PEER_FLAG_AS_OVERRIDE, 1, peer_change_reset_out}, - {PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE, 1, peer_change_reset_out}, - {PEER_FLAG_WEIGHT, 0, peer_change_reset_in}, - {PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_none}, - {PEER_FLAG_SOO, 0, peer_change_reset}, - {PEER_FLAG_ACCEPT_OWN, 0, peer_change_reset}, - {PEER_FLAG_SEND_EXT_COMMUNITY_RPKI, 1, peer_change_reset_out}, - {PEER_FLAG_ADDPATH_RX_PATHS_LIMIT, 0, peer_change_none}, - {0, 0, 0}}; + { PEER_FLAG_SEND_COMMUNITY, 1, peer_change_reset_out }, + { PEER_FLAG_SEND_EXT_COMMUNITY, 1, peer_change_reset_out }, + { PEER_FLAG_SEND_LARGE_COMMUNITY, 1, peer_change_reset_out }, + { PEER_FLAG_NEXTHOP_SELF, 1, peer_change_reset_out }, + { PEER_FLAG_REFLECTOR_CLIENT, 1, peer_change_reset }, + { PEER_FLAG_RSERVER_CLIENT, 1, peer_change_reset }, + { PEER_FLAG_SOFT_RECONFIG, 0, peer_change_reset_in }, + { PEER_FLAG_AS_PATH_UNCHANGED, 1, peer_change_reset_out }, + { PEER_FLAG_NEXTHOP_UNCHANGED, 1, peer_change_reset_out }, + { PEER_FLAG_MED_UNCHANGED, 1, peer_change_reset_out }, + { PEER_FLAG_DEFAULT_ORIGINATE, 0, peer_change_none }, + { PEER_FLAG_REMOVE_PRIVATE_AS, 1, peer_change_reset_out }, + { PEER_FLAG_ALLOWAS_IN, 0, peer_change_reset_in }, + { PEER_FLAG_ALLOWAS_IN_ORIGIN, 0, peer_change_reset_in }, + { PEER_FLAG_ORF_PREFIX_SM, 1, peer_change_reset }, + { PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset }, + { PEER_FLAG_MAX_PREFIX, 0, peer_change_none }, + { PEER_FLAG_MAX_PREFIX_WARNING, 0, peer_change_none }, + { PEER_FLAG_MAX_PREFIX_FORCE, 0, peer_change_none }, + { PEER_FLAG_MAX_PREFIX_OUT, 0, peer_change_none }, + { PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out }, + { PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out }, + { PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out }, + { PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE, 1, peer_change_reset_out }, + { PEER_FLAG_AS_OVERRIDE, 1, peer_change_reset_out }, + { PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE, 1, peer_change_reset_out }, + { PEER_FLAG_WEIGHT, 0, peer_change_reset_in }, + { PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_reset }, + { PEER_FLAG_SOO, 0, peer_change_reset }, + { PEER_FLAG_ACCEPT_OWN, 0, peer_change_reset }, + { PEER_FLAG_SEND_EXT_COMMUNITY_RPKI, 1, peer_change_reset_out }, + { PEER_FLAG_ADDPATH_RX_PATHS_LIMIT, 0, peer_change_none }, + { 0, 0, 0 } +}; /* Proper action set. */ static int peer_flag_action_set(const struct peer_flag_action *action_list, diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 9ba6219beb..d02c1c924d 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1867,6 +1867,7 @@ struct peer { #define PEER_DOWN_RTT_SHUTDOWN 35U /* Automatically shutdown due to RTT */ #define PEER_DOWN_SUPPRESS_FIB_PENDING 36U /* Suppress fib pending changed */ #define PEER_DOWN_PASSWORD_CHANGE 37U /* neighbor password command */ +#define PEER_DOWN_ROUTER_ID_ZERO 38U /* router-id is 0.0.0.0 */ /* * Remember to update peer_down_str in bgp_fsm.c when you add * a new value to the last_reset reason @@ -2290,6 +2291,7 @@ extern void bgp_zclient_reset(void); extern struct bgp *bgp_get_default(void); extern struct bgp *bgp_lookup(as_t, const char *); extern struct bgp *bgp_lookup_by_name(const char *); +extern struct bgp *bgp_lookup_by_name_filter(const char *name, bool filter_auto); extern struct bgp *bgp_lookup_by_vrf_id(vrf_id_t); extern struct bgp *bgp_get_evpn(void); extern void bgp_set_evpn(struct bgp *bgp); @@ -2864,11 +2866,9 @@ extern struct peer *peer_new(struct bgp *bgp); extern struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, const char *ip_str, bool use_json); -extern int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as, - const char *as_pretty, - enum asnotation_mode asnotation, - const char *name, - enum bgp_instance_type inst_type); +extern int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as, const char *as_pretty, + enum asnotation_mode asnotation, const char *name, + enum bgp_instance_type inst_type, bool force_config); /* Hooks */ DECLARE_HOOK(bgp_vrf_status_changed, (struct bgp *bgp, struct interface *ifp), diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index 99d8bcfce4..d9f63700f0 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -4067,9 +4067,15 @@ static void rfapiProcessPeerDownRt(struct peer *peer, bpi, import_table, afi, -1); import_table->holddown_count[afi] += 1; } - rfapiBiStartWithdrawTimer(import_table, rn, bpi, - afi, safi, - timer_service_func); + if (bm->terminating) { + if (safi == SAFI_MPLS_VPN) + rfapiExpireVpnNow(import_table, rn, bpi, 1); + else + rfapiExpireEncapNow(import_table, rn, bpi); + + } else + rfapiBiStartWithdrawTimer(import_table, rn, bpi, afi, safi, + timer_service_func); } } } diff --git a/configure.ac b/configure.ac index e04c0b6d46..09e2d20c3a 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ([2.69]) -AC_INIT([frr], [10.3-dev], [https://github.com/frrouting/frr/issues]) +AC_INIT([frr], [10.4-dev], [https://github.com/frrouting/frr/issues]) PACKAGE_URL="https://frrouting.org/" AC_SUBST([PACKAGE_URL]) PACKAGE_FULLNAME="FRRouting" diff --git a/doc/developer/mgmtd-dev.rst b/doc/developer/mgmtd-dev.rst index 4c56cadb28..6cbd617f8c 100644 --- a/doc/developer/mgmtd-dev.rst +++ b/doc/developer/mgmtd-dev.rst @@ -160,14 +160,19 @@ Back-End Interface: should be destroyed with a call to `mgmt_be_client_destroy` and to be safe NULL out the global `mgmt_be_client` variable. -#. In ``mgmtd/mgmt_be_adapter.c`` add xpath prefix mappings to a one or both - mapping arrays (``be_client_config_xpaths`` and ``be_client_oper_xpaths``) to - direct ``mgmtd`` to send config and oper-state requests to your daemon. NOTE: - make sure to include library supported xpaths prefixes as well (e.g., +#. In ``mgmtd/mgmt_be_adapter.c`` add xpath prefix mappings to a each of the + mapping arrays (``be_client_config_xpaths``, ``be_client_oper_xpaths``, and + ``be_client_rpc_xpaths``) to direct ``mgmtd`` to send config, oper-state, and + RPC requests to your daemon. + + NOTE: make sure to include library supported xpaths prefixes as well (e.g., "/frr-interface:lib"). A good way to figure these paths out are to look in each of the YANG modules that the daemon uses and include each of their paths in the array. +#. In ``python/xref2vtysh.py`` add ``VTYSH_xxxD`` (for client xxx) to + ``lib/mgmt_be_client.c`` entry in the ``daemon_falgs`` dictionary. + Add YANG and CLI into MGMTD ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/developer/packaging-redhat.rst b/doc/developer/packaging-redhat.rst index 8037873461..277de5d16f 100644 --- a/doc/developer/packaging-redhat.rst +++ b/doc/developer/packaging-redhat.rst @@ -3,7 +3,7 @@ Packaging Red Hat ================= -Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24. +Tested on CentOS 6, CentOS 7, Rocky 8 and Fedora 24. 1. On CentOS 6, refer to :ref:`building-centos6` for details on installing sufficiently up-to-date package versions to enable building FRR. @@ -18,14 +18,14 @@ Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24. yum install rpm-build net-snmp-devel pam-devel libcap-devel - For CentOS 7 and CentOS 8, the package will be built using python3 + For CentOS 7 and Rocky 8, the package will be built using python3 and requires additional python3 packages:: yum install python3-devel python3-sphinx .. note:: - For CentOS 8 you need to install ``platform-python-devel`` package + For Rocky 8 you need to install ``platform-python-devel`` package to provide ``/usr/bin/pathfix.py``:: yum install platform-python-devel @@ -33,7 +33,7 @@ Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24. If ``yum`` is not present on your system, use ``dnf`` instead. - You should enable ``PowerTools`` repo if using CentOS 8 which + You should enable ``PowerTools`` repo if using Rocky 8 which is disabled by default. 4. Checkout FRR:: @@ -88,11 +88,18 @@ Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24. %{!?with_watchfrr: %global with_watchfrr 1 } %{!?with_pathd: %global with_pathd 1 } %{!?with_grpc: %global with_grpc 0 } + %{!?with_rpki: %global with_rpki 1 } + %{!?with_docs: %global with_docs 1 } 8. Build the RPM:: rpmbuild --define "_topdir `pwd`/rpmbuild" -ba rpmbuild/SPECS/frr.spec + To override :file:`rpm/SPECS/frr.spec` defaults on the rpmbuild + commandline with: + + rpmbuild --define 'variable value' + If building with RPKI, then download and install the additional RPKI packages from https://ci1.netdef.org/browse/RPKI-RTRLIB/latestSuccessful/artifact diff --git a/doc/developer/sbfd.rst b/doc/developer/sbfd.rst new file mode 100644 index 0000000000..66a3b48dba --- /dev/null +++ b/doc/developer/sbfd.rst @@ -0,0 +1,143 @@ +.. _sbfd: + +**** +SBFD +**** + +:abbr:`SBFD (Seamless Bidirectional Forwarding Detection)` is: + + Seamless Bidirectional Forwarding Detection, a simplified mechanism for using BFD with a large + proportion of negotiation aspects eliminated, thus providing benefits + such as quick provisioning, as well as improved control and + flexibility for network nodes initiating path monitoring. + + -- :rfc:`7880` + +It is described and extended by the following RFCs: + +* :rfc:`7880` +* :rfc:`7881` + +.. _sbfd-sate-machine: + +SBFD state machine +================== + +SBFD takes the same data packet format as BFD, but with a much simpler state machine. +According to RFC7880, SBFD has a stateless SBFDReflector and a stateful SBFDInitiator with the state machine as below: + +:: + + +--+ + ADMIN DOWN, | | + TIMER | V + +------+ UP +------+ + | |-------------------->| |----+ + | DOWN | | UP | | UP + | |<--------------------| |<---+ + +------+ ADMIN DOWN, +------+ + TIMER + + Figure 1: SBFDInitiator Finite State Machine + +.. _sbfd-extention: + +SBFD extension - SRv6 encapsulation +=================================== + +SBFDInitiator periodically send packets to monitor the connection to SBFDReflector. We set up an SBFD connection between the source and the destination node of a path, +with the source node serving as Initiator and the destination node as Reflector. The communicated SBFD packets should also follow every exact hop in the path, +from the source to the destination, which could be achieved by segment routing. This requirement extends the node verification to the path verification. + +.. _sbfd-implement: + +implementation +=============== + +Some considerations when implementing sbfd. + + + +.. _sbfd-implement-coexist: + +SBFD Co-exist with BFD +-------------------------- + +Both SBFD and Classical BFD have their unique discriminator, SBFD can co-exist with BFD since they sharing a same discriminator pool in bfdd. +Also in bfdd SBFD and BFD can share most code logic, SBFD packet and BFD packet are demultiplexed by different discriminators. + + +.. _sbfd-implement-bfdname: + +SBFD name +--------- + +We introduced a bfd-name for every sbfd session. A unique bfd-name can be used to identify a sbfd session quickly. This is quite useful in our Srv6 deployment for path protection case. +A bfd-name is always associated with a TE path, for example if we use the sbfd session to protect the path A-B-D, we would assign the name 'path-a-b-d' or 'a-b-d' to the session. + +Meanwhile bfdd will notify the sbfd status to the Pathd, we should add the bfd-name field in PTM bfd notify message ZEBRA_BFD_DEST_REPLAY: + +:: + + * Message format: + * - header: command, vrf + * - l: interface index + * - c: family + * - AF_INET: + * - 4 bytes: ipv4 + * - AF_INET6: + * - 16 bytes: ipv6 + * - c: prefix length + * - l: bfd status + * - c: family + * - AF_INET: + * - 4 bytes: ipv4 + * - AF_INET6: + * - 16 bytes: ipv6 + * - c: prefix length + * - c: cbit + * - c: bfd name len <---- new field + * - Xbytes: bfd name <---- new field + * + * Commands: ZEBRA_BFD_DEST_REPLAY + * + * q(64), l(32), w(16), c(8) + + + +.. _sbfd-implement-port: + +SBFD UDP port +------------- + +According to RFC7881, SBFD Control packet dst port should be 7784, src port can be any but NOT 7784. In our implementation, the UDP ports in packet are set as: + + +:: + + UDP(sport=4784, dport=7784)/BFD() or UDP(sport=3784, dport=7784)/BFD() + +if "multihop" is specified for sbfd initiator we choose the 4784 as the source port, so the reflected packet will take 4784 as the dst port, this is a local BFD_MULTI_HOP_PORT so the reflected packet can be handled by the existing bfd_recv_cb function. +if "multihop" is not specified for sbfd initiator we choose the 3784 as the source port, this is a local BFD_DEFDESTPORT so the reflected packet can be handled by the existing bfd_recv_cb function. + + +For echo SBFD with SRv6 encapsulation case, we re-use the BFD Echo port, the UDP ports in packet are set as: + +:: + + UDP(sport=3785, dport=3785)/BFD() + + +we choose the 3785 as the source port, so the echo back packet will take 3785 as the dst port, this is a local BFD_DEF_ECHO_PORT so the packet can be handled by the existing bfd_recv_cb function. + + +.. _sbfd-not-implemented: + +Todo list for SBFD +------------------ + + Currently some features are not yet implemented for SBFD, will add it in future: + 1) SBFD in IPv4 only packet + 2) The ADMIN DOWN logic + 3) SBFD echo function in a initiator session + 4) SBFD over MPLS diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index bdf93a05c1..9f6986cd33 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -83,6 +83,7 @@ dev_RSTFILES = \ doc/developer/northbound/transactional-cli.rst \ doc/developer/northbound/yang-module-translator.rst \ doc/developer/northbound/yang-tools.rst \ + doc/developer/sbfd.rst \ # end EXTRA_DIST += \ diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 5d96ea5b33..42f7ca84dd 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -282,7 +282,9 @@ internal or external. interface and address information. In that case default router ID value is selected as the largest IP Address of the interfaces. When `router zebra` is not enabled *bgpd* can't get interface information so `router-id` is set to - 0.0.0.0. So please set router-id by hand. + 0.0.0.0, which is invalid and BGP session can't be established. + + So please set router-id by manually. .. _bgp-multiple-autonomous-systems: @@ -2702,6 +2704,12 @@ The following commands can be used in route maps: happen only when BGP updates have completely same communities value specified in the community list. +.. clicmd:: match community-limit (0-65535) + + This command matches BGP updates that use community list, and with a community + list count less or equal than the defined limit. Setting community-limit to 0 + will only match BGP updates with no community. + .. clicmd:: set community <none|COMMUNITY> additive This command sets the community value in BGP updates. If the attribute is @@ -2941,7 +2949,7 @@ BGP Extended Communities in Route Map ``CO:COLOR`` This is a format to define colors value. ``CO`` part is always 00 (default), - it can be used to support the requirements of Color-Only steering when using + it can be used to support the requirements of Color-Only steering when using a Null Endpoint in the SR-TE Policy as specified in Section 8.8 of [RFC9256]. The below shows in detail what the different combinations of ``CO`` bits can match on to for the purpose of determining what type of SR-TE Policy Tunnel diff --git a/doc/user/sbfd.rst b/doc/user/sbfd.rst new file mode 100644 index 0000000000..d26bffe07b --- /dev/null +++ b/doc/user/sbfd.rst @@ -0,0 +1,316 @@ +.. _sbfd: + +**** +SBFD +**** + +:abbr:`SBFD (Seamless Bidirectional Forwarding Detection)` is: + + Seamless Bidirectional Forwarding Detection, a simplified mechanism for using BFD with a large + proportion of negotiation aspects eliminated, thus providing benefits + such as quick provisioning, as well as improved control and + flexibility for network nodes initiating path monitoring. + + -- :rfc:`7880` + +It is described and extended by the following RFCs: + +* :rfc:`7880` +* :rfc:`7881` + +.. _sbfd-sate-machine: + +SBFD state machine +================== + +SBFD takes the same data packet format as BFD, but with a much simpler state machine. +According to RFC7880, SBFD has a stateless SBFDReflector and a stateful SBFDInitiator with the state machine as below: + +:: + + +--+ + ADMIN DOWN, | | + TIMER | V + +------+ UP +------+ + | |-------------------->| |----+ + | DOWN | | UP | | UP + | |<--------------------| |<---+ + +------+ ADMIN DOWN, +------+ + TIMER + + Figure 1: SBFDInitiator Finite State Machine + +* If SBFDInitiator doesn't receive the response packet in time, session is DOWN. +* If SBFDInitiator receives the response packet in time: reponse state is ADMINDOWN, session goes DOWN; reponse state is UP, session goes UP. + +.. note:: + + SBFDReflector is stateless, it just transmit a packet in response to a received S-BFD packet having a valid S-BFD Discriminator in the Your Discriminator field. + + +.. _sbfd-extention: + +SBFD extension - SRv6 encapsulation +=================================== + +SBFDInitiator periodically send packets to monitor the connection to SBFDReflector. We set up an SBFD connection between the source and the destination node of a path, +with the source node serving as Initiator and the destination node as Reflector. The communicated SBFD packets should also follow every exact hop in the path, +from the source to the destination, which could be achieved by segment routing. This requirement extends the node verification to the path verification. +In the following example, we set up a sbfd session to monitor the path A-B-D (all nodes in the topo are SRv6 ready, which can decap and forward SRv6 packets). + +:: + + +------------C-----------+ + / \ + A---------------B---------------D + ^ ^ ^ + | | | + End: 100::A End: 100::B End: 100::D + Loopback: 200::A Loopback: 200::D + BFD Discrim: 123 BFD Discrim: 456 + + +A is the SBFDInitiator, and D is the SBFDReflector, A will trasmit the SBFD packet to B as the format: + +:: + + IPv6(src="200::A", dst="100::B", nh=43)/IPv6ExtHdrSegmentRouting(addresses=["100::D"], nh=41, segleft=1)/IPv6(src="200::A", dst="200::D")/UDP(dport=7784)/BFD(my_dis=123, your_disc=456, state=UP) + + +Upon receiving the packet, B will take the Srv6 End action since the dst ip 100::B is the End address, B will the shift the dst address according to Srv6 spec, then trasmit the SBFD packet to D as the format: + +:: + + IPv6(src="200::A", dst="100::D", nh=41)/IPv6(src="200::A", dst="200::D")/UDP(dport=7784)/BFD(my_dis=123, your_disc=456, state=UP) + + +After D receive the packet, It will decap the outer IPv6 header since the dst ip 100::D is the End address, the decapped packet is: + +:: + + IPv6(src="200::A", dst="200::D")/UDP(dport=7784)/BFD(my_dis=123, your_disc=456, state=UP) + + +This packet will be routed to kernel stack of D since its dst is 200::D. Then the SBFDReflector service on D will get the packet and Reflect it. The response packet will be: + +:: + + IPv6(src="200::D", dst="200::A")/UDP(sport=7784)/BFD(my_dis=456, your_disc=123, state=UP) + + +This packet will be routed in the topo according to the dst ip 200::A, it will go back to A by D-B-A or D-C-A in this case. + + + + In this example, Command used to configure the SBFDInitiator on A is: + +.. clicmd:: peer 200::D bfd-mode sbfd-init bfd-name a-b-d multihop local-address 200::A remote-discr 456 srv6-source-ipv6 200::A srv6-encap-data 100::B 100::D + + + Command used to configure the SBFDReflector on D is: + +.. clicmd:: sbfd reflector source-address 200::D discriminator 456 + + +.. _sbfd-echo: + +Echo SBFD with SRv6 encapsulation +================================= + +The SBFD Initiator-Reflector mode requires the configuration on both source and destination nodes. It can not work if the remote node has no SBD feature supported, especial on some third-party devices. +The Echo SBFD can solve this kind of deployment issue since it only requires the configuration on source node. This is also known as One-Arm BFD Echo or unaffiliated BFD Echo. +For example, we use Echo SBFD session to protect Srv6 path: A-B-D + +:: + + +------------C-----------+ + / \ + A---------------B---------------D + ^ ^ ^ + | | | + End: 100::A End: 100::B End: 100::D + Loopback: 200::A Loopback: 200::D + BFD Discrim: 123 + + +A is also the SBFDInitiator, and B, C, D is Srv6 ready nodes, A will trasmit the SBFD packet to B as the format: + +:: + + IPv6(src="200::A", dst="100::B", nh=43)/IPv6ExtHdrSegmentRouting(addresses=["100::D"], nh=41, segleft=1)/IPv6(src="200::A", dst="200::A")/UDP(dport=3785)/BFD(my_dis=123, your_disc=123, state=UP) + + +Upon receiving the packet, B will take the Srv6 End action since the dst ip 100::B is the End address, B will the shift the dst address according to Srv6 spec, then trasmit the SBFD packet to D as the format: + +:: + + IPv6(src="200::A", dst="100::D", nh=41)/IPv6(src="200::A", dst="200::A")/UDP(dport=3785)/BFD(my_dis=123, your_disc=123, state=UP) + + +After D receive the packet, It will decap the outer IPv6 header since the dst ip 100::D is the End address, the decapped packet is: + +:: + + IPv6(src="200::A", dst="200::A")/UDP(dport=3785)/BFD(my_dis=123, your_disc=123, state=UP) + + +This packet will be routed in the topo according to the dst ip 200::A, it will go back to A by D-B-A or D-C-A in this case. + + + + In this example, Command used to configure the SBFDInitiator on A is: + +.. clicmd:: peer 200::A bfd-mode sbfd-echo bfd-name a-b-d local-address 200::A srv6-source-ipv6 200::A srv6-encap-data 100::B 100::D + + + no configuration needed on D. + + +.. _sbfd-normal: + +normal SBFD with no SRv6 encapsulation +====================================== + +We can also configure a SBFD Initiator-Reflector session based on simple IPv6/IPv4 packet, no Srv6 involved in this case. + +:: + + +------------C-----------+ + / \ + A---------------B---------------D + ^ ^ ^ + | | | + Loopback: 200::A Loopback: 200::D + BFD Discrim: 123 BFD Discrim: 456 + + + +A is the SBFDInitiator, and D is the SBFDReflector, A will trasmit the SBFD packet to B or C as the format: + +:: + + IPv6(src="200::A", dst="200::D")/UDP(dport=7784)/BFD(my_dis=123, your_disc=456, state=UP) + + +Upon receiving the packet, B/C will route the packet to D according to the dst ip 200::D. + +After D receive the packet, packet will be sent to kernel stack of D since its dst is 200::D. Then the SBFDReflector service on D will get the packet and reflect it. The response packet will be: + +:: + + IPv6(src="200::D", dst="200::A")/UDP(sport=7784)/BFD(my_dis=456, your_disc=123, state=UP) + + +This packet will be routed in the topo according to the dst ip 200::A, it will go back to A by D-B-A or D-C-A in this case. + + + In this example, Command used to configure the SBFDInitiator on A is: + +.. clicmd:: peer 200::D bfd-mode sbfd-init bfd-name a-d local-address 200::A remote-discr 456 + + + Command used to configure the SBFDReflector on D is: + +.. clicmd:: sbfd reflector source-address 200::D discriminator 456 + + +.. note:: + + Currently some features are not yet implemented: + 1) SBFD in IPv4 only packet + 2) The ADMIN DOWN logic + 3) SBFD echo function in a initiator session + 4) SBFD over MPLS + + +.. _sbfd-show: + +show command +============ + +The exsiting bfd show command is also appliable to SBFD sessions, for example: +This command will show all the BFD and SBFD sessions in the bfdd: + +.. clicmd:: show bfd peers + + +:: + + BFD Peers: + peer 200::D bfd-mode sbfd-init bfd-name a-d multihop local-address 200::A vrf default remote-discr 456 + ID: 1421669725 + Remote ID: 456 + Active mode + Minimum TTL: 254 + Status: up + Uptime: 5 hour(s), 48 minute(s), 39 second(s) + Diagnostics: ok + Remote diagnostics: ok + Peer Type: sbfd initiator + Local timers: + Detect-multiplier: 3 + Receive interval: 300ms + Transmission interval: 1000ms + Echo receive interval: 50ms + Echo transmission interval: disabled + Remote timers: + Detect-multiplier: - + Receive interval: - + Transmission interval: - + Echo receive interval: - + +This command will show all the BFD and SBFD session packet counters: + +.. clicmd:: show bfd peers counters + +:: + + BFD Peers: + peer 200::A bfd-mode sbfd-echo bfd-name a-b-d local-address 200::A vrf default srv6-source-ipv6 200::A srv6-encap-data 100::B 100::D + Control packet input: 0 packets + Control packet output: 0 packets + Echo packet input: 23807 packets + Echo packet output: 23807 packets + Session up events: 1 + Session down events: 0 + Zebra notifications: 1 + Tx fail packet: 0 + + peer 200::D bfd-mode sbfd-init bfd-name a-d local-address 200::A vrf default remote-discr 456 + Control packet input: 25289 packets + Control packet output: 51812 packets + Echo packet input: 0 packets + Echo packet output: 0 packets + Session up events: 5 + Session down events: 4 + Zebra notifications: 9 + Tx fail packet: 0 + + +we also implemented a new show command to display BFD sessions with a bfd-name, the bfd-name is the key to search the sessioon. + +.. clicmd:: show bfd bfd-name a-b-d + +:: + + BFD Peers: + peer 200::A bfd-mode sbfd-echo bfd-name a-b-d local-address 200::A vrf default srv6-source-ipv6 200::A srv6-encap-data 100::B 100::D + ID: 123 + Remote ID: 123 + Active mode + Status: up + Uptime: 5 hour(s), 39 minute(s), 34 second(s) + Diagnostics: ok + Remote diagnostics: ok + Peer Type: echo + Local timers: + Detect-multiplier: 3 + Receive interval: 300ms + Transmission interval: 300ms + Echo receive interval: 300ms + Echo transmission interval: 1000ms + Remote timers: + Detect-multiplier: - + Receive interval: - + Transmission interval: - + Echo receive interval: - diff --git a/doc/user/static.rst b/doc/user/static.rst index 922c71a073..0ce6e2107e 100644 --- a/doc/user/static.rst +++ b/doc/user/static.rst @@ -176,3 +176,66 @@ multiple segments instructions. router# show ipv6 route [..] S>* 2005::/64 [1/0] is directly connected, ens3, seg6 2001:db8:aaaa::7,2002::4,2002::3,2002::2, weight 1, 00:00:06 + +STATIC also supports steering of IPv4 traffic over an SRv6 SID list, as shown in the example below. + +.. code-block:: frr + + ip route A.B.C.D <A.B.C.D|nexthop> segments U:U::U:U/Y:Y::Y:Y/Z:Z::Z:Z + +:: + + router(config)# ip route 10.0.0.0/24 sr0 segments fcbb:bbbb:1:2:3:fe00:: + + router# show ip route + [..] + S>* 10.0.0.0/24 [1/0] is directly connected, sr0, seg6 fcbb:bbbb:1:2:3:fe00::, weight 1, 00:00:06 + +SRv6 Static SIDs Commands +========================= + +.. clicmd:: segment-routing + + Move from configure mode to segment-routing node. + +.. clicmd:: srv6 + + Move from segment-routing node to srv6 node. + +.. clicmd:: static-sids + + Move from srv6 node to static-sids node. In this static-sids node, user can + configure static SRv6 SIDs. + +.. clicmd:: sid X:X::X:X/M locator NAME behavior <uN|uDT4|uDT6|uDT46> [vrf VRF] + + Specify the locator sid manually. Configuring a local sid in a purely static mode + by specifying the sid value would generate a unique SID. + This feature will support the configuration of static SRv6 decapsulation on the system. + + It supports four parameter options, corresponding to the following functions: + uN, uDT4, uDT6, uDT46 + + When configuring the local sid, if the action is set to 'uN', no vrf should be set. + While for any other action, it is necessary to specify a specific vrf. + +:: + + router# configure terminal + router(config)# segment-routing + router(config-sr)# srv6 + router(config-srv6)# static-sids + router(config-srv6-sids)# sid fcbb:bbbb:1:fe01::/64 locator LOC1 behavior uDT6 vrf Vrf1 + router(config-srv6-sids)# sid fcbb:bbbb:1:fe02::/64 locator LOC1 behavior uDT4 vrf Vrf1 + router(config-srv6-sids)# sid fcbb:bbbb:1:fe03::/64 locator LOC1 behavior uDT46 vrf Vrf2 + + router(config-srv6-locator)# show run + ... + segment-routing + srv6 + static-sids + sid fcbb:bbbb:1:fe01::/64 locator LOC1 behavior uDT6 vrf Vrf1 + sid fcbb:bbbb:1:fe02::/64 locator LOC1 behavior uDT4 vrf Vrf1 + sid fcbb:bbbb:1:fe03::/64 locator LOC1 behavior uDT46 vrf Vrf2 + ! + ...
\ No newline at end of file diff --git a/doc/user/subdir.am b/doc/user/subdir.am index 395ce305fe..e4e12788e7 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -55,6 +55,7 @@ user_RSTFILES = \ doc/user/watchfrr.rst \ doc/user/wecmp_linkbw.rst \ doc/user/mgmtd.rst \ + doc/user/sbfd.rst \ # end EXTRA_DIST += \ diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index ef3a619853..f700d36086 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -1133,10 +1133,8 @@ kernel. - any, - babel, - bgp, - - connected, - eigrp, - isis, - - kernel, - nhrp, - openfabric, - ospf, diff --git a/fpm/fpm_pb.h b/fpm/fpm_pb.h index 23d7e43993..8847365a37 100644 --- a/fpm/fpm_pb.h +++ b/fpm/fpm_pb.h @@ -111,6 +111,7 @@ static inline int fpm__nexthop__get(const Fpm__Nexthop *nh, nexthop->vrf_id = VRF_DEFAULT; nexthop->type = NEXTHOP_TYPE_IPV4; + memset(&nexthop->gate, 0, sizeof(nexthop->gate)); nexthop->gate.ipv4 = ipv4; if (ifindex) { nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX; diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 9ea2cfd0a1..5b62d3c518 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -840,12 +840,10 @@ void isis_circuit_down(struct isis_circuit *circuit) if (circuit->u.bc.adjdb[0]) { circuit->u.bc.adjdb[0]->del = isis_delete_adj; list_delete(&circuit->u.bc.adjdb[0]); - circuit->u.bc.adjdb[0] = NULL; } if (circuit->u.bc.adjdb[1]) { circuit->u.bc.adjdb[1]->del = isis_delete_adj; list_delete(&circuit->u.bc.adjdb[1]); - circuit->u.bc.adjdb[1] = NULL; } if (circuit->u.bc.is_dr[0]) { isis_dr_resign(circuit, 1); diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index 735e39a377..c86d929903 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -2365,31 +2365,27 @@ DEFPY_YANG (isis_frr_lfa_tiebreaker, if (!level || strmatch(level, "level-1")) { if (no) { - snprintf( - xpath, XPATH_MAXLEN, - "./fast-reroute/level-1/lfa/tiebreaker[index='%s']", - index_str); + snprintf(xpath, XPATH_MAXLEN, + "./fast-reroute/level-1/lfa/tiebreaker[index='%s'][type='%s']", + index_str, type); nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); } else { - snprintf( - xpath, XPATH_MAXLEN, - "./fast-reroute/level-1/lfa/tiebreaker[index='%s']/type", - index_str); + snprintf(xpath, XPATH_MAXLEN, + "./fast-reroute/level-1/lfa/tiebreaker[index='%s'][type='%s']/type", + index_str, type); nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, type); } } if (!level || strmatch(level, "level-2")) { if (no) { - snprintf( - xpath, XPATH_MAXLEN, - "./fast-reroute/level-2/lfa/tiebreaker[index='%s']", - index_str); + snprintf(xpath, XPATH_MAXLEN, + "./fast-reroute/level-2/lfa/tiebreaker[index='%s'][type='%s']", + index_str, type); nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); } else { - snprintf( - xpath, XPATH_MAXLEN, - "./fast-reroute/level-2/lfa/tiebreaker[index='%s']/type", - index_str); + snprintf(xpath, XPATH_MAXLEN, + "./fast-reroute/level-2/lfa/tiebreaker[index='%s'][type='%s']/type", + index_str, type); nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, type); } } diff --git a/isisd/isis_events.c b/isisd/isis_events.c index 5574bbc50f..5237e99999 100644 --- a/isisd/isis_events.c +++ b/isisd/isis_events.c @@ -83,7 +83,8 @@ static void circuit_commence_level(struct isis_circuit *circuit, int level) send_hello_sched(circuit, level, TRIGGERED_IIH_DELAY); circuit->u.bc.lan_neighs[level - 1] = list_new(); - circuit->u.bc.adjdb[level - 1] = list_new(); + if (!circuit->u.bc.adjdb[level - 1]) + circuit->u.bc.adjdb[level - 1] = list_new(); } } diff --git a/isisd/isis_srv6.c b/isisd/isis_srv6.c index 2348bd043a..4b97b5372e 100644 --- a/isisd/isis_srv6.c +++ b/isisd/isis_srv6.c @@ -698,7 +698,7 @@ void isis_srv6_area_init(struct isis_area *area) srv6db->config.max_end_pop_msd = ISIS_DEFAULT_SRV6_MAX_END_POP_MSD; srv6db->config.max_h_encaps_msd = ISIS_DEFAULT_SRV6_MAX_H_ENCAPS_MSD; srv6db->config.max_end_d_msd = ISIS_DEFAULT_SRV6_MAX_END_D_MSD; - strlcpy(srv6db->config.srv6_ifname, ISIS_DEFAULT_SRV6_IFNAME, sizeof(srv6db->config.srv6_ifname)); + strlcpy(srv6db->config.srv6_ifname, DEFAULT_SRV6_IFNAME, sizeof(srv6db->config.srv6_ifname)); #endif /* Initialize SRv6 Locator chunks list */ diff --git a/isisd/isis_srv6.h b/isisd/isis_srv6.h index bde14965f6..eeb76c0b86 100644 --- a/isisd/isis_srv6.h +++ b/isisd/isis_srv6.h @@ -16,8 +16,7 @@ #define ISIS_DEFAULT_SRV6_MAX_SEG_LEFT_MSD 3 #define ISIS_DEFAULT_SRV6_MAX_END_POP_MSD 3 #define ISIS_DEFAULT_SRV6_MAX_H_ENCAPS_MSD 2 -#define ISIS_DEFAULT_SRV6_MAX_END_D_MSD 5 -#define ISIS_DEFAULT_SRV6_IFNAME "sr0" +#define ISIS_DEFAULT_SRV6_MAX_END_D_MSD 5 /* SRv6 SID structure */ struct isis_srv6_sid_structure { diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index 18b13547a5..8c97dcda2f 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -1053,9 +1053,8 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, adj->algorithm)); json_object_int_add(srv6_endx_sid_json, "weight", adj->weight); - json_object_string_add(srv6_endx_sid_json, - "behavior", - seg6local_action2str( + json_object_string_add(srv6_endx_sid_json, "behavior", + srv6_endpoint_behavior_codepoint2str( adj->behavior)); json_object_boolean_add( srv6_endx_sid_json, "flagB", @@ -1081,22 +1080,17 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, for (adj = (struct isis_srv6_endx_sid_subtlv *) exts->srv6_endx_sid.head; adj; adj = adj->next) { - sbuf_push( - buf, indent, - "SRv6 End.X SID: %pI6, Algorithm: %s, Weight: %hhu, Endpoint Behavior: %s, Flags: B:%c, S:%c, P:%c\n", - &adj->sid, - sr_algorithm_string(adj->algorithm), - adj->weight, - seg6local_action2str(adj->behavior), - adj->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_BFLG - ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_SFLG - ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_PFLG - ? '1' - : '0'); + sbuf_push(buf, indent, + "SRv6 End.X SID: %pI6, Algorithm: %s, Weight: %hhu, Endpoint Behavior: %s, Flags: B:%c, S:%c, P:%c\n", + &adj->sid, sr_algorithm_string(adj->algorithm), + adj->weight, + srv6_endpoint_behavior_codepoint2str(adj->behavior), + adj->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_BFLG ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_SFLG ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_PFLG ? '1' + : '0'); if (adj->subsubtlvs) isis_format_subsubtlvs(adj->subsubtlvs, buf, NULL, @@ -1131,9 +1125,8 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, lan->algorithm)); json_object_int_add(srv6_lan_endx_sid_json, "weight", lan->weight); - json_object_string_add(srv6_lan_endx_sid_json, - "behavior", - seg6local_action2str( + json_object_string_add(srv6_lan_endx_sid_json, "behavior", + srv6_endpoint_behavior_codepoint2str( lan->behavior)); json_object_boolean_add( srv6_lan_endx_sid_json, "flagB", @@ -1162,24 +1155,19 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, for (lan = (struct isis_srv6_lan_endx_sid_subtlv *) exts->srv6_lan_endx_sid.head; lan; lan = lan->next) { - sbuf_push( - buf, indent, - "SRv6 Lan End.X SID: %pI6, Algorithm: %s, Weight: %hhu, Endpoint Behavior: %s, Flags: B:%c, S:%c, P:%c " - "Neighbor-ID: %pSY\n", - &lan->sid, - sr_algorithm_string(lan->algorithm), - lan->weight, - seg6local_action2str(lan->behavior), - lan->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_BFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_SFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_PFLG - ? '1' - : '0', - lan->neighbor_id); + sbuf_push(buf, indent, + "SRv6 Lan End.X SID: %pI6, Algorithm: %s, Weight: %hhu, Endpoint Behavior: %s, Flags: B:%c, S:%c, P:%c " + "Neighbor-ID: %pSY\n", + &lan->sid, sr_algorithm_string(lan->algorithm), + lan->weight, + srv6_endpoint_behavior_codepoint2str(lan->behavior), + lan->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_BFLG ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_SFLG ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_PFLG ? '1' + : '0', + lan->neighbor_id); if (lan->subsubtlvs) isis_format_subsubtlvs(lan->subsubtlvs, buf, NULL, @@ -2823,7 +2811,7 @@ static void format_item_srv6_end_sid(uint16_t mtid, struct isis_item *i, sid_json = json_object_new_object(); json_object_object_add(json, "srv6EndSid", sid_json); json_object_string_add(sid_json, "endpointBehavior", - seg6local_action2str(sid->behavior)); + srv6_endpoint_behavior_codepoint2str(sid->behavior)); json_object_string_addf(sid_json, "sidValue", "%pI6", &sid->sid); if (sid->subsubtlvs) { struct json_object *subtlvs_json; @@ -2836,7 +2824,7 @@ static void format_item_srv6_end_sid(uint16_t mtid, struct isis_item *i, } else { sbuf_push(buf, indent, "SRv6 End SID "); sbuf_push(buf, 0, "Endpoint Behavior: %s, ", - seg6local_action2str(sid->behavior)); + srv6_endpoint_behavior_codepoint2str(sid->behavior)); sbuf_push(buf, 0, "SID value: %pI6\n", &sid->sid); if (sid->subsubtlvs) { diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index c64bbf7f69..5798d318f2 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -777,12 +777,6 @@ struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size); #define ISIS_MT_AT_MASK 0x4000 #endif -/* RFC 8919 */ -#define ISIS_SABM_FLAG_R 0x80 /* RSVP-TE */ -#define ISIS_SABM_FLAG_S 0x40 /* Segment Routing Policy */ -#define ISIS_SABM_FLAG_L 0x20 /* Loop-Free Alternate */ -#define ISIS_SABM_FLAG_X 0x10 /* Flex-Algorithm - RFC9350 */ - void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd); void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs, struct list *addresses); @@ -18,6 +18,7 @@ #include "table.h" #include "vty.h" #include "bfd.h" +#include "bfdd/bfd.h" DEFINE_MTYPE_STATIC(LIB, BFD_INFO, "BFD info"); DEFINE_MTYPE_STATIC(LIB, BFD_SOURCE, "BFD source cache"); @@ -140,14 +141,15 @@ static void bfd_source_cache_put(struct bfd_session_params *session); * bfd_get_peer_info - Extract the Peer information for which the BFD session * went down from the message sent from Zebra to clients. */ -static struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, - struct prefix *sp, int *status, - int *remote_cbit, vrf_id_t vrf_id) +static struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, struct prefix *sp, + int *status, int *remote_cbit, vrf_id_t vrf_id, + char *bfd_name) { unsigned int ifindex; struct interface *ifp = NULL; int plen; int local_remote_cbit; + uint8_t bfd_name_len = 0; /* * If the ifindex lookup fails the @@ -194,6 +196,13 @@ static struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, STREAM_GETC(s, local_remote_cbit); if (remote_cbit) *remote_cbit = local_remote_cbit; + + STREAM_GETC(s, bfd_name_len); + if (bfd_name_len) { + STREAM_GET(bfd_name, s, bfd_name_len); + *(bfd_name + bfd_name_len) = 0; + } + return ifp; stream_failure: @@ -918,6 +927,7 @@ int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS) struct prefix dp; struct prefix sp; char ifstr[128], cbitstr[32]; + char bfd_name[BFD_NAME_SIZE + 1] = { 0 }; if (!zclient->bfd_integration) return 0; @@ -926,8 +936,7 @@ int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS) if (bsglobal.shutting_down) return 0; - ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &state, &remote_cbit, - vrf_id); + ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &state, &remote_cbit, vrf_id, bfd_name); /* * When interface lookup fails or an invalid stream is read, we must * not proceed otherwise it will trigger an assertion while checking @@ -26,6 +26,8 @@ extern "C" { #define BFD_PROFILE_NAME_LEN 64 +#define BFD_NAME_SIZE 255 + const char *bfd_get_status_str(int status); extern void bfd_client_sendmsg(struct zclient *zclient, int command, @@ -409,6 +411,8 @@ struct bfd_session_arg { uint32_t min_tx; /** Detection multiplier. */ uint32_t detection_multiplier; + /* bfd session name*/ + char bfd_name[BFD_NAME_SIZE + 1]; }; /** diff --git a/lib/command.h b/lib/command.h index c60751789f..dfd732893b 100644 --- a/lib/command.h +++ b/lib/command.h @@ -154,6 +154,7 @@ enum node_type { PCEP_PCE_NODE, /* PCE configuration node */ PCEP_PCC_NODE, /* PCC configuration node */ SRV6_NODE, /* SRv6 node */ + SRV6_SIDS_NODE, /* SRv6 SIDs node */ SRV6_LOCS_NODE, /* SRv6 locators node */ SRV6_LOC_NODE, /* SRv6 locator node */ SRV6_ENCAP_NODE, /* SRv6 encapsulation node */ diff --git a/lib/event.c b/lib/event.c index d95b3021a7..6081ba4727 100644 --- a/lib/event.c +++ b/lib/event.c @@ -111,6 +111,11 @@ static struct cpu_event_history *cpu_records_get(struct event_loop *loop, return res; } +static void cpu_records_clear(struct cpu_event_history *p) +{ + memset(p->_clear_begin, 0, p->_clear_end - p->_clear_begin); +} + static void cpu_records_free(struct cpu_event_history **p) { XFREE(MTYPE_EVENT_STATS, *p); @@ -250,20 +255,15 @@ static void cpu_record_clear(uint8_t filter) for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) { frr_with_mutex (&m->mtx) { struct cpu_event_history *item; - struct cpu_records_head old[1]; - cpu_records_init(old); - cpu_records_swap_all(old, m->cpu_records); - - while ((item = cpu_records_pop(old))) { + /* it isn't possible to free the memory here + * because some of these will be in use (e.g. + * the one we're currently running in!) + */ + frr_each (cpu_records, m->cpu_records, item) { if (item->types & filter) - cpu_records_free(&item); - else - cpu_records_add(m->cpu_records, - item); + cpu_records_clear(item); } - - cpu_records_fini(old); } } } diff --git a/lib/frrevent.h b/lib/frrevent.h index 44776b29a7..c35b39a147 100644 --- a/lib/frrevent.h +++ b/lib/frrevent.h @@ -139,6 +139,10 @@ struct cpu_event_history { struct cpu_records_item item; void (*func)(struct event *e); + + /* fields between the pair of these two are nulled on "clear event cpu" */ + char _clear_begin[0]; + atomic_size_t total_cpu_warn; atomic_size_t total_wall_warn; atomic_size_t total_starv_warn; @@ -149,6 +153,10 @@ struct cpu_event_history { } real; struct time_stats cpu; atomic_uint_fast32_t types; + + /* end of cleared region */ + char _clear_end[0]; + const char *funcname; }; diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c index 3a07a1d2d9..806242ed53 100644 --- a/lib/mgmt_be_client.c +++ b/lib/mgmt_be_client.c @@ -1114,19 +1114,24 @@ static void be_client_handle_notify(struct mgmt_be_client *client, void *msgbuf, size_t msg_len) { struct mgmt_msg_notify_data *notif_msg = msgbuf; - struct nb_node *nb_node; - struct lyd_node *dnode; + struct nb_node *nb_node, *nb_parent; + struct lyd_node *dnode = NULL; const char *data = NULL; const char *notif; - LY_ERR err; + bool is_yang_notify; + LY_ERR err = LY_SUCCESS; debug_be_client("Received notification for client %s", client->name); notif = mgmt_msg_native_xpath_data_decode(notif_msg, msg_len, data); - if (!notif || !data) { + if (!notif) { log_err_be_client("Corrupt notify msg"); return; } + if (!data && (notif_msg->op == NOTIFY_OP_DS_REPLACE || notif_msg->op == NOTIFY_OP_DS_PATCH)) { + log_err_be_client("Corrupt replace/patch notify msg: missing data"); + return; + } nb_node = nb_node_find(notif); if (!nb_node) { @@ -1134,20 +1139,41 @@ static void be_client_handle_notify(struct mgmt_be_client *client, void *msgbuf, return; } - if (!nb_node->cbs.notify) { + is_yang_notify = !!CHECK_FLAG(nb_node->snode->nodetype, LYS_NOTIF); + + if (is_yang_notify && !nb_node->cbs.notify) { debug_be_client("No notification callback for: %s", notif); return; } - err = yang_parse_notification(notif, notif_msg->result_type, data, + if (!nb_node->cbs.notify) { + /* + * See if a parent has a callback, this is so backend's can + * listen for changes on an entire datastore sub-tree. + */ + for (nb_parent = nb_node->parent; nb_parent; nb_parent = nb_node->parent) + if (nb_parent->cbs.notify) + break; + if (!nb_parent) { + debug_be_client("Including parents, no DS notification callback for: %s", + notif); + return; + } + nb_node = nb_parent; + } + + if (data && is_yang_notify) { + err = yang_parse_notification(notif, notif_msg->result_type, data, &dnode); + } else if (data) { + err = yang_parse_data(notif, notif_msg->result_type, false, true, false, data, &dnode); + } if (err) { - log_err_be_client("Can't parse notification data for: %s", - notif); + log_err_be_client("Can't parse notification data for: %s", notif); return; } - nb_callback_notify(nb_node, notif, dnode); + nb_callback_notify(nb_node, notif_msg->op, notif, dnode); lyd_free_all(dnode); } diff --git a/lib/nexthop.c b/lib/nexthop.c index 332581fbd8..ee6c2b7ec0 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -772,68 +772,30 @@ unsigned int nexthop_level(const struct nexthop *nexthop) return rv; } -/* Only hash word-sized things, let cmp do the rest. */ -uint32_t nexthop_hash_quick(const struct nexthop *nexthop) +uint32_t nexthop_hash(const struct nexthop *nexthop) { uint32_t key = 0x45afe398; - int i; - key = jhash_3words(nexthop->type, nexthop->vrf_id, - nexthop->nh_label_type, key); - - if (nexthop->nh_label) { - int labels = nexthop->nh_label->num_labels; + /* type, vrf, ifindex, ip addresses - see nexthop.h */ + key = _nexthop_hash_bytes(nexthop, key); - i = 0; + key = jhash_1word(nexthop->flags & NEXTHOP_FLAGS_HASHED, key); - while (labels >= 3) { - key = jhash_3words(nexthop->nh_label->label[i], - nexthop->nh_label->label[i + 1], - nexthop->nh_label->label[i + 2], - key); - labels -= 3; - i += 3; - } - - if (labels >= 2) { - key = jhash_2words(nexthop->nh_label->label[i], - nexthop->nh_label->label[i + 1], - key); - labels -= 2; - i += 2; - } + if (nexthop->nh_label) { + const struct mpls_label_stack *ls = nexthop->nh_label; - if (labels >= 1) - key = jhash_1word(nexthop->nh_label->label[i], key); + /* num_labels itself isn't useful to hash, if the number of + * labels is different, the hash value will change just due to + * that already. + */ + key = jhash(ls->label, sizeof(ls->label[0]) * ls->num_labels, key); } - key = jhash_2words(nexthop->ifindex, - CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK), - key); - /* Include backup nexthops, if present */ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { int backups = nexthop->backup_num; - i = 0; - - while (backups >= 3) { - key = jhash_3words(nexthop->backup_idx[i], - nexthop->backup_idx[i + 1], - nexthop->backup_idx[i + 2], key); - backups -= 3; - i += 3; - } - - while (backups >= 2) { - key = jhash_2words(nexthop->backup_idx[i], - nexthop->backup_idx[i + 1], key); - backups -= 2; - i += 2; - } - - if (backups >= 1) - key = jhash_1word(nexthop->backup_idx[i], key); + key = jhash(nexthop->backup_idx, sizeof(nexthop->backup_idx[0]) * backups, key); } if (nexthop->nh_srv6) { @@ -868,31 +830,6 @@ uint32_t nexthop_hash_quick(const struct nexthop *nexthop) return key; } - -#define GATE_SIZE 4 /* Number of uint32_t words in struct g_addr */ - -/* For a more granular hash */ -uint32_t nexthop_hash(const struct nexthop *nexthop) -{ - uint32_t gate_src_rmap_raw[GATE_SIZE * 3] = {}; - /* Get all the quick stuff */ - uint32_t key = nexthop_hash_quick(nexthop); - - assert(((sizeof(nexthop->gate) + sizeof(nexthop->src) - + sizeof(nexthop->rmap_src)) - / 3) - == (GATE_SIZE * sizeof(uint32_t))); - - memcpy(gate_src_rmap_raw, &nexthop->gate, GATE_SIZE); - memcpy(gate_src_rmap_raw + GATE_SIZE, &nexthop->src, GATE_SIZE); - memcpy(gate_src_rmap_raw + (2 * GATE_SIZE), &nexthop->rmap_src, - GATE_SIZE); - - key = jhash2(gate_src_rmap_raw, (GATE_SIZE * 3), key); - - return key; -} - void nexthop_copy_no_recurse(struct nexthop *copy, const struct nexthop *nexthop, struct nexthop *rparent) diff --git a/lib/nexthop.h b/lib/nexthop.h index 5dfb58d846..cea7c77e3e 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -8,6 +8,7 @@ #ifndef _LIB_NEXTHOP_H #define _LIB_NEXTHOP_H +#include "jhash.h" #include "prefix.h" #include "mpls.h" #include "vxlan.h" @@ -56,15 +57,48 @@ struct nexthop { struct nexthop *next; struct nexthop *prev; - /* - * What vrf is this nexthop associated with? + + /* begin of hashed data - all fields from here onwards are given to + * jhash() as one consecutive chunk. DO NOT create "padding holes". + * DO NOT insert pointers that need to be deep-hashed. + * + * static_assert() below needs to be updated when fields are added */ + char _hash_begin[0]; + + /* see above */ + enum nexthop_types_t type; + + /* What vrf is this nexthop associated with? */ vrf_id_t vrf_id; /* Interface index. */ ifindex_t ifindex; - enum nexthop_types_t type; + /* Type of label(s), if any */ + enum lsp_types_t nh_label_type; + + /* padding: keep 16 byte alignment here */ + + /* Nexthop address + * make sure all 16 bytes for IPv6 are zeroed when putting in an IPv4 + * address since the entire thing is hashed as-is + */ + union { + union g_addr gate; + enum blackhole_type bh_type; + }; + union g_addr src; + union g_addr rmap_src; /* Src is set via routemap */ + + /* end of hashed data - remaining fields in this struct are not + * directly fed into jhash(). Most of them are actually part of the + * hash but have special rules or handling attached. + */ + char _hash_end[0]; + + /* Weight of the nexthop ( for unequal cost ECMP ) */ + uint8_t weight; uint16_t flags; #define NEXTHOP_FLAG_ACTIVE (1 << 0) /* This nexthop is alive. */ @@ -82,18 +116,15 @@ struct nexthop { #define NEXTHOP_FLAG_EVPN (1 << 8) /* nexthop is EVPN */ #define NEXTHOP_FLAG_LINKDOWN (1 << 9) /* is not removed on link down */ + /* which flags are part of nexthop_hash(). Should probably be split + * off into a separate field... + */ +#define NEXTHOP_FLAGS_HASHED NEXTHOP_FLAG_ONLINK + #define NEXTHOP_IS_ACTIVE(flags) \ (CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \ && !CHECK_FLAG(flags, NEXTHOP_FLAG_DUPLICATE)) - /* Nexthop address */ - union { - union g_addr gate; - enum blackhole_type bh_type; - }; - union g_addr src; - union g_addr rmap_src; /* Src is set via routemap */ - /* Nexthops obtained by recursive resolution. * * If the nexthop struct needs to be resolved recursively, @@ -104,15 +135,9 @@ struct nexthop { /* Recursive parent */ struct nexthop *rparent; - /* Type of label(s), if any */ - enum lsp_types_t nh_label_type; - /* Label(s) associated with this nexthop. */ struct mpls_label_stack *nh_label; - /* Weight of the nexthop ( for unequal cost ECMP ) */ - uint8_t weight; - /* Count and index of corresponding backup nexthop(s) in a backup list; * only meaningful if the HAS_BACKUP flag is set. */ @@ -138,6 +163,29 @@ struct nexthop { struct nexthop_srv6 *nh_srv6; }; +/* all hashed fields (including padding, if it is necessary to add) need to + * be listed in the static_assert below + */ + +#define S(field) sizeof(((struct nexthop *)NULL)->field) + +static_assert( + offsetof(struct nexthop, _hash_end) - offsetof(struct nexthop, _hash_begin) == + S(type) + S(vrf_id) + S(ifindex) + S(nh_label_type) + S(gate) + S(src) + S(rmap_src), + "struct nexthop contains padding, this can break things. insert _pad fields at appropriate places"); + +#undef S + +/* this is here to show exactly what is meant by the comments above about + * the hashing + */ +static inline uint32_t _nexthop_hash_bytes(const struct nexthop *nh, uint32_t seed) +{ + return jhash(&nh->_hash_begin, + offsetof(struct nexthop, _hash_end) - offsetof(struct nexthop, _hash_begin), + seed); +} + /* Utility to append one nexthop to another. */ #define NEXTHOP_APPEND(to, new) \ do { \ @@ -183,27 +231,11 @@ struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type, /* * Hash a nexthop. Suitable for use with hash tables. * - * This function uses the following values when computing the hash: - * - vrf_id - * - ifindex - * - type - * - gate - * - * nexthop - * The nexthop to hash - * - * Returns: - * 32-bit hash of nexthop + * Please double check the code on what is included in the hash, there was + * documentation here but it got outdated and the only thing worse than no + * doc is incorrect doc. */ uint32_t nexthop_hash(const struct nexthop *nexthop); -/* - * Hash a nexthop only on word-sized attributes: - * - vrf_id - * - ifindex - * - type - * - (some) flags - */ -uint32_t nexthop_hash_quick(const struct nexthop *nexthop); extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2); extern bool nexthop_same_no_labels(const struct nexthop *nh1, diff --git a/lib/northbound.c b/lib/northbound.c index 418cb246f6..60794b8728 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -685,19 +685,30 @@ void nb_config_diff(const struct nb_config *config1, lyd_free_all(diff); } -static int dnode_create(struct nb_config *candidate, const char *xpath, - const char *value, uint32_t options, - struct lyd_node **new_dnode) +/** + * dnode_create() - create a new node in the tree + * @candidate: config tree to create node in. + * @xpath: target node to create. + * @value: value for the new if required. + * @options: lyd_new_path options + * @new_dnode: the newly created node. If options includes LYD_NEW_PATH_UPDATE, + * and the node exists (i.e., isn't create but updated), then + * new_node will be set to NULL not the existing node). + * + * Return: NB_OK or NB_ERR. + */ +static LY_ERR dnode_create(struct nb_config *candidate, const char *xpath, const char *value, + uint32_t options, struct lyd_node **new_dnode) { struct lyd_node *dnode; LY_ERR err; - err = lyd_new_path(candidate->dnode, ly_native_ctx, xpath, value, - options, &dnode); + err = lyd_new_path2(candidate->dnode, ly_native_ctx, xpath, value, 0, 0, options, NULL, + &dnode); if (err) { flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path(%s) failed: %d", __func__, xpath, err); - return NB_ERR; + return err; } else if (dnode) { err = lyd_new_implicit_tree(dnode, LYD_IMPLICIT_NO_STATE, NULL); if (err) { @@ -708,7 +719,7 @@ static int dnode_create(struct nb_config *candidate, const char *xpath, } if (new_dnode) *new_dnode = dnode; - return NB_OK; + return LY_SUCCESS; } int nb_candidate_edit(struct nb_config *candidate, const struct nb_node *nb_node, @@ -1857,7 +1868,7 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, return nb_node->cbs.rpc(&args); } -void nb_callback_notify(const struct nb_node *nb_node, const char *xpath, +void nb_callback_notify(const struct nb_node *nb_node, uint8_t op, const char *xpath, struct lyd_node *dnode) { struct nb_cb_notify_args args = {}; @@ -1865,6 +1876,7 @@ void nb_callback_notify(const struct nb_node *nb_node, const char *xpath, DEBUGD(&nb_dbg_cbs_notify, "northbound notify: %s", xpath); args.xpath = xpath; + args.op = op; args.dnode = dnode; nb_node->cbs.notify(&args); } diff --git a/lib/northbound.h b/lib/northbound.h index ce59bfd01a..c31f007e70 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -305,6 +305,7 @@ struct nb_cb_rpc_args { struct nb_cb_notify_args { /* XPath of the notification. */ const char *xpath; + uint8_t op; /* * libyang data node representing the notification. If the notification @@ -861,7 +862,7 @@ extern const void *nb_callback_lookup_next(const struct nb_node *nb_node, extern int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, const struct lyd_node *input, struct lyd_node *output, char *errmsg, size_t errmsg_len); -extern void nb_callback_notify(const struct nb_node *nb_node, const char *xpath, +extern void nb_callback_notify(const struct nb_node *nb_node, uint8_t op, const char *xpath, struct lyd_node *dnode); /* diff --git a/lib/northbound_notif.c b/lib/northbound_notif.c index b75c865613..9caca9f6d7 100644 --- a/lib/northbound_notif.c +++ b/lib/northbound_notif.c @@ -480,87 +480,96 @@ static struct op_changes_group *op_changes_group_next(void) /* Query for changes and notify */ /* ---------------------------- */ +static void timer_walk_abort(struct nb_notif_walk_args *args); static void timer_walk_continue(struct event *event); +static void timer_walk_done(struct nb_notif_walk_args *args); + +static struct op_change *__next_change(struct op_changes_group *group) +{ + struct op_change *next = RB_NEXT(op_changes, group->cur_change); + + /* Remove and free current so retry works */ + RB_REMOVE(op_changes, group->cur_changes, group->cur_change); + op_change_free(group->cur_change); + return next; +} + +static struct op_changes_group *__next_group(struct op_changes_group *group) +{ + __dbg("done with oper-path collection for group"); + op_changes_group_free(group); + return op_changes_group_next(); +} static enum nb_error oper_walk_done(const struct lyd_node *tree, void *arg, enum nb_error ret) { struct nb_notif_walk_args *args = arg; struct op_changes_group *group = args->group; const char *path = group->cur_change->path; - const char *op = group->cur_changes == &group->adds ? "add" : "delete"; /* we don't send batches when yielding as we need completed edit in any patch */ assert(ret != NB_YIELD); - nb_notif_walk = NULL; - if (ret == NB_ERR_NOT_FOUND) { __dbg("Path not found while walking oper tree: %s", path); - XFREE(MTYPE_NB_NOTIF_WALK_ARGS, args); - return ret; - } - /* Something else went wrong with the walk */ - if (ret != NB_OK) { + ret = NB_OK; + } else if (ret != NB_OK) { error: - __log_err("Error notifying for datastore change on path: %s: %s", path, - nb_err_name(ret)); - XFREE(MTYPE_NB_NOTIF_WALK_ARGS, args); - /* XXX Need to inform mgmtd/front-ends things are out-of-sync */ - return ret; - } - - __dbg("done with oper-path collection for %s path: %s", op, path); - - /* Do we need this? */ - while (tree->parent) - tree = lyd_parent(tree); - - /* Send the add (replace) notification */ - if (mgmt_be_send_ds_replace_notification(path, tree)) { - ret = NB_ERR; - goto error; + __log_err("Error notifying for datastore path: %s: %s", path, nb_err_name(ret)); + + timer_walk_abort(args); + goto done; + } else { + __dbg("Done with oper-path collection for path: %s", path); + + /* Do we need this? */ + while (tree->parent) + tree = lyd_parent(tree); + + /* Send the add (replace) notification */ + if (mgmt_be_send_ds_replace_notification(path, tree)) { + __log_err("Error sending notification message for path: %s", path); + ret = NB_ERR; + goto error; + } } /* - * Advance to next change (either dels or adds or both). + * Advance to next change. */ - group->cur_change = RB_NEXT(op_changes, group->cur_change); + group->cur_change = __next_change(group); if (!group->cur_change) { - __dbg("done with oper-path collection for group"); - op_changes_group_free(group); - - group = op_changes_group_next(); - args->group = group; - if (!group) { - __dbg("done with ALL oper-path collection for notification"); - XFREE(MTYPE_NB_NOTIF_WALK_ARGS, args); + args->group = __next_group(group); + if (!args->group) { + timer_walk_done(args); goto done; } } + /* Run next walk after giving other events a shot to run */ event_add_timer_msec(nb_notif_master, timer_walk_continue, args, 0, &nb_notif_timer); done: /* Done with current walk and scheduled next one if there is more */ nb_notif_walk = NULL; - return NB_OK; + return ret; } -static LY_ERR nb_notify_delete_changes(struct nb_notif_walk_args *args) +static int nb_notify_delete_changes(struct nb_notif_walk_args *args) { struct op_changes_group *group = args->group; - LY_ERR err; group->cur_change = RB_MIN(op_changes, group->cur_changes); while (group->cur_change) { - err = mgmt_be_send_ds_delete_notification(group->cur_change->path); - assert(err == LY_SUCCESS); /* XXX */ - - group->cur_change = RB_NEXT(op_changes, group->cur_change); + if (mgmt_be_send_ds_delete_notification(group->cur_change->path)) { + __log_err("Error sending delete notification message for path: %s", + group->cur_change->path); + return 1; + } + group->cur_change = __next_change(group); } - - return LY_SUCCESS; + return 0; } static void timer_walk_continue(struct event *event) @@ -568,15 +577,17 @@ static void timer_walk_continue(struct event *event) struct nb_notif_walk_args *args = EVENT_ARG(event); struct op_changes_group *group = args->group; const char *path; - LY_ERR err; + int ret; /* * Notify about deletes until we have add changes to collect. */ while (group->cur_changes == &group->dels) { - err = nb_notify_delete_changes(args); - assert(err == LY_SUCCESS); /* XXX */ - assert(!group->cur_change); /* we send all the deletes in one message */ + ret = nb_notify_delete_changes(args); + if (ret) { + timer_walk_abort(args); + return; + } /* after deletes advance to adds */ group->cur_changes = &group->adds; @@ -584,16 +595,12 @@ static void timer_walk_continue(struct event *event) if (group->cur_change) break; - __dbg("done with oper-path change group"); - op_changes_group_free(group); - - group = op_changes_group_next(); - args->group = group; - if (!group) { - __dbg("done with ALL oper-path changes"); - XFREE(MTYPE_NB_NOTIF_WALK_ARGS, args); + args->group = __next_group(group); + if (!args->group) { + timer_walk_done(args); return; } + group = args->group; } path = group->cur_change->path; @@ -621,6 +628,22 @@ static void timer_walk_start(struct event *event) timer_walk_continue(event); } +static void timer_walk_abort(struct nb_notif_walk_args *args) +{ + __dbg("Failed notifying datastore changes, will retry"); + + __dbg("oper-state notify setting retry timer to fire in: %d msec ", NB_NOTIF_TIMER_MSEC); + event_add_timer_msec(nb_notif_master, timer_walk_continue, args, NB_NOTIF_TIMER_MSEC, + &nb_notif_timer); +} + +static void timer_walk_done(struct nb_notif_walk_args *args) +{ + __dbg("Finished notifying for all datastore changes"); + assert(!args->group); + XFREE(MTYPE_NB_NOTIF_WALK_ARGS, args); +} + static void nb_notif_set_walk_timer(void) { if (nb_notif_walk) { @@ -659,19 +682,23 @@ void nb_notif_init(struct event_loop *tm) void nb_notif_terminate(void) { - struct nb_notif_walk_args *args; + struct nb_notif_walk_args *args = nb_notif_timer ? EVENT_ARG(nb_notif_timer) : NULL; struct op_changes_group *group; + __dbg("terminating: timer: %p timer arg: %p walk %p", nb_notif_timer, args, nb_notif_walk); + EVENT_OFF(nb_notif_timer); if (nb_notif_walk) { - nb_oper_cancel_walk(nb_notif_walk); - /* need to free the group that's in the walk */ + /* Grab walk args from walk if active. */ args = nb_oper_walk_finish_arg(nb_notif_walk); - if (args) - op_changes_group_free(args->group); + nb_oper_cancel_walk(nb_notif_walk); nb_notif_walk = NULL; } + if (args) { + op_changes_group_free(args->group); + XFREE(MTYPE_NB_NOTIF_WALK_ARGS, args); + } while ((group = op_changes_group_next())) op_changes_group_free(group); diff --git a/lib/privs.c b/lib/privs.c index b0809bf690..e7df383e5d 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -210,10 +210,11 @@ int zprivs_change_caps(zebra_privs_ops_t op) { cap_flag_value_t cflag; - /* should be no possibility of being called without valid caps */ - assert(zprivs_state.syscaps_p && zprivs_state.caps); - if (!(zprivs_state.syscaps_p && zprivs_state.caps)) - exit(1); + /* Called without valid caps - just return. Not every daemon needs + * privs. + */ + if (zprivs_state.syscaps_p == NULL || zprivs_state.caps == NULL) + return 0; if (op == ZPRIVS_RAISE) cflag = CAP_SET; diff --git a/lib/route_types.pl b/lib/route_types.pl index c75a866964..834cb822d2 100755 --- a/lib/route_types.pl +++ b/lib/route_types.pl @@ -127,9 +127,12 @@ printf "#define SHOW_ROUTE_V6_HEADER \\\n%s\n", codelist(@protosv6); print "\n"; sub collect { - my ($daemon, $ipv4, $ipv6, $any) = @_; + my ($daemon, $ipv4, $ipv6, $any, $ip_prot) = @_; my (@names, @help) = ((), ()); for my $p (@protos) { + next if ($ip_prot == 1 && $daemon eq "zebra" && $protodetail{$p}->{"cname"} eq "kernel"); + next if ($ip_prot == 1 && $daemon eq "zebra" && $protodetail{$p}->{"cname"} eq "connected"); + next if ($ip_prot == 1 && $daemon eq "zebra" && $protodetail{$p}->{"cname"} eq "local"); next if ($protodetail{$p}->{"daemon"} eq $daemon && $daemon ne "zebra"); next if ($protodetail{$p}->{"restrict2"} ne "" && $protodetail{$p}->{"restrict2"} ne $daemon); @@ -151,24 +154,24 @@ for my $daemon (sort keys %daemons) { next unless ($daemons{$daemon}->{"ipv4"} || $daemons{$daemon}->{"ipv6"}); printf "/* %s */\n", $daemon; if ($daemons{$daemon}->{"ipv4"} && $daemons{$daemon}->{"ipv6"}) { - my ($names, $help) = collect($daemon, 1, 1, 0); + my ($names, $help) = collect($daemon, 1, 1, 0, 0); printf "#define FRR_REDIST_STR_%s \\\n %s\n", uc $daemon, $names; printf "#define FRR_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help; - ($names, $help) = collect($daemon, 1, 0, 0); + ($names, $help) = collect($daemon, 1, 0, 0, 0); printf "#define FRR_IP_REDIST_STR_%s \\\n %s\n", uc $daemon, $names; printf "#define FRR_IP_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help; - ($names, $help) = collect($daemon, 0, 1, 0); + ($names, $help) = collect($daemon, 0, 1, 0, 0); printf "#define FRR_IP6_REDIST_STR_%s \\\n %s\n", uc $daemon, $names; printf "#define FRR_IP6_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help; if ($daemon eq "zebra") { - ($names, $help) = collect($daemon, 1, 0, 1); + ($names, $help) = collect($daemon, 1, 0, 1, 1); printf "#define FRR_IP_PROTOCOL_MAP_STR_%s \\\n %s\n", uc $daemon, $names; printf "#define FRR_IP_PROTOCOL_MAP_HELP_STR_%s \\\n%s\n", uc $daemon, $help; - ($names, $help) = collect($daemon, 0, 1, 1); + ($names, $help) = collect($daemon, 0, 1, 1, 1); printf "#define FRR_IP6_PROTOCOL_MAP_STR_%s \\\n %s\n", uc $daemon, $names; printf "#define FRR_IP6_PROTOCOL_MAP_HELP_STR_%s \\\n%s\n", uc $daemon, $help; } diff --git a/lib/routemap.h b/lib/routemap.h index 8dcc17ecc3..1c02348313 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -310,6 +310,7 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(C, "frr-bgp-route-map:ip-route-source")) #define IS_MATCH_ROUTE_SRC_PL(C) \ (strmatch(C, "frr-bgp-route-map:ip-route-source-prefix-list")) +#define IS_MATCH_COMMUNITY_LIMIT(C) (strmatch(C, "frr-bgp-route-map:match-community-limit")) #define IS_MATCH_COMMUNITY(C) \ (strmatch(C, "frr-bgp-route-map:match-community")) #define IS_MATCH_LCOMMUNITY(C) \ diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 69b942064b..eb01709707 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -810,6 +810,10 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-match-condition/frr-bgp-route-map:list-name")); + } else if (IS_MATCH_COMMUNITY_LIMIT(condition)) { + vty_out(vty, " match community-limit %s\n", + yang_dnode_get_string(dnode, + "./rmap-match-condition/frr-bgp-route-map:community-limit")); } else if (IS_MATCH_COMMUNITY(condition)) { vty_out(vty, " match community %s", yang_dnode_get_string( diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c index 3247a0372c..7203c8ac8e 100644 --- a/lib/srcdest_table.c +++ b/lib/srcdest_table.c @@ -309,13 +309,3 @@ static ssize_t printfrr_rn(struct fbuf *buf, struct printfrr_eargs *ea, cbuf, sizeof(cbuf)); return bputs(buf, cbuf); } - -struct route_table *srcdest_srcnode_table(struct route_node *rn) -{ - if (rnode_is_dstnode(rn)) { - struct srcdest_rnode *srn = srcdest_rnode_from_rnode(rn); - - return srn->src_table; - } - return NULL; -} diff --git a/lib/srcdest_table.h b/lib/srcdest_table.h index ff97f9b735..a699d4a11b 100644 --- a/lib/srcdest_table.h +++ b/lib/srcdest_table.h @@ -87,8 +87,6 @@ static inline void *srcdest_rnode_table_info(struct route_node *rn) return route_table_get_info(srcdest_rnode_table(rn)); } -extern struct route_table *srcdest_srcnode_table(struct route_node *rn); - #ifdef __cplusplus } #endif diff --git a/lib/srv6.h b/lib/srv6.h index 9a041e3d85..7e4fb97ad1 100644 --- a/lib/srv6.h +++ b/lib/srv6.h @@ -22,6 +22,8 @@ #define SRV6_SID_FORMAT_NAME_SIZE 512 +#define DEFAULT_SRV6_IFNAME "sr0" + #ifdef __cplusplus extern "C" { #endif @@ -186,6 +188,42 @@ enum srv6_endpoint_behavior_codepoint { SRV6_ENDPOINT_BEHAVIOR_OPAQUE = 0xFFFF, }; +/* + * Convert SRv6 endpoint behavior codepoints to human-friendly string. + */ +static inline const char * +srv6_endpoint_behavior_codepoint2str(enum srv6_endpoint_behavior_codepoint behavior) +{ + switch (behavior) { + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + return "Reserved"; + case SRV6_ENDPOINT_BEHAVIOR_END: + return "End"; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + return "End.X"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + return "End.DT6"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + return "End.DT4"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + return "End.DT46"; + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + return "uN"; + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + return "uA"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + return "uDT6"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + return "uDT4"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + return "uDT46"; + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + return "Opaque"; + } + + return "Unspec"; +} + struct nexthop_srv6 { /* SRv6 localsid info for Endpoint-behaviour */ enum seg6local_action_t seg6local_action; diff --git a/lib/yang.c b/lib/yang.c index 2aa3539259..dd48d8861b 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -874,6 +874,60 @@ static void ly_zlog_cb(LY_LOG_LEVEL level, const char *msg, const char *data_pat zlog(priority, "libyang: %s", msg); } +LY_ERR yang_parse_data(const char *xpath, LYD_FORMAT format, bool as_subtree, bool is_oper, + bool validate, const char *data, struct lyd_node **tree) +{ + struct ly_in *in = NULL; + struct lyd_node *subtree = NULL; + uint32_t parse_options = LYD_PARSE_STRICT | LYD_PARSE_ONLY; + uint32_t validate_options = LYD_VALIDATE_PRESENT; + LY_ERR err; + + err = ly_in_new_memory(data, &in); + if (err != LY_SUCCESS) + return err; + + if (as_subtree) { + struct lyd_node *parent; + + /* + * Create the subtree branch from root using the xpath. This + * will be used below to parse the data rooted at the subtree -- + * a common YANG JSON technique (vs XML which starts all + * data trees from the root). + */ + err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, &parent, &subtree); + if (err != LY_SUCCESS) + goto done; + err = lyd_find_path(parent, xpath, false, &subtree); + if (err != LY_SUCCESS) + goto done; + } + + if (is_oper) + validate_options |= LYD_VALIDATE_OPERATIONAL; + +#ifdef LYD_VALIDATE_NOT_FINAL + if (!validate) + validate_options |= LYD_VALIDATE_NOT_FINAL; +#endif + + err = lyd_parse_data(ly_native_ctx, subtree, in, format, parse_options, validate_options, + tree); + if (err == LY_SUCCESS && subtree) + *tree = subtree; +done: + ly_in_free(in, 0); + if (err != LY_SUCCESS) { + if (*tree) + lyd_free_all(*tree); + else if (subtree) + lyd_free_all(subtree); + *tree = NULL; + } + return err; +} + LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format, const char *data, struct lyd_node **notif) { diff --git a/lib/yang.h b/lib/yang.h index eed2fa8dbe..748f089037 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -681,6 +681,25 @@ extern struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, bool explicit_co */ extern void yang_debugging_set(bool enable); + +/* + * Parse YANG data. + * + * Args: + * xpath: xpath of the data. + * format: LYD_FORMAT of input data. + * as_subtree: parse the data as starting at the subtree identified by xpath. + * is_oper: parse as operational state allows for invalid (logs warning). + * validate: validate the data (otherwise treat as non-final). + * data: input data. + * notif: pointer to the libyang data tree to store the parsed notification. + * If the notification is not on the top level of the yang model, + * the pointer to the notification node is still returned, but it's + * part of the full data tree with all its parents. + */ +LY_ERR yang_parse_data(const char *xpath, LYD_FORMAT format, bool as_subtree, bool is_oper, + bool validate, const char *data, struct lyd_node **tree); + /* * Parse a YANG notification. * diff --git a/lib/zclient.c b/lib/zclient.c index 063944fd3b..5deea8f0cf 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -31,6 +31,7 @@ DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient"); DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs"); +DEFINE_MTYPE_STATIC(LIB, REDIST_TABLE_DIRECT, "Redistribution table direct"); /* Zebra client events. */ enum zclient_event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT }; @@ -104,6 +105,11 @@ void zclient_free(struct zclient *zclient) XFREE(MTYPE_ZCLIENT, zclient); } +static void redist_free_instance(void *data) +{ + XFREE(MTYPE_REDIST_INST, data); +} + unsigned short *redist_check_instance(struct redist_proto *red, unsigned short instance) { @@ -126,8 +132,10 @@ void redist_add_instance(struct redist_proto *red, unsigned short instance) red->enabled = 1; - if (!red->instances) + if (!red->instances) { red->instances = list_new(); + red->instances->del = redist_free_instance; + } in = XMALLOC(MTYPE_REDIST_INST, sizeof(unsigned short)); *in = instance; @@ -143,23 +151,100 @@ void redist_del_instance(struct redist_proto *red, unsigned short instance) return; listnode_delete(red->instances, id); - XFREE(MTYPE_REDIST_INST, id); + red->instances->del(id); if (!red->instances->count) { red->enabled = 0; list_delete(&red->instances); } } -void redist_del_all_instances(struct redist_proto *red) +static void redist_free_table_direct(void *data) { - struct listnode *ln, *nn; - unsigned short *id; + XFREE(MTYPE_REDIST_TABLE_DIRECT, data); +} + +struct redist_table_direct *redist_lookup_table_direct(const struct redist_proto *red, + const struct redist_table_direct *table) +{ + struct redist_table_direct *ntable; + struct listnode *node; + + if (red->instances == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(red->instances, node, ntable)) { + if (table->vrf_id != ntable->vrf_id) + continue; + if (table->table_id != ntable->table_id) + continue; + + return ntable; + } + + return NULL; +} + +bool redist_table_direct_has_id(const struct redist_proto *red, int table_id) +{ + struct redist_table_direct *table; + struct listnode *node; + + if (red->instances == NULL) + return false; + for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) { + if (table->table_id != table_id) + continue; + + return true; + } + + return false; +} + +void redist_add_table_direct(struct redist_proto *red, const struct redist_table_direct *table) +{ + struct redist_table_direct *ntable; + + ntable = redist_lookup_table_direct(red, table); + if (ntable != NULL) + return; + + if (red->instances == NULL) { + red->instances = list_new(); + red->instances->del = redist_free_table_direct; + } + + red->enabled = 1; + + ntable = XCALLOC(MTYPE_REDIST_TABLE_DIRECT, sizeof(*ntable)); + ntable->vrf_id = table->vrf_id; + ntable->table_id = table->table_id; + listnode_add(red->instances, ntable); +} + +void redist_del_table_direct(struct redist_proto *red, const struct redist_table_direct *table) +{ + struct redist_table_direct *ntable; + + ntable = redist_lookup_table_direct(red, table); + if (ntable == NULL) + return; + + listnode_delete(red->instances, ntable); + red->instances->del(ntable); + if (red->instances->count == 0) { + red->enabled = 0; + list_delete(&red->instances); + } +} + +void redist_del_all_instances(struct redist_proto *red) +{ if (!red->instances) return; - for (ALL_LIST_ELEMENTS(red->instances, ln, nn, id)) - redist_del_instance(red, *id); + list_delete(&red->instances); } /* Stop zebra client services. */ @@ -480,6 +565,17 @@ enum zclient_send_status zclient_send_localsid(struct zclient *zclient, return zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); } +static void zclient_send_table_direct(struct zclient *zclient, afi_t afi, int type) +{ + struct redist_table_direct *table; + struct redist_proto *red = &zclient->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT]; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) + zebra_redistribute_send(type, zclient, afi, ZEBRA_ROUTE_TABLE_DIRECT, + table->table_id, table->vrf_id); +} + /* Send register requests to zebra daemon for the information in a VRF. */ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) { @@ -513,6 +609,12 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) if (!zclient->mi_redist[afi][i].enabled) continue; + if (i == ZEBRA_ROUTE_TABLE_DIRECT) { + zclient_send_table_direct(zclient, afi, + ZEBRA_REDISTRIBUTE_ADD); + continue; + } + struct listnode *node; unsigned short *id; @@ -580,6 +682,12 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id) if (!zclient->mi_redist[afi][i].enabled) continue; + if (i == ZEBRA_ROUTE_TABLE_DIRECT) { + zclient_send_table_direct(zclient, afi, + ZEBRA_REDISTRIBUTE_DELETE); + continue; + } + struct listnode *node; unsigned short *id; @@ -2016,6 +2124,15 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, enum zapi_route_notify_owner *note, afi_t *afi, safi_t *safi) { + struct prefix dummy; + + return zapi_route_notify_decode_srcdest(s, p, &dummy, tableid, note, afi, safi); +} + +bool zapi_route_notify_decode_srcdest(struct stream *s, struct prefix *p, struct prefix *src_p, + uint32_t *tableid, enum zapi_route_notify_owner *note, + afi_t *afi, safi_t *safi) +{ uint32_t t; afi_t afi_val; safi_t safi_val; @@ -2025,6 +2142,9 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, STREAM_GETC(s, p->family); STREAM_GETC(s, p->prefixlen); STREAM_GET(&p->u.prefix, s, prefix_blen(p)); + src_p->family = p->family; + STREAM_GETC(s, src_p->prefixlen); + STREAM_GET(&src_p->u.prefix, s, prefix_blen(src_p)); STREAM_GETL(s, t); STREAM_GETC(s, afi_val); STREAM_GETC(s, safi_val); @@ -2180,7 +2300,27 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh) n->type = znh->type; n->vrf_id = znh->vrf_id; n->ifindex = znh->ifindex; - n->gate = znh->gate; + + /* only copy values that have meaning - make sure "spare bytes" are + * left zeroed for hashing (look at _nexthop_hash_bytes) + */ + switch (znh->type) { + case NEXTHOP_TYPE_BLACKHOLE: + n->bh_type = znh->bh_type; + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + n->gate.ipv4 = znh->gate.ipv4; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + n->gate.ipv6 = znh->gate.ipv6; + break; + case NEXTHOP_TYPE_IFINDEX: + /* nothing, ifindex is always copied */ + break; + } + n->srte_color = znh->srte_color; n->weight = znh->weight; @@ -4634,9 +4774,52 @@ static void zclient_read(struct event *thread) zclient_event(ZCLIENT_READ, zclient); } +static void zclient_redistribute_table_direct(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi, + int instance, int command) +{ + struct redist_proto *red = &zclient->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT]; + bool has_table; + struct redist_table_direct table = { + .vrf_id = vrf_id, + .table_id = instance, + }; + + has_table = redist_lookup_table_direct(red, &table); + + if (command == ZEBRA_REDISTRIBUTE_ADD) { + if (has_table) + return; + + redist_add_table_direct(red, &table); + } else { + if (!has_table) + return; + + redist_del_table_direct(red, &table); + } + + if (zclient->sock > 0) + zebra_redistribute_send(command, zclient, afi, ZEBRA_ROUTE_TABLE_DIRECT, instance, + vrf_id); +} + void zclient_redistribute(int command, struct zclient *zclient, afi_t afi, int type, unsigned short instance, vrf_id_t vrf_id) { + /* + * When asking for table-direct redistribution the parameter + * `instance` has a different meaning: it means table + * identification. + * + * The table identification information is stored in + * `zclient->mi_redist` along with the VRF identification + * information in a pair (different from the usual single protocol + * instance value). + */ + if (type == ZEBRA_ROUTE_TABLE_DIRECT) { + zclient_redistribute_table_direct(zclient, vrf_id, afi, instance, command); + return; + } if (instance) { if (command == ZEBRA_REDISTRIBUTE_ADD) { diff --git a/lib/zclient.h b/lib/zclient.h index 2385a8a219..afd84acce2 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -268,6 +268,21 @@ struct redist_proto { struct list *instances; }; +/** + * Redistribute table direct instance data structure: keeps the VRF + * that subscribed to the table ID. + * + * **NOTE** + * `table_id` is an integer because that is what the netlink interface + * uses for route attribute RTA_TABLE (32bit int), however the whole + * zclient API uses `unsigned short` (and CLI commands) so it will be + * limited to the range 1 to 65535. + */ +struct redist_table_direct { + vrf_id_t vrf_id; + int table_id; +}; + struct zclient_capabilities { uint32_t ecmp; bool mpls_enabled; @@ -924,6 +939,15 @@ extern void redist_add_instance(struct redist_proto *, unsigned short); extern void redist_del_instance(struct redist_proto *, unsigned short); extern void redist_del_all_instances(struct redist_proto *red); +extern struct redist_table_direct * +redist_lookup_table_direct(const struct redist_proto *red, const struct redist_table_direct *table); +extern bool redist_table_direct_has_id(const struct redist_proto *red, int table_id); +extern void redist_add_table_direct(struct redist_proto *red, + const struct redist_table_direct *table); +extern void redist_del_table_direct(struct redist_proto *red, + const struct redist_table_direct *table); + + /* * Send to zebra that the specified vrf is using label to resolve * itself for L3VPN's. Repeated calls of this function with @@ -1144,6 +1168,9 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, uint32_t *tableid, enum zapi_route_notify_owner *note, afi_t *afi, safi_t *safi); +bool zapi_route_notify_decode_srcdest(struct stream *s, struct prefix *p, struct prefix *src_p, + uint32_t *tableid, enum zapi_route_notify_owner *note, + afi_t *afi, safi_t *safi); bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, uint32_t *priority, uint32_t *unique, char *ifname, enum zapi_rule_notify_owner *note); diff --git a/mgmtd/mgmt_be_adapter.c b/mgmtd/mgmt_be_adapter.c index 1c32f936b0..8f70490379 100644 --- a/mgmtd/mgmt_be_adapter.c +++ b/mgmtd/mgmt_be_adapter.c @@ -84,6 +84,13 @@ static const char *const zebra_oper_xpaths[] = { NULL, }; +#ifdef HAVE_MGMTD_TESTC +static const char *const mgmtd_testc_oper_xpaths[] = { + "/frr-backend:clients", + NULL, +}; +#endif + #ifdef HAVE_RIPD static const char *const ripd_config_xpaths[] = { "/frr-filter:lib", @@ -154,6 +161,9 @@ static const char *const *be_client_config_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { }; static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { +#ifdef HAVE_MGMTD_TESTC + [MGMTD_BE_CLIENT_ID_TESTC] = mgmtd_testc_oper_xpaths, +#endif #ifdef HAVE_RIPD [MGMTD_BE_CLIENT_ID_RIPD] = ripd_oper_xpaths, #endif diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c index 96b7cbd598..8d59198803 100644 --- a/mgmtd/mgmt_fe_adapter.c +++ b/mgmtd/mgmt_fe_adapter.c @@ -107,32 +107,49 @@ static void mgmt_fe_free_ns_strings(struct ns_string_head *head) ns_string_fini(head); } -static void mgmt_fe_ns_string_remove_session(struct ns_string_head *head, - struct mgmt_fe_session_ctx *session) +static uint64_t mgmt_fe_ns_string_remove_session(struct ns_string_head *head, + struct mgmt_fe_session_ctx *session) { + struct listnode *node; struct ns_string *ns; + uint64_t clients = 0; frr_each_safe (ns_string, head, ns) { - listnode_delete(ns->sessions, session); + node = listnode_lookup(ns->sessions, session); + if (!node) + continue; + list_delete_node(ns->sessions, node); + clients |= mgmt_be_interested_clients(ns->s, MGMT_BE_XPATH_SUBSCR_TYPE_OPER); if (list_isempty(ns->sessions)) { ns_string_del(head, ns); mgmt_fe_free_ns_string(ns); } } + + return clients; } -static void mgmt_fe_add_ns_string(struct ns_string_head *head, const char *path, size_t plen, - struct mgmt_fe_session_ctx *session) +static uint64_t mgmt_fe_add_ns_string(struct ns_string_head *head, const char *path, size_t plen, + struct mgmt_fe_session_ctx *session) { struct ns_string *e, *ns; + uint64_t clients = 0; ns = XCALLOC(MTYPE_MGMTD_XPATH, sizeof(*ns) + plen + 1); strlcpy(ns->s, path, plen + 1); + e = ns_string_add(head, ns); - if (!e) + if (!e) { ns->sessions = list_new(); - if (!listnode_lookup(ns->sessions, session)) listnode_add(ns->sessions, session); + clients = mgmt_be_interested_clients(ns->s, MGMT_BE_XPATH_SUBSCR_TYPE_OPER); + } else { + XFREE(MTYPE_MGMTD_XPATH, ns); + if (!listnode_lookup(e->sessions, session)) + listnode_add(e->sessions, session); + } + + return clients; } char **mgmt_fe_get_all_selectors(void) @@ -1653,7 +1670,7 @@ static void fe_adapter_handle_notify_select(struct mgmt_fe_session_ctx *session, } if (msg->replace) { - mgmt_fe_ns_string_remove_session(&mgmt_fe_ns_strings, session); + clients = mgmt_fe_ns_string_remove_session(&mgmt_fe_ns_strings, session); // [ ] Keep a local tree to optimize sending selectors to BE? // [*] Or just KISS and fanout the original message to BEs? // mgmt_remove_add_notify_selectors(session->notify_xpaths, selectors); @@ -1684,18 +1701,11 @@ static void fe_adapter_handle_notify_select(struct mgmt_fe_session_ctx *session, /* Add the new selectors to the global tree */ darr_foreach_p (selectors, sp) - mgmt_fe_add_ns_string(&mgmt_fe_ns_strings, *sp, darr_strlen(*sp), session); + clients |= mgmt_fe_add_ns_string(&mgmt_fe_ns_strings, *sp, darr_strlen(*sp), + session); - /* Check if any backends are interested in the new selectors. */ - if (msg->replace) { - /* If we are replacing we'll send all the selectors again with replace flag */ - clients = mgmt_be_interested_clients("/", MGMT_BE_XPATH_SUBSCR_TYPE_OPER); - } else { - darr_foreach_p (selectors, sp) - clients |= mgmt_be_interested_clients(*sp, MGMT_BE_XPATH_SUBSCR_TYPE_OPER); - } if (!clients) { - __dbg("No backends provide oper for notify selectors: '%s' txn-id %Lu session-id: %Lu", + __dbg("No backends to newly notify for selectors: '%s' txn-id %Lu session-id: %Lu", selstr, session->txn_id, session->session_id); goto done; } diff --git a/mgmtd/mgmt_testc.c b/mgmtd/mgmt_testc.c index 8bb07ed068..ab8ea9a04d 100644 --- a/mgmtd/mgmt_testc.c +++ b/mgmtd/mgmt_testc.c @@ -9,8 +9,10 @@ #include <zebra.h> #include <lib/version.h> #include "darr.h" +#include "debug.h" #include "libfrr.h" #include "mgmt_be_client.h" +#include "mgmt_msg_native.h" #include "northbound.h" /* ---------------- */ @@ -43,15 +45,15 @@ struct zebra_privs_t __privs = { .cap_num_i = 0, }; -#define OPTION_LISTEN 2000 -#define OPTION_NOTIF_COUNT 2001 -#define OPTION_TIMEOUT 2002 -const struct option longopts[] = { - { "listen", no_argument, NULL, OPTION_LISTEN }, - { "notif-count", required_argument, NULL, OPTION_NOTIF_COUNT }, - { "timeout", required_argument, NULL, OPTION_TIMEOUT }, - { 0 } -}; +#define OPTION_DATASTORE 2000 +#define OPTION_LISTEN 2001 +#define OPTION_NOTIF_COUNT 2002 +#define OPTION_TIMEOUT 2003 +const struct option longopts[] = { { "datastore", no_argument, NULL, OPTION_DATASTORE }, + { "listen", no_argument, NULL, OPTION_LISTEN }, + { "notify-count", required_argument, NULL, OPTION_NOTIF_COUNT }, + { "timeout", required_argument, NULL, OPTION_TIMEOUT }, + { 0 } }; /* Master of threads. */ @@ -79,6 +81,20 @@ struct frr_signal_t __signals[] = { #define MGMTD_TESTC_VTY_PORT 2624 /* clang-format off */ +static const struct frr_yang_module_info frr_if_info = { + .name = "frr-interface", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = "/frr-interface:lib/interface", + .cbs.notify = async_notification, + }, + { + .xpath = NULL, + } + } +}; + static const struct frr_yang_module_info frr_ripd_info = { .name = "frr-ripd", .ignore_cfg_cbs = true, @@ -98,6 +114,8 @@ static const struct frr_yang_module_info frr_ripd_info = { }; static const struct frr_yang_module_info *const mgmt_yang_modules[] = { + &frr_backend_info, + &frr_if_info, &frr_ripd_info, }; @@ -123,6 +141,7 @@ const char **__rpc_xpaths; struct mgmt_be_client_cbs __client_cbs = {}; struct event *event_timeout; +int f_datastore; int o_notif_count = 1; int o_timeout; @@ -165,10 +184,56 @@ static void success(struct event *event) quit(0); } -static void async_notification(struct nb_cb_notify_args *args) +static void __ds_notification(struct nb_cb_notify_args *args) { - zlog_notice("Received YANG notification"); + uint8_t *output = NULL; + + zlog_notice("Received YANG datastore notification: op %u", args->op); + + if (args->op == NOTIFY_OP_NOTIFICATION) { + zlog_warn("ignoring non-datastore op notification: %s", args->xpath); + return; + } + + /* datastore notification */ + switch (args->op) { + case NOTIFY_OP_DS_REPLACE: + printfrr("#OP=REPLACE: %s\n", args->xpath); + break; + case NOTIFY_OP_DS_DELETE: + printfrr("#OP=DELETE: %s\n", args->xpath); + break; + case NOTIFY_OP_DS_PATCH: + printfrr("#OP=PATCH: %s\n", args->xpath); + break; + default: + printfrr("#OP=%u: unknown notify op\n", args->op); + quit(1); + } + if (args->dnode && args->op != NOTIFY_OP_DS_DELETE) { + output = yang_print_tree(args->dnode, LYD_JSON, LYD_PRINT_SHRINK); + if (output) { + printfrr("%s\n", output); + darr_free(output); + } + } + fflush(stdout); + + if (o_notif_count && !--o_notif_count) + quit(0); +} + +static void __notification(struct nb_cb_notify_args *args) +{ + zlog_notice("Received YANG notification: op: %u", args->op); + + if (args->op != NOTIFY_OP_NOTIFICATION) { + zlog_warn("ignoring datastore notification: op: %u: path %s", args->op, args->xpath); + return; + } + + /* bogus, we should print the actual data */ printf("{\"frr-ripd:authentication-failure\": {\"interface-name\": \"%s\"}}\n", yang_dnode_get_string(args->dnode, "interface-name")); @@ -176,6 +241,14 @@ static void async_notification(struct nb_cb_notify_args *args) quit(0); } +static void async_notification(struct nb_cb_notify_args *args) +{ + if (f_datastore) + __ds_notification(args); + else + __notification(args); +} + static int rpc_callback(struct nb_cb_rpc_args *args) { const char *vrf = NULL; @@ -210,6 +283,9 @@ int main(int argc, char **argv) break; switch (opt) { + case OPTION_DATASTORE: + f_datastore = 1; + break; case OPTION_LISTEN: f_listen = 1; break; @@ -228,6 +304,9 @@ int main(int argc, char **argv) master = frr_init(); + mgmt_be_client_lib_vty_init(); + mgmt_dbg_be_client.flags = DEBUG_MODE_ALL; + /* * Setup notification listen */ diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c index 213ee8c1fd..cfc13fcc53 100644 --- a/ospfd/ospf_api.c +++ b/ospfd/ospf_api.c @@ -514,6 +514,12 @@ struct msg *new_msg_originate_request(uint32_t seqnum, struct in_addr ifaddr, omsglen += sizeof(struct msg_originate_request) - sizeof(struct lsa_header); + if (omsglen > UINT16_MAX) { + zlog_warn("%s: LSA specified is bigger than maximum LSA size, something is wrong", + __func__); + omsglen = UINT16_MAX; + } + return msg_new(MSG_ORIGINATE_REQUEST, omsg, seqnum, omsglen); } @@ -639,6 +645,12 @@ struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum, memcpy(nmsg_data, data, len); len += sizeof(struct msg_lsa_change_notify) - sizeof(struct lsa_header); + if (len > UINT16_MAX) { + zlog_warn("%s: LSA specified is bigger than maximum LSA size, something is wrong", + __func__); + len = UINT16_MAX; + } + return msg_new(msgtype, nmsg, seqnum, len); } @@ -666,6 +678,12 @@ struct msg *new_msg_reachable_change(uint32_t seqnum, uint16_t nadd, nmsg->nremove = htons(nremove); len = sizeof(*nmsg) + insz * (nadd + nremove); + if (len > UINT16_MAX) { + zlog_warn("%s: LSA specified is bigger than maximum LSA size, something is wrong", + __func__); + len = UINT16_MAX; + } + return msg_new(MSG_REACHABLE_CHANGE, nmsg, seqnum, len); } diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index b718d498ae..f45135f44f 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -304,6 +304,27 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p, if (api.nexthop_num >= ospf->max_multipath) break; + /* + * Prune duplicate next-hops from the route that is + * installed in the zebra IP route table. OSPF Intra-Area + * routes never have duplicates. + */ + if (or->path_type != OSPF_PATH_INTRA_AREA) { + struct zapi_nexthop *api_nh = &api.nexthops[0]; + unsigned int nh_index; + bool duplicate_next_hop = false; + + for (nh_index = 0; nh_index < api.nexthop_num; api_nh++, nh_index++) { + if (IPV4_ADDR_SAME(&api_nh->gate.ipv4, &path->nexthop) && + (api_nh->ifindex == path->ifindex)) { + duplicate_next_hop = true; + break; + } + } + if (duplicate_next_hop) + continue; + } + ospf_zebra_add_nexthop(ospf, path, &api); if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index ff252f8505..d5cee5f3e4 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -493,7 +493,7 @@ void pbr_nht_change_group(const char *name) } for (ALL_NEXTHOPS(nhgc->nhg, nhop)) { - struct pbr_nexthop_cache lookup; + struct pbr_nexthop_cache lookup = {}; struct pbr_nexthop_cache *pnhc; lookup.nexthop = *nhop; @@ -565,7 +565,7 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms, struct pbr_nexthop_group_cache *pnhgc; struct pbr_nexthop_group_cache find; struct pbr_nexthop_cache *pnhc; - struct pbr_nexthop_cache lookup; + struct pbr_nexthop_cache lookup = {}; struct nexthop *nh; char buf[PBR_NHC_NAMELEN]; @@ -624,7 +624,7 @@ static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence *pbrms) struct pbr_nexthop_group_cache *pnhgc; struct pbr_nexthop_group_cache find; struct pbr_nexthop_cache *pnhc; - struct pbr_nexthop_cache lup; + struct pbr_nexthop_cache lup = {}; struct nexthop *nh; enum nexthop_types_t nh_type = 0; @@ -690,7 +690,7 @@ struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name) DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __func__, pnhgc); for (ALL_NEXTHOPS(nhgc->nhg, nhop)) { - struct pbr_nexthop_cache lookupc; + struct pbr_nexthop_cache lookupc = {}; struct pbr_nexthop_cache *pnhc; lookupc.nexthop = *nhop; diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 08fe56c7bb..aa98913571 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -1488,7 +1488,7 @@ pbrms_nexthop_group_write_individual_nexthop( { struct pbr_nexthop_group_cache find; struct pbr_nexthop_group_cache *pnhgc; - struct pbr_nexthop_cache lookup; + struct pbr_nexthop_cache lookup = {}; struct pbr_nexthop_cache *pnhc; memset(&find, 0, sizeof(find)); diff --git a/pimd/pim_autorp.c b/pimd/pim_autorp.c index caed914a87..dc077dbbd6 100644 --- a/pimd/pim_autorp.c +++ b/pimd/pim_autorp.c @@ -113,6 +113,12 @@ static void pim_autorp_free(struct pim_autorp *autorp) XFREE(MTYPE_PIM_AUTORP_ANNOUNCE, autorp->announce_pkt); } +static bool pim_autorp_should_close(struct pim_autorp *autorp) +{ + /* If discovery or mapping agent is active, then we need the socket open */ + return !autorp->do_discovery && !autorp->send_rp_discovery; +} + static bool pim_autorp_join_groups(struct interface *ifp) { struct pim_interface *pim_ifp; @@ -670,10 +676,19 @@ static void autorp_send_discovery(struct event *evt) &(autorp->send_discovery_timer)); } +static bool pim_autorp_socket_enable(struct pim_autorp *autorp); +static bool pim_autorp_socket_disable(struct pim_autorp *autorp); + static void autorp_send_discovery_on(struct pim_autorp *autorp) { int interval = 5; + /* Make sure the socket is open and ready */ + if (!pim_autorp_socket_enable(autorp)) { + zlog_err("%s: AutoRP failed to open socket", __func__); + return; + } + /* Send the first discovery shortly after being enabled. * If the configured interval is less than 5 seconds, then just use that. */ @@ -695,6 +710,10 @@ static void autorp_send_discovery_off(struct pim_autorp *autorp) if (PIM_DEBUG_AUTORP) zlog_debug("%s: AutoRP discovery sending disabled", __func__); event_cancel(&(autorp->send_discovery_timer)); + + /* Close the socket if we need to */ + if (pim_autorp_should_close(autorp) && !pim_autorp_socket_disable(autorp)) + zlog_warn("%s: AutoRP failed to close socket", __func__); } static bool autorp_recv_discovery(struct pim_autorp *autorp, uint8_t rpcnt, uint16_t holdtime, @@ -949,6 +968,10 @@ static bool pim_autorp_socket_enable(struct pim_autorp *autorp) { int fd; + /* Return early if socket is already enabled */ + if (autorp->sock != -1) + return true; + frr_with_privs (&pimd_privs) { fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (fd < 0) { @@ -975,6 +998,10 @@ static bool pim_autorp_socket_enable(struct pim_autorp *autorp) static bool pim_autorp_socket_disable(struct pim_autorp *autorp) { + /* Return early if socket is already disabled */ + if (autorp->sock == -1) + return true; + if (close(autorp->sock)) { zlog_warn("Failure closing autorp socket: fd=%d errno=%d: %s", autorp->sock, errno, safe_strerror(errno)); @@ -1453,6 +1480,12 @@ void pim_autorp_start_discovery(struct pim_instance *pim) struct interface *ifp; struct pim_autorp *autorp = pim->autorp; + /* Make sure the socket is open and ready */ + if (!pim_autorp_socket_enable(autorp)) { + zlog_err("%s: AutoRP failed to open socket", __func__); + return; + } + if (!autorp->do_discovery) { autorp->do_discovery = true; autorp_read_on(autorp); @@ -1482,6 +1515,10 @@ void pim_autorp_stop_discovery(struct pim_instance *pim) if (PIM_DEBUG_AUTORP) zlog_debug("%s: AutoRP Discovery stopped", __func__); } + + /* Close the socket if we need to */ + if (pim_autorp_should_close(autorp) && !pim_autorp_socket_disable(autorp)) + zlog_warn("%s: AutoRP failed to close socket", __func__); } void pim_autorp_init(struct pim_instance *pim) @@ -1510,12 +1547,6 @@ void pim_autorp_init(struct pim_instance *pim) pim->autorp = autorp; - if (!pim_autorp_socket_enable(autorp)) { - zlog_warn("%s: AutoRP failed to initialize, feature will not work correctly", - __func__); - return; - } - if (PIM_DEBUG_AUTORP) zlog_debug("%s: AutoRP Initialized", __func__); diff --git a/pimd/pim_cmd_common.c b/pimd/pim_cmd_common.c index 8aebce7d27..9f09852a94 100644 --- a/pimd/pim_cmd_common.c +++ b/pimd/pim_cmd_common.c @@ -608,26 +608,14 @@ int pim_process_no_rp_plist_cmd(struct vty *vty, const char *rp_str, int pim_process_autorp_cmd(struct vty *vty) { - char xpath[XPATH_MAXLEN]; - - snprintf(xpath, sizeof(xpath), "%s/%s", FRR_PIM_AUTORP_XPATH, - "discovery-enabled"); - - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "true"); - - return nb_cli_apply_changes(vty, NULL); + nb_cli_enqueue_change(vty, "./discovery-enabled", NB_OP_MODIFY, "true"); + return nb_cli_apply_changes(vty, "%s", FRR_PIM_AUTORP_XPATH); } int pim_process_no_autorp_cmd(struct vty *vty) { - char xpath[XPATH_MAXLEN]; - - snprintf(xpath, sizeof(xpath), "%s/%s", FRR_PIM_AUTORP_XPATH, - "discovery-enabled"); - - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); + nb_cli_enqueue_change(vty, "./discovery-enabled", NB_OP_MODIFY, "false"); + return nb_cli_apply_changes(vty, "%s", FRR_PIM_AUTORP_XPATH); } int pim_process_autorp_candidate_rp_cmd(struct vty *vty, bool no, const char *rpaddr_str, diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 1791502b94..d55d2a958a 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -239,10 +239,16 @@ void pim_ifchannel_delete_all(struct interface *ifp) void delete_on_noinfo(struct pim_ifchannel *ch) { - if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO - && ch->ifjoin_state == PIM_IFJOIN_NOINFO - && ch->t_ifjoin_expiry_timer == NULL) + struct pim_upstream *up = ch->upstream; + /* + * (S,G) with no active traffic, KAT expires, PPT expries, + * channel state is NoInfo + */ + if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO && + ch->ifjoin_state == PIM_IFJOIN_NOINFO && + (ch->t_ifjoin_expiry_timer == NULL || (up && !pim_upstream_is_kat_running(up)))) { pim_ifchannel_delete(ch); + } } void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, diff --git a/pimd/pim_memory.c b/pimd/pim_memory.c index 2c35bc6473..f918cbd146 100644 --- a/pimd/pim_memory.c +++ b/pimd/pim_memory.c @@ -26,6 +26,7 @@ DEFINE_MTYPE(PIMD, PIM_STATIC_ROUTE, "PIM Static Route"); DEFINE_MTYPE(PIMD, PIM_RP, "PIM RP info"); DEFINE_MTYPE(PIMD, PIM_FILTER_NAME, "PIM RP filter info"); DEFINE_MTYPE(PIMD, PIM_MSDP_PEER, "PIM MSDP peer"); +DEFINE_MTYPE(PIMD, PIM_MSDP_FILTER_NAME, "PIM MSDP peer filter name"); DEFINE_MTYPE(PIMD, PIM_MSDP_MG_NAME, "PIM MSDP mesh-group name"); DEFINE_MTYPE(PIMD, PIM_MSDP_AUTH_KEY, "PIM MSDP authentication key"); DEFINE_MTYPE(PIMD, PIM_MSDP_SA, "PIM MSDP source-active cache"); diff --git a/pimd/pim_memory.h b/pimd/pim_memory.h index b44d3e191a..5c9bdad50c 100644 --- a/pimd/pim_memory.h +++ b/pimd/pim_memory.h @@ -25,6 +25,7 @@ DECLARE_MTYPE(PIM_STATIC_ROUTE); DECLARE_MTYPE(PIM_RP); DECLARE_MTYPE(PIM_FILTER_NAME); DECLARE_MTYPE(PIM_MSDP_PEER); +DECLARE_MTYPE(PIM_MSDP_FILTER_NAME); DECLARE_MTYPE(PIM_MSDP_MG_NAME); DECLARE_MTYPE(PIM_MSDP_SA); DECLARE_MTYPE(PIM_MSDP_MG); diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 46d5f4881f..1fd27d383e 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -1095,6 +1095,8 @@ static void pim_msdp_peer_free(struct pim_msdp_peer *mp) if (mp->auth_listen_sock != -1) close(mp->auth_listen_sock); + XFREE(MTYPE_PIM_MSDP_FILTER_NAME, mp->acl_in); + XFREE(MTYPE_PIM_MSDP_FILTER_NAME, mp->acl_out); XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name); mp->pim = NULL; diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index 51f0615884..5203f78b92 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -1564,8 +1564,8 @@ int pim_msdp_peer_sa_filter_in_modify(struct nb_cb_modify_args *args) break; case NB_EV_APPLY: mp = nb_running_get_entry(args->dnode, NULL, true); - XFREE(MTYPE_TMP, mp->acl_in); - mp->acl_in = XSTRDUP(MTYPE_TMP, + XFREE(MTYPE_PIM_MSDP_FILTER_NAME, mp->acl_in); + mp->acl_in = XSTRDUP(MTYPE_PIM_MSDP_FILTER_NAME, yang_dnode_get_string(args->dnode, NULL)); break; } @@ -1585,7 +1585,7 @@ int pim_msdp_peer_sa_filter_in_destroy(struct nb_cb_destroy_args *args) break; case NB_EV_APPLY: mp = nb_running_get_entry(args->dnode, NULL, true); - XFREE(MTYPE_TMP, mp->acl_in); + XFREE(MTYPE_PIM_MSDP_FILTER_NAME, mp->acl_in); break; } @@ -1608,8 +1608,8 @@ int pim_msdp_peer_sa_filter_out_modify(struct nb_cb_modify_args *args) break; case NB_EV_APPLY: mp = nb_running_get_entry(args->dnode, NULL, true); - XFREE(MTYPE_TMP, mp->acl_out); - mp->acl_out = XSTRDUP(MTYPE_TMP, + XFREE(MTYPE_PIM_MSDP_FILTER_NAME, mp->acl_out); + mp->acl_out = XSTRDUP(MTYPE_PIM_MSDP_FILTER_NAME, yang_dnode_get_string(args->dnode, NULL)); break; } @@ -1629,7 +1629,7 @@ int pim_msdp_peer_sa_filter_out_destroy(struct nb_cb_destroy_args *args) break; case NB_EV_APPLY: mp = nb_running_get_entry(args->dnode, NULL, true); - XFREE(MTYPE_TMP, mp->acl_out); + XFREE(MTYPE_PIM_MSDP_FILTER_NAME, mp->acl_out); break; } @@ -2043,6 +2043,11 @@ int lib_interface_pim_address_family_pim_enable_modify(struct nb_cb_modify_args ifp->name); return NB_ERR_INCONSISTENCY; } + + /* Trigger election in case it was never run before */ + pim_ifp = ifp->info; + if (pim_addr_is_any(pim_ifp->pim_dr_addr)) + pim_if_dr_election(ifp); } else { pim_ifp = ifp->info; if (!pim_ifp) @@ -2076,6 +2081,10 @@ int lib_interface_pim_address_family_pim_passive_enable_modify( pim_ifp = ifp->info; pim_ifp->pim_passive_enable = yang_dnode_get_bool(args->dnode, NULL); + + /* Trigger election in case it was never run before */ + if (pim_ifp->pim_passive_enable && pim_addr_is_any(pim_ifp->pim_dr_addr)) + pim_if_dr_election(ifp); break; } diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index c52119e43a..01e1321b25 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -312,7 +312,7 @@ static void on_join_timer(struct event *t) } /* - * In the case of a HFR we will not ahve anyone to send this to. + * In the case of a FHR we will not ahve anyone to send this to. */ if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) return; diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 1ba8f043b2..4e0196d24a 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -31,6 +31,8 @@ %{!?with_watchfrr: %global with_watchfrr 1 } %{!?with_pathd: %global with_pathd 1 } %{!?with_grpc: %global with_grpc 0 } +%{!?with_rpki: %global with_rpki 1 } +%{!?with_docs: %global with_docs 1 } # user and group %{!?frr_user: %global frr_user frr } @@ -189,20 +191,29 @@ BuildRequires: ncurses-devel BuildRequires: readline-devel BuildRequires: texinfo BuildRequires: libyang-devel >= 2.1.128 +# Version requirement don't get reflected down from a BuildRequire +# to Require, so need to require libyang version as both ways +Requires: libyang >= 2.1.128 BuildRequires: pcre2-devel %if 0%{?rhel} && 0%{?rhel} < 7 #python27-devel is available from ius community repo for RedHat/CentOS 6 BuildRequires: python27-devel +%if %{with_docs} BuildRequires: python27-sphinx +%endif %else %if %{use_python2} BuildRequires: python-devel >= 2.7 +%if %{with_docs} BuildRequires: python-sphinx +%endif %else BuildRequires: python3-devel +%if %{with_docs} BuildRequires: python3-sphinx %endif %endif +%endif %if %{with_grpc} BuildRequires: grpc-devel >= 1.16.1 BuildRequires: protobuf-devel >= 3.6.1 @@ -283,6 +294,7 @@ The frr-devel package contains the header and object files necessary for developing OSPF-API and frr applications. +%if %{with_rpki} %package rpki-rtrlib Summary: BGP RPKI support (rtrlib) Group: System Environment/Daemons @@ -295,6 +307,7 @@ against cryptographic information stored in WHOIS databases. This is used to prevent hijacking of networks on the wider internet. It is only relevant to internet service providers using their own autonomous system number. +%endif %package snmp @@ -433,7 +446,9 @@ Adds GRPC support to the individual FRR daemons. --disable-bgp-vnc \ %endif --enable-isisd \ +%if %{with_rpki} --enable-rpki \ +%endif %if %{with_bfdd} --enable-bfdd \ %else @@ -465,9 +480,11 @@ sed -e '1c #!/usr/bin/python3' -i %{zeb_src}/tools/frr-reload.py sed -e '1c #!/usr/bin/python3' -i %{zeb_src}/tools/generate_support_bundle.py %endif +%if %{with_docs} pushd doc make info popd +%endif %install @@ -605,7 +622,9 @@ zebra_spec_add_service fabricd 2618/tcp "Fabricd vty" %__sed -i 's|watchfrr_enable=no|watchfrr_enable=yes|g' %{configdir}/daemons 2> /dev/null || true %endif +%if %{with_docs} /sbin/install-info %{_infodir}/frr.info.gz %{_infodir}/dir +%endif # Create dummy config file if they don't exist so basic functions can be used. if [ ! -e %{configdir}/frr.conf ] && [ ! -e %{configdir}/zebra.conf ]; then @@ -673,7 +692,9 @@ fi /sbin/chkconfig --del frr fi %endif +%if %{with_docs} /sbin/install-info --delete %{_infodir}/frr.info.gz %{_infodir}/dir +%endif %files @@ -690,8 +711,10 @@ fi %dir %attr(755,root,root) %{_localstatedir}/log/frr %dir %attr(750,root,root) %{_runstatedir}/frr %endif -%{_infodir}/frr.info.gz -%{_mandir}/man*/* +%if %{with_docs} + %{_infodir}/frr.info.gz + %{_mandir}/man*/* +%endif %{_sbindir}/zebra %{_sbindir}/staticd %{_sbindir}/ospfd @@ -739,19 +762,9 @@ fi %endif %if %{with_pathd} %{_sbindir}/pathd - %{_libdir}/frr/modules/pathd_pcep.so %endif -%{_libdir}/libfrr.so* -%{_libdir}/libfrrcares* -%{_libdir}/libfrrospf* -%if %{with_fpm} - %{_libdir}/frr/modules/zebra_fpm.so -%endif -%{_libdir}/frr/modules/zebra_cumulus_mlag.so -%{_libdir}/frr/modules/dplane_fpm_nl.so -%{_libdir}/frr/modules/bgpd_bmp.so -%{_libdir}/libfrr_pb.so* -%{_libdir}/libfrrfpm_pb.so* +%{_libdir}/libfrr*.so* +%{_libdir}/frr/modules/*.so %{_libdir}/libmgmt_be_nb.so* %{_bindir}/* %config(noreplace) %{configdir}/[!v]*.conf* @@ -791,6 +804,7 @@ fi %endif +%if %{with_rpki} %post rpki-rtrlib # add rpki module to daemons sed -i -e 's/^\(bgpd_options=\)\(.*\)\(".*\)/\1\2 -M rpki\3/' %{_sysconfdir}/frr/daemons @@ -798,22 +812,9 @@ sed -i -e 's/^\(bgpd_options=\)\(.*\)\(".*\)/\1\2 -M rpki\3/' %{_sysconfdir}/frr %postun rpki-rtrlib # remove rpki module from daemons sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons - -%files rpki-rtrlib -%{_libdir}/frr/modules/bgpd_rpki.so - - -%files snmp -%{_libdir}/libfrrsnmp.so* -%{_libdir}/frr/modules/*snmp.so - - -%if %{with_grpc} -%files grpc -%{_libdir}/libfrrgrpc_pb.* -%{_libdir}/frr/modules/grpc.so %endif + %files devel %{_libdir}/lib*.so %dir %{_includedir}/%{name} diff --git a/staticd/static_debug.c b/staticd/static_debug.c index 618ba91d12..b308860424 100644 --- a/staticd/static_debug.c +++ b/staticd/static_debug.c @@ -22,6 +22,7 @@ struct debug static_dbg_events = {0, "debug static events", "Staticd events"}; struct debug static_dbg_route = {0, "debug static route", "Staticd route"}; struct debug static_dbg_bfd = {0, "debug static bfd", "Staticd bfd"}; +struct debug static_dbg_srv6 = {0, "debug static srv6", "Staticd srv6"}; /* clang-format on */ /* @@ -37,8 +38,7 @@ struct debug static_dbg_bfd = {0, "debug static bfd", "Staticd bfd"}; * Debug general internal events * */ -void static_debug_set(int vtynode, bool onoff, bool events, bool route, - bool bfd) +void static_debug_set(int vtynode, bool onoff, bool events, bool route, bool bfd, bool srv6) { uint32_t mode = DEBUG_NODE2MODE(vtynode); @@ -50,6 +50,8 @@ void static_debug_set(int vtynode, bool onoff, bool events, bool route, DEBUG_MODE_SET(&static_dbg_bfd, mode, onoff); bfd_protocol_integration_set_debug(onoff); } + if (srv6) + DEBUG_MODE_SET(&static_dbg_srv6, mode, onoff); } /* @@ -61,4 +63,5 @@ void static_debug_init(void) debug_install(&static_dbg_events); debug_install(&static_dbg_route); debug_install(&static_dbg_bfd); + debug_install(&static_dbg_srv6); } diff --git a/staticd/static_debug.h b/staticd/static_debug.h index b990f7bcc9..a16e398eba 100644 --- a/staticd/static_debug.h +++ b/staticd/static_debug.h @@ -20,6 +20,7 @@ extern "C" { extern struct debug static_dbg_events; extern struct debug static_dbg_route; extern struct debug static_dbg_bfd; +extern struct debug static_dbg_srv6; /* * Initialize staticd debugging. @@ -41,8 +42,7 @@ void static_debug_init(void); * Debug general internal events * */ -void static_debug_set(int vtynode, bool onoff, bool events, bool route, - bool bfd); +void static_debug_set(int vtynode, bool onoff, bool events, bool route, bool bfd, bool srv6); #ifdef __cplusplus } diff --git a/staticd/static_main.c b/staticd/static_main.c index 5e74326e38..3b59ca6a75 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -26,6 +26,7 @@ #include "static_zebra.h" #include "static_debug.h" #include "static_nb.h" +#include "static_srv6.h" #include "mgmt_be_client.h" @@ -76,6 +77,10 @@ static void sigint(void) static_vrf_terminate(); static_zebra_stop(); + + /* clean up SRv6 data structures */ + static_srv6_cleanup(); + frr_fini(); exit(0); @@ -161,6 +166,9 @@ int main(int argc, char **argv, char **envp) static_debug_init(); static_vrf_init(); + /* initialize SRv6 data structures */ + static_srv6_init(); + static_zebra_init(); static_vty_init(); diff --git a/staticd/static_nb.c b/staticd/static_nb.c index e6aa71a77b..ef363bfe7e 100644 --- a/staticd/static_nb.c +++ b/staticd/static_nb.c @@ -135,93 +135,32 @@ const struct frr_yang_module_info frr_staticd_info = { } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids/sid", .cbs = { - .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy, + .apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_apply_finish, + .create = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_destroy, } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids/sid/behavior", .cbs = { - .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy, + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_behavior_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_behavior_destroy, } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/tag", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids/sid/vrf-name", .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify, + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_vrf_name_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_vrf_name_destroy, } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids/sid/locator-name", .cbs = { - .apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish, - .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy, - .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/bh-type", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry", - .cbs = { - .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry", - .cbs = { - .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy, + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_locator_name_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_locator_name_destroy, } }, { diff --git a/staticd/static_nb.h b/staticd/static_nb.h index be75d9d38c..aa11f34021 100644 --- a/staticd/static_nb.h +++ b/staticd/static_nb.h @@ -72,58 +72,40 @@ int route_next_hop_bfd_source_destroy(struct nb_cb_destroy_args *args); int route_next_hop_bfd_profile_modify(struct nb_cb_modify_args *args); int route_next_hop_bfd_profile_destroy(struct nb_cb_destroy_args *args); int route_next_hop_bfd_multi_hop_modify(struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_create( struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_destroy( struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_create( struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_destroy( struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create( - struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_create( struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_destroy( struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_create( struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_destroy( struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_behavior_modify( struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_behavior_destroy( struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_vrf_name_modify( struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_vrf_name_destroy( struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_locator_name_modify( struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_locator_name_destroy( struct nb_cb_destroy_args *args); /* Optional 'apply_finish' callbacks. */ void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_apply_finish( struct nb_cb_apply_finish_args *args); -void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish( +void routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_apply_finish( struct nb_cb_apply_finish_args *args); /* Optional 'pre_validate' callbacks. */ @@ -139,16 +121,16 @@ int routing_control_plane_protocols_name_validate( /* xpath macros */ /* route-list */ -#define FRR_STATIC_ROUTE_INFO_KEY_XPATH \ - "/frr-routing:routing/control-plane-protocols/" \ - "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ - "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \ +#define FRR_STATIC_ROUTE_INFO_KEY_XPATH \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ + "frr-staticd:staticd/route-list[prefix='%s'][src-prefix='%s'][afi-safi='%s']/" \ "path-list[table-id='%u'][distance='%u']" -#define FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH \ - "/frr-routing:routing/control-plane-protocols/" \ - "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ - "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \ +#define FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ + "frr-staticd:staticd/route-list[prefix='%s'][src-prefix='%s'][afi-safi='%s']/" \ "path-list[table-id='%u']" @@ -173,19 +155,6 @@ int routing_control_plane_protocols_name_validate( #define FRR_STATIC_ROUTE_NH_SRV6_KEY_SEG_XPATH "/entry[id='%u']/seg" -/* route-list/srclist */ -#define FRR_S_ROUTE_SRC_INFO_KEY_XPATH \ - "/frr-routing:routing/control-plane-protocols/" \ - "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ - "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \ - "src-list[src-prefix='%s']/path-list[table-id='%u'][distance='%u']" - -#define FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH \ - "/frr-routing:routing/control-plane-protocols/" \ - "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ - "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \ - "src-list[src-prefix='%s']/path-list[table-id='%u']" - /* route-list/frr-nexthops */ #define FRR_DEL_S_ROUTE_NH_KEY_XPATH \ FRR_STATIC_ROUTE_INFO_KEY_XPATH \ @@ -196,15 +165,23 @@ int routing_control_plane_protocols_name_validate( FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH \ FRR_STATIC_ROUTE_NH_KEY_XPATH -/* route-list/src/src-list/frr-nexthops*/ -#define FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH \ - FRR_S_ROUTE_SRC_INFO_KEY_XPATH \ - FRR_STATIC_ROUTE_NH_KEY_XPATH +/* srv6 */ +#define FRR_STATIC_SRV6_INFO_KEY_XPATH \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ + "frr-staticd:staticd/segment-routing/srv6" -/* route-list/src/src-list/frr-nexthops*/ -#define FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH \ - FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH \ - FRR_STATIC_ROUTE_NH_KEY_XPATH +/* srv6/static-sids */ +#define FRR_STATIC_SRV6_SID_KEY_XPATH \ + FRR_STATIC_SRV6_INFO_KEY_XPATH \ + "/static-sids/" \ + "sid[sid='%s']" + +#define FRR_STATIC_SRV6_SID_BEHAVIOR_XPATH "/behavior" + +#define FRR_STATIC_SRV6_SID_VRF_NAME_XPATH "/vrf-name" + +#define FRR_STATIC_SRV6_SID_LOCATOR_NAME_XPATH "/locator-name" #ifdef __cplusplus } diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c index 7de5f0474a..e2ab1f2ffe 100644 --- a/staticd/static_nb_config.c +++ b/staticd/static_nb_config.c @@ -20,6 +20,9 @@ #include "static_nb.h" #include "static_zebra.h" +#include "static_srv6.h" +#include "static_debug.h" + static int static_path_list_create(struct nb_cb_create_args *args) { @@ -499,16 +502,6 @@ void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_p static_install_nexthop(nh); } -void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish( - struct nb_cb_apply_finish_args *args) -{ - struct static_nexthop *nh; - - nh = nb_running_get_entry(args->dnode, NULL, true); - - static_install_nexthop(nh); -} - int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate( struct nb_cb_pre_validate_args *args) { @@ -573,7 +566,7 @@ int routing_control_plane_protocols_staticd_destroy( if (!stable) continue; - for (rn = route_top(stable); rn; rn = route_next(rn)) + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) static_del_route(rn); } @@ -592,7 +585,7 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr struct static_vrf *svrf; struct route_node *rn; const struct lyd_node *vrf_dnode; - struct prefix prefix; + struct prefix prefix, src_prefix, *src_p; const char *afi_safi; afi_t prefix_afi; afi_t afi; @@ -601,6 +594,8 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr switch (args->event) { case NB_EV_VALIDATE: yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); + yang_dnode_get_prefix(&src_prefix, args->dnode, "src-prefix"); + src_p = src_prefix.prefixlen ? &src_prefix : NULL; afi_safi = yang_dnode_get_string(args->dnode, "afi-safi"); yang_afi_safi_identity2value(afi_safi, &afi, &safi); prefix_afi = family2afi(prefix.family); @@ -611,6 +606,14 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr yang_dnode_get_string(args->dnode, "prefix")); return NB_ERR_VALIDATION; } + + if (src_p && afi != AFI_IP6) { + flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, + "invalid use of IPv6 dst-src prefix %s on %s", + yang_dnode_get_string(args->dnode, "src-prefix"), + yang_dnode_get_string(args->dnode, "prefix")); + return NB_ERR_VALIDATION; + } break; case NB_EV_PREPARE: case NB_EV_ABORT: @@ -621,10 +624,12 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr svrf = nb_running_get_entry(vrf_dnode, NULL, true); yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); + yang_dnode_get_prefix(&src_prefix, args->dnode, "src-prefix"); + src_p = src_prefix.prefixlen ? &src_prefix : NULL; afi_safi = yang_dnode_get_string(args->dnode, "afi-safi"); yang_afi_safi_identity2value(afi_safi, &afi, &safi); - rn = static_add_route(afi, safi, &prefix, NULL, svrf); + rn = static_add_route(afi, safi, &prefix, (struct prefix_ipv6 *)src_p, svrf); if (!svrf->vrf || svrf->vrf->vrf_id == VRF_UNKNOWN) snprintf( args->errmsg, args->errmsg_len, @@ -1045,325 +1050,219 @@ int route_next_hop_bfd_profile_destroy(struct nb_cb_destroy_args *args) /* * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_create( struct nb_cb_create_args *args) { - struct static_vrf *s_vrf; - struct route_node *rn; - struct route_node *src_rn; - struct prefix_ipv6 src_prefix = {}; - struct stable_info *info; - afi_t afi; - safi_t safi = SAFI_UNICAST; - - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - rn = nb_running_get_entry(args->dnode, NULL, true); - info = route_table_get_info(rn->table); - s_vrf = info->svrf; - yang_dnode_get_ipv6p(&src_prefix, args->dnode, "src-prefix"); - afi = family2afi(src_prefix.family); - src_rn = - static_add_route(afi, safi, &rn->p, &src_prefix, s_vrf); - nb_running_set_entry(args->dnode, src_rn); - break; - } return NB_OK; } -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_destroy( struct nb_cb_destroy_args *args) { - struct route_node *src_rn; - - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - src_rn = nb_running_unset_entry(args->dnode); - static_del_route(src_rn); - break; - } - return NB_OK; } /* * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6 */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_create( struct nb_cb_create_args *args) { - return static_path_list_create(args); + return NB_OK; } -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_destroy( struct nb_cb_destroy_args *args) { - return static_path_list_destroy(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/tag - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify( - struct nb_cb_modify_args *args) -{ - return static_path_list_tag_modify(args); + return NB_OK; } /* * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_create( struct nb_cb_create_args *args) { - return static_nexthop_create(args); + return NB_OK; } -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_destroy( struct nb_cb_destroy_args *args) { - return static_nexthop_destroy(args); + return NB_OK; } /* * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/bh-type + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/locators/locator/static-sids/sid */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify( - struct nb_cb_modify_args *args) +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_create( + struct nb_cb_create_args *args) { - return static_nexthop_bh_type_modify(args); -} + struct static_srv6_sid *sid; + struct prefix_ipv6 sid_value; -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify( - struct nb_cb_modify_args *args) -{ - return static_nexthop_onlink_modify(args); -} + if (args->event != NB_EV_APPLY) + return NB_OK; -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify( - struct nb_cb_modify_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - if (static_nexthop_color_modify(args) != NB_OK) - return NB_ERR; + yang_dnode_get_ipv6p(&sid_value, args->dnode, "sid"); + sid = static_srv6_sid_alloc(&sid_value); + nb_running_set_entry(args->dnode, sid); - break; - } return NB_OK; } - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_destroy( struct nb_cb_destroy_args *args) { - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - if (static_nexthop_color_destroy(args) != NB_OK) - return NB_ERR; - break; - } - return NB_OK; -} + struct static_srv6_sid *sid; -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( - struct nb_cb_create_args *args) -{ - return nexthop_srv6_segs_stack_entry_create(args); -} + if (args->event != NB_EV_APPLY) + return NB_OK; -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( - struct nb_cb_destroy_args *args) -{ - return nexthop_srv6_segs_stack_entry_destroy(args); -} + sid = nb_running_unset_entry(args->dnode); + listnode_delete(srv6_sids, sid); + static_srv6_sid_del(sid); -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( - struct nb_cb_modify_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - if (static_nexthop_srv6_segs_modify(args) != NB_OK) - return NB_ERR; - break; - } return NB_OK; } -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( - struct nb_cb_destroy_args *args) +void routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_apply_finish( + struct nb_cb_apply_finish_args *args) { - /* - * No operation is required in this call back. - * nexthop_mpls_seg_stack_entry_destroy() will take care - * to reset the seg vaue. - */ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; + struct static_srv6_sid *sid; + struct static_srv6_locator *locator; + + sid = nb_running_get_entry(args->dnode, NULL, true); + + locator = static_srv6_locator_lookup(sid->locator_name); + if (!locator) { + DEBUGD(&static_dbg_srv6, + "%s: Locator %s not found, trying to get locator information from zebra", + __func__, sid->locator_name); + static_zebra_srv6_manager_get_locator(sid->locator_name); + listnode_add(srv6_sids, sid); + return; } - return NB_OK; -} -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( - struct nb_cb_create_args *args) -{ - return nexthop_mpls_label_stack_entry_create(args); -} + sid->locator = locator; -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( - struct nb_cb_destroy_args *args) -{ - return nexthop_mpls_label_stack_entry_destroy(args); + listnode_add(srv6_sids, sid); + static_zebra_request_srv6_sid(sid); } /* * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/locators/locator/static-sids/sid/behavior */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_behavior_modify( struct nb_cb_modify_args *args) { - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - if (static_nexthop_mpls_label_modify(args) != NB_OK) - return NB_ERR; - break; + struct static_srv6_sid *sid; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + sid = nb_running_get_entry(args->dnode, NULL, true); + + /* Release and uninstall existing SID, if any, before requesting the new one */ + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID)) { + static_zebra_release_srv6_sid(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); + } + + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) { + static_zebra_srv6_sid_uninstall(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); } + + sid->behavior = yang_dnode_get_enum(args->dnode, "../behavior"); + return NB_OK; } -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_behavior_destroy( struct nb_cb_destroy_args *args) { - /* - * No operation is required in this call back. - * nexthop_mpls_label_stack_entry_destroy() will take care - * to reset the label vaue. - */ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; - } return NB_OK; } /* * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/locators/locator/static-sids/sid/vrf-name */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_vrf_name_modify( struct nb_cb_modify_args *args) { - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; + struct static_srv6_sid *sid; + const char *vrf_name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + sid = nb_running_get_entry(args->dnode, NULL, true); + + /* Release and uninstall existing SID, if any, before requesting the new one */ + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID)) { + static_zebra_release_srv6_sid(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); + } + + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) { + static_zebra_srv6_sid_uninstall(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); } + vrf_name = yang_dnode_get_string(args->dnode, "../vrf-name"); + snprintf(sid->attributes.vrf_name, sizeof(sid->attributes.vrf_name), "%s", vrf_name); + return NB_OK; } -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_vrf_name_destroy( struct nb_cb_destroy_args *args) { - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; - } - return NB_OK; } /* * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/locators/locator/static-sids/sid/vrf-name */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_locator_name_modify( struct nb_cb_modify_args *args) { - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; + struct static_srv6_sid *sid; + const char *loc_name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + sid = nb_running_get_entry(args->dnode, NULL, true); + + /* Release and uninstall existing SID, if any, before requesting the new one */ + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID)) { + static_zebra_release_srv6_sid(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); + } + + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) { + static_zebra_srv6_sid_uninstall(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); } + loc_name = yang_dnode_get_string(args->dnode, "../locator-name"); + snprintf(sid->locator_name, sizeof(sid->locator_name), "%s", loc_name); + return NB_OK; } -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy( +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_locator_name_destroy( struct nb_cb_destroy_args *args) { - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; - } - return NB_OK; } diff --git a/staticd/static_nht.c b/staticd/static_nht.c index 06d27c6f59..367ee85040 100644 --- a/staticd/static_nht.c +++ b/staticd/static_nht.c @@ -49,8 +49,8 @@ static void static_nht_update_path(struct static_path *pn, struct prefix *nhp, static_zebra_route_add(pn, true); } -static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, - uint32_t nh_num, afi_t afi, safi_t safi, +static void static_nht_update_safi(const struct prefix *sp, const struct prefix *ssrc_p, + struct prefix *nhp, uint32_t nh_num, afi_t afi, safi_t safi, struct static_vrf *svrf, vrf_id_t nh_vrf_id) { struct route_table *stable; @@ -63,7 +63,7 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, return; if (sp) { - rn = srcdest_rnode_lookup(stable, sp, NULL); + rn = srcdest_rnode_lookup(stable, sp, (const struct prefix_ipv6 *)ssrc_p); if (rn && rn->info) { si = static_route_info_from_rnode(rn); frr_each(static_path_list, &si->path_list, pn) { @@ -75,7 +75,7 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, return; } - for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; @@ -85,14 +85,13 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, } } -void static_nht_update(struct prefix *sp, struct prefix *nhp, uint32_t nh_num, - afi_t afi, safi_t safi, vrf_id_t nh_vrf_id) +void static_nht_update(const struct prefix *sp, const struct prefix *ssrc_p, struct prefix *nhp, + uint32_t nh_num, afi_t afi, safi_t safi, vrf_id_t nh_vrf_id) { struct static_vrf *svrf; RB_FOREACH (svrf, svrf_name_head, &svrfs) - static_nht_update_safi(sp, nhp, nh_num, afi, safi, svrf, - nh_vrf_id); + static_nht_update_safi(sp, ssrc_p, nhp, nh_num, afi, safi, svrf, nh_vrf_id); } static void static_nht_reset_start_safi(struct prefix *nhp, afi_t afi, @@ -109,7 +108,7 @@ static void static_nht_reset_start_safi(struct prefix *nhp, afi_t afi, if (!stable) return; - for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; @@ -150,8 +149,8 @@ void static_nht_reset_start(struct prefix *nhp, afi_t afi, safi_t safi, static_nht_reset_start_safi(nhp, afi, safi, svrf, nh_vrf_id); } -static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi, - safi_t safi, struct vrf *vrf, +static void static_nht_mark_state_safi(const struct prefix *sp, const struct prefix *ssrc_p, + afi_t afi, safi_t safi, struct vrf *vrf, enum static_install_states state) { struct static_vrf *svrf; @@ -169,7 +168,7 @@ static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi, if (!stable) return; - rn = srcdest_rnode_lookup(stable, sp, NULL); + rn = srcdest_rnode_lookup(stable, sp, (const struct prefix_ipv6 *)ssrc_p); if (!rn) return; si = rn->info; @@ -184,8 +183,8 @@ static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi, route_unlock_node(rn); } -void static_nht_mark_state(struct prefix *sp, safi_t safi, vrf_id_t vrf_id, - enum static_install_states state) +void static_nht_mark_state(const struct prefix *sp, const struct prefix *ssrc_p, safi_t safi, + vrf_id_t vrf_id, enum static_install_states state) { struct vrf *vrf; @@ -198,5 +197,5 @@ void static_nht_mark_state(struct prefix *sp, safi_t safi, vrf_id_t vrf_id, if (!vrf || !vrf->info) return; - static_nht_mark_state_safi(sp, afi, safi, vrf, state); + static_nht_mark_state_safi(sp, ssrc_p, afi, safi, vrf, state); } diff --git a/staticd/static_nht.h b/staticd/static_nht.h index 74f4401e49..41ff30cd52 100644 --- a/staticd/static_nht.h +++ b/staticd/static_nht.h @@ -16,15 +16,14 @@ extern "C" { * us call this function to find the nexthop we are tracking so it * can be installed or removed. * - * sp -> The route we are looking at. If NULL then look at all - * routes. + * sp + ssrc_p -> The route we are looking at. If NULL then look at all routes. * nhp -> The nexthop that is being tracked. * nh_num -> number of valid nexthops. * afi -> The afi we are working in. * vrf_id -> The vrf the nexthop is in. */ -extern void static_nht_update(struct prefix *sp, struct prefix *nhp, - uint32_t nh_num, afi_t afi, safi_t safi, +extern void static_nht_update(const struct prefix *sp, const struct prefix *ssrc_p, + struct prefix *nhp, uint32_t nh_num, afi_t afi, safi_t safi, vrf_id_t vrf_id); /* @@ -35,11 +34,10 @@ extern void static_nht_reset_start(struct prefix *nhp, afi_t afi, safi_t safi, vrf_id_t nh_vrf_id); /* - * For the given prefix, sp, mark it as in a particular state + * For the given prefix, sp + ssrc_p, mark it as in a particular state */ -extern void static_nht_mark_state(struct prefix *sp, safi_t safi, - vrf_id_t vrf_id, - enum static_install_states state); +extern void static_nht_mark_state(const struct prefix *sp, const struct prefix *ssrc_p, safi_t safi, + vrf_id_t vrf_id, enum static_install_states state); /* * For the given nexthop, returns the string diff --git a/staticd/static_routes.c b/staticd/static_routes.c index cba38183bb..cbe1c3c8c0 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -33,10 +33,6 @@ void zebra_stable_node_cleanup(struct route_table *table, struct static_nexthop *nh; struct static_path *pn; struct static_route_info *si; - struct route_table *src_table; - struct route_node *src_node; - struct static_path *src_pn; - struct static_route_info *src_si; si = node->info; @@ -50,36 +46,6 @@ void zebra_stable_node_cleanup(struct route_table *table, static_path_list_del(&si->path_list, pn); XFREE(MTYPE_STATIC_PATH, pn); } - - /* clean up for dst table */ - src_table = srcdest_srcnode_table(node); - if (src_table) { - /* This means the route_node is part of the top - * hierarchy and refers to a destination prefix. - */ - for (src_node = route_top(src_table); src_node; - src_node = route_next(src_node)) { - src_si = src_node->info; - - frr_each_safe(static_path_list, - &src_si->path_list, src_pn) { - frr_each_safe(static_nexthop_list, - &src_pn->nexthop_list, - nh) { - static_nexthop_list_del( - &src_pn->nexthop_list, - nh); - XFREE(MTYPE_STATIC_NEXTHOP, nh); - } - static_path_list_del(&src_si->path_list, - src_pn); - XFREE(MTYPE_STATIC_PATH, src_pn); - } - - XFREE(MTYPE_STATIC_ROUTE, src_node->info); - } - } - XFREE(MTYPE_STATIC_ROUTE, node->info); } } @@ -124,28 +90,10 @@ struct route_node *static_add_route(afi_t afi, safi_t safi, struct prefix *p, return rn; } -/* To delete the srcnodes */ -static void static_del_src_route(struct route_node *rn) -{ - struct static_path *pn; - struct static_route_info *si; - - si = rn->info; - - frr_each_safe(static_path_list, &si->path_list, pn) { - static_del_path(pn); - } - - XFREE(MTYPE_STATIC_ROUTE, rn->info); - route_unlock_node(rn); -} - void static_del_route(struct route_node *rn) { struct static_path *pn; struct static_route_info *si; - struct route_table *src_table; - struct route_node *src_node; si = rn->info; @@ -153,17 +101,6 @@ void static_del_route(struct route_node *rn) static_del_path(pn); } - /* clean up for dst table */ - src_table = srcdest_srcnode_table(rn); - if (src_table) { - /* This means the route_node is part of the top hierarchy - * and refers to a destination prefix. - */ - for (src_node = route_top(src_table); src_node; - src_node = route_next(src_node)) { - static_del_src_route(src_node); - } - } XFREE(MTYPE_STATIC_ROUTE, rn->info); route_unlock_node(rn); } @@ -477,7 +414,7 @@ static void static_fixup_vrf(struct vrf *vrf, struct route_table *stable, struct static_path *pn; struct static_route_info *si; - for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; @@ -517,7 +454,7 @@ static void static_enable_vrf(struct route_table *stable, afi_t afi, safi_t safi struct static_path *pn; struct static_route_info *si; - for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; @@ -575,7 +512,7 @@ static void static_cleanup_vrf(struct vrf *vrf, struct route_table *stable, struct static_path *pn; struct static_route_info *si; - for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; @@ -608,7 +545,7 @@ static void static_disable_vrf(struct route_table *stable, struct static_path *pn; struct static_route_info *si; - for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; diff --git a/staticd/static_srv6.c b/staticd/static_srv6.c new file mode 100644 index 0000000000..032bb9de9f --- /dev/null +++ b/staticd/static_srv6.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * STATICd - Segment Routing over IPv6 (SRv6) code + * Copyright (C) 2025 Alibaba Inc. + * Yuqing Zhao + * Lingyu Zhang + */ +#include <zebra.h> + +#include "vrf.h" +#include "nexthop.h" + +#include "static_routes.h" +#include "static_srv6.h" +#include "static_vrf.h" +#include "static_zebra.h" +#include "static_debug.h" + +/* + * List of SRv6 SIDs. + */ +struct list *srv6_locators; +struct list *srv6_sids; + +DEFINE_MTYPE_STATIC(STATIC, STATIC_SRV6_LOCATOR, "Static SRv6 locator"); +DEFINE_MTYPE_STATIC(STATIC, STATIC_SRV6_SID, "Static SRv6 SID"); + +/* + * When an interface is enabled in the kernel, go through all the static SRv6 SIDs in + * the system that use this interface and install/remove them in the zebra RIB. + * + * ifp - The interface being enabled + * is_up - Whether the interface is up or down + */ +void static_ifp_srv6_sids_update(struct interface *ifp, bool is_up) +{ + struct static_srv6_sid *sid; + struct listnode *node; + + if (!srv6_sids || !ifp) + return; + + DEBUGD(&static_dbg_srv6, "%s: Interface %s %s. %s SIDs that depend on the interface", + __func__, (is_up) ? "enabled" : "disabled", (is_up) ? "Removing" : "disabled", + ifp->name); + + /* + * iterate over the list of SRv6 SIDs and remove the SIDs that use this + * VRF from the zebra RIB + */ + for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { + if ((strcmp(sid->attributes.vrf_name, ifp->name) == 0) || + (strncmp(ifp->name, DEFAULT_SRV6_IFNAME, sizeof(ifp->name)) == 0 && + (sid->behavior == SRV6_ENDPOINT_BEHAVIOR_END || + sid->behavior == SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID))) { + if (is_up) { + static_zebra_srv6_sid_install(sid); + SET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + } else { + static_zebra_srv6_sid_uninstall(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + } + } + } +} + +/* + * Allocate an SRv6 SID object and initialize the fields common to all the + * behaviors (i.e., SID address and behavor). + */ +struct static_srv6_sid *static_srv6_sid_alloc(struct prefix_ipv6 *addr) +{ + struct static_srv6_sid *sid = NULL; + + sid = XCALLOC(MTYPE_STATIC_SRV6_SID, sizeof(struct static_srv6_sid)); + sid->addr = *addr; + + return sid; +} + +void static_srv6_sid_free(struct static_srv6_sid *sid) +{ + XFREE(MTYPE_STATIC_SRV6_SID, sid); +} + +struct static_srv6_locator *static_srv6_locator_lookup(const char *name) +{ + struct static_srv6_locator *locator; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(srv6_locators, node, locator)) + if (!strncmp(name, locator->name, SRV6_LOCNAME_SIZE)) + return locator; + return NULL; +} + +/* + * Look-up an SRv6 SID in the list of SRv6 SIDs. + */ +struct static_srv6_sid *static_srv6_sid_lookup(struct prefix_ipv6 *sid_addr) +{ + struct static_srv6_sid *sid; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) + if (memcmp(&sid->addr, sid_addr, sizeof(struct prefix_ipv6)) == 0) + return sid; + + return NULL; +} + +struct static_srv6_locator *static_srv6_locator_alloc(const char *name) +{ + struct static_srv6_locator *locator = NULL; + + locator = XCALLOC(MTYPE_STATIC_SRV6_LOCATOR, sizeof(struct static_srv6_locator)); + strlcpy(locator->name, name, sizeof(locator->name)); + + return locator; +} + +void static_srv6_locator_free(struct static_srv6_locator *locator) +{ + XFREE(MTYPE_STATIC_SRV6_LOCATOR, locator); +} + +void delete_static_srv6_locator(void *val) +{ + static_srv6_locator_free((struct static_srv6_locator *)val); +} + +/* + * Remove an SRv6 SID from the zebra RIB (if it was previously installed) and + * release the memory previously allocated for the SID. + */ +void static_srv6_sid_del(struct static_srv6_sid *sid) +{ + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID)) { + static_zebra_release_srv6_sid(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); + } + + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) { + static_zebra_srv6_sid_uninstall(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + } + + XFREE(MTYPE_STATIC_SRV6_SID, sid); +} + +void delete_static_srv6_sid(void *val) +{ + static_srv6_sid_free((struct static_srv6_sid *)val); +} + +/* + * Initialize SRv6 data structures. + */ +void static_srv6_init(void) +{ + srv6_locators = list_new(); + srv6_locators->del = delete_static_srv6_locator; + srv6_sids = list_new(); + srv6_sids->del = delete_static_srv6_sid; +} + +/* + * Clean up all the SRv6 data structures. + */ +void static_srv6_cleanup(void) +{ + list_delete(&srv6_locators); + list_delete(&srv6_sids); +} diff --git a/staticd/static_srv6.h b/staticd/static_srv6.h new file mode 100644 index 0000000000..48986092ae --- /dev/null +++ b/staticd/static_srv6.h @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * STATICd - Segment Routing over IPv6 (SRv6) header + * Copyright (C) 2025 Alibaba Inc. + * Yuqing Zhao + * Lingyu Zhang + */ +#ifndef __STATIC_SRV6_H__ +#define __STATIC_SRV6_H__ + +#include "vrf.h" +#include "srv6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Attributes for an SRv6 SID */ +struct static_srv6_sid_attributes { + /* VRF name */ + char vrf_name[VRF_NAMSIZ]; + char ifname[IFNAMSIZ]; + struct in6_addr nh6; +}; + +/* Static SRv6 SID */ +struct static_srv6_sid { + /* SRv6 SID address */ + struct prefix_ipv6 addr; + /* behavior bound to the SRv6 SID */ + enum srv6_endpoint_behavior_codepoint behavior; + /* SID attributes */ + struct static_srv6_sid_attributes attributes; + + /* SRv6 SID flags */ + uint8_t flags; +/* + * this SRv6 SID has been allocated by SID Manager + * and can be installed in the zebra RIB + */ +#define STATIC_FLAG_SRV6_SID_VALID (1 << 0) +/* this SRv6 SID has been installed in the zebra RIB */ +#define STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA (1 << 1) + + char locator_name[SRV6_LOCNAME_SIZE]; + struct static_srv6_locator *locator; +}; + +struct static_srv6_locator { + char name[SRV6_LOCNAME_SIZE]; + struct prefix_ipv6 prefix; + + /* + * Bit length of SRv6 locator described in + * draft-ietf-bess-srv6-services-05#section-3.2.1 + */ + uint8_t block_bits_length; + uint8_t node_bits_length; + uint8_t function_bits_length; + uint8_t argument_bits_length; + + uint8_t flags; +}; + +/* List of SRv6 SIDs. */ +extern struct list *srv6_locators; +extern struct list *srv6_sids; + +/* + * Allocate an SRv6 SID object and initialize its fields, SID address and + * behavor. + */ +extern struct static_srv6_sid *static_srv6_sid_alloc(struct prefix_ipv6 *addr); +extern void static_srv6_sid_free(struct static_srv6_sid *sid); +/* Look-up an SRv6 SID in the list of SRv6 SIDs. */ +extern struct static_srv6_sid *static_srv6_sid_lookup(struct prefix_ipv6 *sid_addr); +/* + * Remove an SRv6 SID from the zebra RIB (if it was previously installed) and + * release the memory previously allocated for the SID. + */ +extern void static_srv6_sid_del(struct static_srv6_sid *sid); + +/* Initialize SRv6 data structures. */ +extern void static_srv6_init(void); +/* Clean up all the SRv6 data structures. */ +extern void static_srv6_cleanup(void); + +/* + * When an interface is enabled in the kernel, go through all the static SRv6 SIDs in + * the system that use this interface and install/remove them in the zebra RIB. + * + * ifp - The interface being enabled + * is_up - Whether the interface is up or down + */ +void static_ifp_srv6_sids_update(struct interface *ifp, bool is_up); + +struct static_srv6_locator *static_srv6_locator_alloc(const char *name); +void static_srv6_locator_free(struct static_srv6_locator *locator); +struct static_srv6_locator *static_srv6_locator_lookup(const char *name); + +void delete_static_srv6_sid(void *val); +void delete_static_srv6_locator(void *val); + +#ifdef __cplusplus +} +#endif + +#endif /* __STATIC_SRV6_H__ */ diff --git a/staticd/static_vrf.c b/staticd/static_vrf.c index 710827a9ff..78bc30500b 100644 --- a/staticd/static_vrf.c +++ b/staticd/static_vrf.c @@ -51,10 +51,8 @@ struct static_vrf *static_vrf_alloc(const char *name) for (afi = AFI_IP; afi <= AFI_IP6; afi++) { for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) { - if (afi == AFI_IP6) - table = srcdest_table_init(); - else - table = route_table_init(); + table = srcdest_table_init(); + table->cleanup = zebra_stable_node_cleanup; info = XCALLOC(MTYPE_STATIC_RTABLE_INFO, sizeof(struct stable_info)); @@ -63,7 +61,6 @@ struct static_vrf *static_vrf_alloc(const char *name) info->safi = safi; route_table_set_info(table, info); - table->cleanup = zebra_stable_node_cleanup; svrf->stable[afi][safi] = table; } } diff --git a/staticd/static_vty.c b/staticd/static_vty.c index 07b8bc3d28..895846a1c7 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -27,6 +27,8 @@ #include "static_debug.h" #include "staticd/static_vty_clippy.c" #include "static_nb.h" +#include "static_srv6.h" +#include "static_zebra.h" #define STATICD_STR "Static route daemon\n" @@ -77,7 +79,7 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) char xpath_seg[XPATH_MAXLEN]; char ab_xpath[XPATH_MAXLEN]; char buf_prefix[PREFIX_STRLEN]; - char buf_src_prefix[PREFIX_STRLEN] = {}; + char buf_src_prefix[PREFIX_STRLEN] = "::/0"; char buf_nh_type[PREFIX_STRLEN] = {}; char buf_tag[PREFIX_STRLEN]; uint8_t label_stack_id = 0; @@ -114,6 +116,7 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) } assert(!!str2prefix(args->prefix, &p)); + src = (struct prefix){ .family = p.family, .prefixlen = 0 }; switch (args->afi) { case AFI_IP: @@ -144,7 +147,7 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) return CMD_WARNING_CONFIG_FAILED; } - if (args->source) + if (src.prefixlen) prefix2str(&src, buf_src_prefix, sizeof(buf_src_prefix)); if (args->gateway) buf_gate_str = args->gateway; @@ -181,25 +184,10 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) static_get_nh_type(type, buf_nh_type, sizeof(buf_nh_type)); if (!args->delete) { - if (args->source) - snprintf(ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH, - "frr-staticd:staticd", "staticd", args->vrf, - buf_prefix, - yang_afi_safi_value2identity(args->afi, - args->safi), - buf_src_prefix, table_id, buf_nh_type, - args->nexthop_vrf, buf_gate_str, - args->interface_name); - else - snprintf(ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, - "frr-staticd:staticd", "staticd", args->vrf, - buf_prefix, - yang_afi_safi_value2identity(args->afi, - args->safi), - table_id, buf_nh_type, args->nexthop_vrf, - buf_gate_str, args->interface_name); + snprintf(ab_xpath, sizeof(ab_xpath), FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, + "frr-staticd:staticd", "staticd", args->vrf, buf_prefix, buf_src_prefix, + yang_afi_safi_value2identity(args->afi, args->safi), table_id, buf_nh_type, + args->nexthop_vrf, buf_gate_str, args->interface_name); /* * If there's already the same nexthop but with a different @@ -216,22 +204,9 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) } /* route + path procesing */ - if (args->source) - snprintf(xpath_prefix, sizeof(xpath_prefix), - FRR_S_ROUTE_SRC_INFO_KEY_XPATH, - "frr-staticd:staticd", "staticd", args->vrf, - buf_prefix, - yang_afi_safi_value2identity(args->afi, - args->safi), - buf_src_prefix, table_id, distance); - else - snprintf(xpath_prefix, sizeof(xpath_prefix), - FRR_STATIC_ROUTE_INFO_KEY_XPATH, - "frr-staticd:staticd", "staticd", args->vrf, - buf_prefix, - yang_afi_safi_value2identity(args->afi, - args->safi), - table_id, distance); + snprintf(xpath_prefix, sizeof(xpath_prefix), FRR_STATIC_ROUTE_INFO_KEY_XPATH, + "frr-staticd:staticd", "staticd", args->vrf, buf_prefix, buf_src_prefix, + yang_afi_safi_value2identity(args->afi, args->safi), table_id, distance); nb_cli_enqueue_change(vty, xpath_prefix, NB_OP_CREATE, NULL); @@ -410,51 +385,18 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) if (orig_seg) XFREE(MTYPE_TMP, orig_seg); } else { - if (args->source) { - if (args->distance) - snprintf(ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH, - "frr-staticd:staticd", "staticd", - args->vrf, buf_prefix, - yang_afi_safi_value2identity( - args->afi, args->safi), - buf_src_prefix, table_id, distance, - buf_nh_type, args->nexthop_vrf, - buf_gate_str, args->interface_name); - else - snprintf( - ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH, - "frr-staticd:staticd", "staticd", - args->vrf, buf_prefix, - yang_afi_safi_value2identity( - args->afi, args->safi), - buf_src_prefix, table_id, buf_nh_type, - args->nexthop_vrf, buf_gate_str, - args->interface_name); - } else { - if (args->distance) - snprintf(ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_NH_KEY_XPATH, - "frr-staticd:staticd", "staticd", - args->vrf, buf_prefix, - yang_afi_safi_value2identity( - args->afi, args->safi), - table_id, distance, buf_nh_type, - args->nexthop_vrf, buf_gate_str, - args->interface_name); - else - snprintf( - ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, - "frr-staticd:staticd", "staticd", - args->vrf, buf_prefix, - yang_afi_safi_value2identity( - args->afi, args->safi), - table_id, buf_nh_type, - args->nexthop_vrf, buf_gate_str, - args->interface_name); - } + if (args->distance) + snprintf(ab_xpath, sizeof(ab_xpath), FRR_DEL_S_ROUTE_NH_KEY_XPATH, + "frr-staticd:staticd", "staticd", args->vrf, buf_prefix, + buf_src_prefix, yang_afi_safi_value2identity(args->afi, args->safi), + table_id, distance, buf_nh_type, args->nexthop_vrf, buf_gate_str, + args->interface_name); + else + snprintf(ab_xpath, sizeof(ab_xpath), + FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, "frr-staticd:staticd", + "staticd", args->vrf, buf_prefix, buf_src_prefix, + yang_afi_safi_value2identity(args->afi, args->safi), table_id, + buf_nh_type, args->nexthop_vrf, buf_gate_str, args->interface_name); dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath); if (!dnode) { @@ -622,6 +564,7 @@ DEFPY_YANG(ip_route_address_interface, |onlink$onlink \ |color (1-4294967295) \ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \ + |segments WORD \ }]", NO_STR IP_STR "Establish static routes\n" @@ -647,7 +590,9 @@ DEFPY_YANG(ip_route_address_interface, BFD_INTEGRATION_SOURCE_STR BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR - BFD_PROFILE_NAME_STR) + BFD_PROFILE_NAME_STR + "Steer this route over an SRv6 SID list\n" + "SRv6 SID list\n") { struct static_route_args args = { .delete = !!no, @@ -669,6 +614,7 @@ DEFPY_YANG(ip_route_address_interface, .bfd_multi_hop = !!bfd_multi_hop, .bfd_source = bfd_source_str, .bfd_profile = bfd_profile, + .segs = segments, }; return static_route_nb_run(vty, &args); @@ -689,6 +635,7 @@ DEFPY_YANG(ip_route_address_interface_vrf, |onlink$onlink \ |color (1-4294967295) \ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \ + |segments WORD \ }]", NO_STR IP_STR "Establish static routes\n" @@ -713,7 +660,9 @@ DEFPY_YANG(ip_route_address_interface_vrf, BFD_INTEGRATION_SOURCE_STR BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR - BFD_PROFILE_NAME_STR) + BFD_PROFILE_NAME_STR + "Steer this route over an SRv6 SID list\n" + "SRv6 SID list\n") { struct static_route_args args = { .delete = !!no, @@ -735,6 +684,7 @@ DEFPY_YANG(ip_route_address_interface_vrf, .bfd_multi_hop = !!bfd_multi_hop, .bfd_source = bfd_source_str, .bfd_profile = bfd_profile, + .segs = segments, }; return static_route_nb_run(vty, &args); @@ -754,6 +704,7 @@ DEFPY_YANG(ip_route, |nexthop-vrf NAME \ |color (1-4294967295) \ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \ + |segments WORD \ }]", NO_STR IP_STR "Establish static routes\n" @@ -778,7 +729,9 @@ DEFPY_YANG(ip_route, BFD_INTEGRATION_SOURCE_STR BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR - BFD_PROFILE_NAME_STR) + BFD_PROFILE_NAME_STR + "Steer this route over an SRv6 SID list\n" + "SRv6 SID list\n") { struct static_route_args args = { .delete = !!no, @@ -799,6 +752,7 @@ DEFPY_YANG(ip_route, .bfd_multi_hop = !!bfd_multi_hop, .bfd_source = bfd_source_str, .bfd_profile = bfd_profile, + .segs = segments, }; return static_route_nb_run(vty, &args); @@ -817,6 +771,7 @@ DEFPY_YANG(ip_route_vrf, |nexthop-vrf NAME \ |color (1-4294967295) \ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \ + |segments WORD \ }]", NO_STR IP_STR "Establish static routes\n" @@ -840,7 +795,9 @@ DEFPY_YANG(ip_route_vrf, BFD_INTEGRATION_SOURCE_STR BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR - BFD_PROFILE_NAME_STR) + BFD_PROFILE_NAME_STR + "Steer this route over an SRv6 SID list\n" + "SRv6 SID list\n") { struct static_route_args args = { .delete = !!no, @@ -861,6 +818,7 @@ DEFPY_YANG(ip_route_vrf, .bfd_multi_hop = !!bfd_multi_hop, .bfd_source = bfd_source_str, .bfd_profile = bfd_profile, + .segs = segments, }; return static_route_nb_run(vty, &args); @@ -1201,8 +1159,167 @@ DEFPY_YANG(ipv6_route_vrf, ipv6_route_vrf_cmd, return static_route_nb_run(vty, &args); } +DEFUN_NOSH (static_segment_routing, static_segment_routing_cmd, + "segment-routing", + "Segment Routing\n") +{ + VTY_PUSH_CONTEXT_NULL(SEGMENT_ROUTING_NODE); + return CMD_SUCCESS; +} + +DEFUN_NOSH (static_srv6, static_srv6_cmd, + "srv6", + "Segment Routing SRv6\n") +{ + VTY_PUSH_CONTEXT_NULL(SRV6_NODE); + return CMD_SUCCESS; +} + +DEFUN_YANG_NOSH (no_static_srv6, no_static_srv6_cmd, + "no srv6", + NO_STR + "Segment Routing SRv6\n") +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), FRR_STATIC_SRV6_INFO_KEY_XPATH, "frr-staticd:staticd", + "staticd", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, "%s", xpath); +} + +DEFUN_NOSH (static_srv6_sids, static_srv6_sids_cmd, + "static-sids", + "Segment Routing SRv6 SIDs\n") +{ + VTY_PUSH_CONTEXT_NULL(SRV6_SIDS_NODE); + return CMD_SUCCESS; +} + +DEFPY_YANG(srv6_sid, srv6_sid_cmd, + "sid X:X::X:X/M locator NAME$locator_name behavior <uN | uDT6 vrf VIEWVRFNAME | uDT4 vrf VIEWVRFNAME | uDT46 vrf VIEWVRFNAME>", + "Configure SRv6 SID\n" + "Specify SRv6 SID\n" + "Locator name\n" + "Specify Locator name\n" + "Specify SRv6 SID behavior\n" + "Apply the code to a uN SID\n" + "Apply the code to an uDT6 SID\n" + "Configure VRF name\n" + "Specify VRF name\n" + "Apply the code to an uDT4 SID\n" + "Configure VRF name\n" + "Specify VRF name\n" + "Apply the code to an uDT46 SID\n" + "Configure VRF name\n" + "Specify VRF name\n") +{ + enum srv6_endpoint_behavior_codepoint behavior = SRV6_ENDPOINT_BEHAVIOR_RESERVED; + int idx = 0; + const char *vrf_name = NULL; + char xpath_srv6[XPATH_MAXLEN]; + char xpath_sid[XPATH_MAXLEN]; + char xpath_behavior[XPATH_MAXLEN]; + char xpath_vrf_name[XPATH_MAXLEN]; + char xpath_locator_name[XPATH_MAXLEN]; + + if (argv_find(argv, argc, "uN", &idx)) { + behavior = SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID; + } else if (argv_find(argv, argc, "uDT6", &idx)) { + behavior = SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID; + vrf_name = argv[idx + 2]->arg; + } else if (argv_find(argv, argc, "uDT4", &idx)) { + behavior = SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID; + vrf_name = argv[idx + 2]->arg; + } else if (argv_find(argv, argc, "uDT46", &idx)) { + behavior = SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID; + vrf_name = argv[idx + 2]->arg; + } + + snprintf(xpath_srv6, sizeof(xpath_srv6), FRR_STATIC_SRV6_INFO_KEY_XPATH, + "frr-staticd:staticd", "staticd", VRF_DEFAULT_NAME); + + snprintf(xpath_sid, sizeof(xpath_sid), FRR_STATIC_SRV6_SID_KEY_XPATH, "frr-staticd:staticd", + "staticd", VRF_DEFAULT_NAME, sid_str); + + strlcpy(xpath_behavior, xpath_sid, sizeof(xpath_behavior)); + strlcat(xpath_behavior, FRR_STATIC_SRV6_SID_BEHAVIOR_XPATH, sizeof(xpath_behavior)); + + nb_cli_enqueue_change(vty, xpath_sid, NB_OP_CREATE, sid_str); + + nb_cli_enqueue_change(vty, xpath_behavior, NB_OP_MODIFY, + srv6_endpoint_behavior_codepoint2str(behavior)); + + if (vrf_name) { + strlcpy(xpath_vrf_name, xpath_sid, sizeof(xpath_vrf_name)); + strlcat(xpath_vrf_name, FRR_STATIC_SRV6_SID_VRF_NAME_XPATH, sizeof(xpath_vrf_name)); + + nb_cli_enqueue_change(vty, xpath_vrf_name, NB_OP_MODIFY, vrf_name); + } + + strlcpy(xpath_locator_name, xpath_sid, sizeof(xpath_locator_name)); + strlcat(xpath_locator_name, FRR_STATIC_SRV6_SID_LOCATOR_NAME_XPATH, + sizeof(xpath_locator_name)); + + nb_cli_enqueue_change(vty, xpath_locator_name, NB_OP_MODIFY, locator_name); + + return nb_cli_apply_changes(vty, "%s", xpath_sid); +} + +DEFPY_YANG(no_srv6_sid, no_srv6_sid_cmd, + "no sid X:X::X:X/M [locator NAME$locator_name] [behavior <uN | uDT6 vrf VIEWVRFNAME | uDT4 vrf VIEWVRFNAME | uDT46 vrf VIEWVRFNAME>]", + NO_STR + "Configure SRv6 SID\n" + "Specify SRv6 SID\n" + "Locator name\n" + "Specify Locator name\n" + "Specify SRv6 SID behavior\n" + "Apply the code to a uN SID\n" + "Apply the code to an uDT6 SID\n" + "Configure VRF name\n" + "Specify VRF name\n" + "Apply the code to an uDT4 SID\n" + "Configure VRF name\n" + "Specify VRF name\n" + "Apply the code to an uDT46 SID\n" + "Configure VRF name\n" + "Specify VRF name\n") +{ + char xpath[XPATH_MAXLEN + 37]; + + snprintf(xpath, sizeof(xpath), FRR_STATIC_SRV6_SID_KEY_XPATH, "frr-staticd:staticd", + "staticd", VRF_DEFAULT_NAME, sid_str); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + #ifdef INCLUDE_MGMTD_CMDDEFS_ONLY +static struct cmd_node sr_node = { + .name = "sr", + .node = SEGMENT_ROUTING_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-sr)# ", +}; + +static struct cmd_node srv6_node = { + .name = "srv6", + .node = SRV6_NODE, + .parent_node = SEGMENT_ROUTING_NODE, + .prompt = "%s(config-srv6)# ", +}; + +static struct cmd_node srv6_sids_node = { + .name = "srv6-sids", + .node = SRV6_SIDS_NODE, + .parent_node = SRV6_NODE, + .prompt = "%s(config-srv6-sids)# ", +}; + static void static_cli_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { @@ -1278,9 +1395,8 @@ static int srv6_seg_iter_cb(const struct lyd_node *dnode, void *arg) } static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, - const struct lyd_node *src, - const struct lyd_node *path, - const struct lyd_node *nexthop, bool show_defaults) + const struct lyd_node *path, const struct lyd_node *nexthop, + bool show_defaults) { const char *vrf; const char *afi_safi; @@ -1294,6 +1410,7 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, struct srv6_seg_iter seg_iter; const char *nexthop_vrf; uint32_t table_id; + struct prefix src_prefix; bool onlink; vrf = yang_dnode_get_string(route, "../../vrf"); @@ -1315,9 +1432,9 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, vty_out(vty, " %s", yang_dnode_get_string(route, "prefix")); - if (src) - vty_out(vty, " from %s", - yang_dnode_get_string(src, "src-prefix")); + yang_dnode_get_prefix(&src_prefix, route, "src-prefix"); + if (src_prefix.prefixlen) + vty_out(vty, " from %pFX", &src_prefix); nh_type = yang_dnode_get_enum(nexthop, "nh-type"); switch (nh_type) { @@ -1421,18 +1538,7 @@ static void static_nexthop_cli_show(struct vty *vty, const struct lyd_node *route = yang_dnode_get_parent(path, "route-list"); - nexthop_cli_show(vty, route, NULL, path, dnode, show_defaults); -} - -static void static_src_nexthop_cli_show(struct vty *vty, - const struct lyd_node *dnode, - bool show_defaults) -{ - const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list"); - const struct lyd_node *src = yang_dnode_get_parent(path, "src-list"); - const struct lyd_node *route = yang_dnode_get_parent(src, "route-list"); - - nexthop_cli_show(vty, route, src, path, dnode, show_defaults); + nexthop_cli_show(vty, route, path, dnode, show_defaults); } static int static_nexthop_cli_cmp(const struct lyd_node *dnode1, @@ -1497,6 +1603,8 @@ static int static_route_list_cli_cmp(const struct lyd_node *dnode1, afi_t afi1, afi2; safi_t safi1, safi2; struct prefix prefix1, prefix2; + struct prefix src_prefix1, src_prefix2; + int rv; afi_safi1 = yang_dnode_get_string(dnode1, "afi-safi"); yang_afi_safi_identity2value(afi_safi1, &afi1, &safi1); @@ -1512,19 +1620,13 @@ static int static_route_list_cli_cmp(const struct lyd_node *dnode1, yang_dnode_get_prefix(&prefix1, dnode1, "prefix"); yang_dnode_get_prefix(&prefix2, dnode2, "prefix"); + rv = prefix_cmp(&prefix1, &prefix2); + if (rv) + return rv; - return prefix_cmp(&prefix1, &prefix2); -} - -static int static_src_list_cli_cmp(const struct lyd_node *dnode1, - const struct lyd_node *dnode2) -{ - struct prefix prefix1, prefix2; - - yang_dnode_get_prefix(&prefix1, dnode1, "src-prefix"); - yang_dnode_get_prefix(&prefix2, dnode2, "src-prefix"); - - return prefix_cmp(&prefix1, &prefix2); + yang_dnode_get_prefix(&src_prefix1, dnode1, "src-prefix"); + yang_dnode_get_prefix(&src_prefix2, dnode2, "src-prefix"); + return prefix_cmp(&src_prefix1, &src_prefix2); } static int static_path_list_cli_cmp(const struct lyd_node *dnode1, @@ -1545,6 +1647,100 @@ static int static_path_list_cli_cmp(const struct lyd_node *dnode1, return (int)distance1 - (int)distance2; } +static void static_segment_routing_cli_show(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, "segment-routing\n"); +} + +static void static_segment_routing_cli_show_end(struct vty *vty, const struct lyd_node *dnode) +{ + vty_out(vty, "exit\n"); + vty_out(vty, "!\n"); +} + +static void static_srv6_cli_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + vty_out(vty, " srv6\n"); +} + +static void static_srv6_cli_show_end(struct vty *vty, const struct lyd_node *dnode) +{ + vty_out(vty, " exit\n"); + vty_out(vty, " !\n"); +} + +static void static_sids_cli_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + vty_out(vty, " static-sids\n"); +} + +static void static_sids_cli_show_end(struct vty *vty, const struct lyd_node *dnode) +{ + vty_out(vty, " exit\n"); + vty_out(vty, " !\n"); +} + +static void srv6_sid_cli_show(struct vty *vty, const struct lyd_node *sid, bool show_defaults) +{ + enum srv6_endpoint_behavior_codepoint srv6_behavior; + struct prefix_ipv6 sid_value; + + yang_dnode_get_ipv6p(&sid_value, sid, "sid"); + + vty_out(vty, " sid %pFX", &sid_value); + vty_out(vty, " locator %s", yang_dnode_get_string(sid, "locator-name")); + + srv6_behavior = yang_dnode_get_enum(sid, "behavior"); + switch (srv6_behavior) { + case SRV6_ENDPOINT_BEHAVIOR_END: + vty_out(vty, " behavior End"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + vty_out(vty, " behavior End.X"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + vty_out(vty, " behavior End.DT6"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + vty_out(vty, " behavior End.DT4"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + vty_out(vty, " behavior End.DT46"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + vty_out(vty, " behavior uN"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + vty_out(vty, " behavior uA"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + vty_out(vty, " behavior uDT6"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + vty_out(vty, " behavior uDT4"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + vty_out(vty, " behavior uDT46"); + break; + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + vty_out(vty, " behavior unknown"); + break; + } + + if (yang_dnode_exists(sid, "vrf-name")) + vty_out(vty, " vrf %s", yang_dnode_get_string(sid, "vrf-name")); + + vty_out(vty, "\n"); +} + +static void static_srv6_sid_cli_show(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + srv6_sid_cli_show(vty, dnode, show_defaults); +} + const struct frr_yang_module_info frr_staticd_cli_info = { .name = "frr-staticd", .ignore_cfg_cbs = true, @@ -1576,22 +1772,30 @@ const struct frr_yang_module_info frr_staticd_cli_info = { } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing", .cbs = { - .cli_cmp = static_src_list_cli_cmp, + .cli_show = static_segment_routing_cli_show, + .cli_show_end = static_segment_routing_cli_show_end, } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6", .cbs = { - .cli_cmp = static_path_list_cli_cmp, + .cli_show = static_srv6_cli_show, + .cli_show_end = static_srv6_cli_show_end, } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids", .cbs = { - .cli_show = static_src_nexthop_cli_show, - .cli_cmp = static_nexthop_cli_cmp, + .cli_show = static_sids_cli_show, + .cli_show_end = static_sids_cli_show_end, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids/sid", + .cbs = { + .cli_show = static_srv6_sid_cli_show, } }, { @@ -1603,17 +1807,18 @@ const struct frr_yang_module_info frr_staticd_cli_info = { #else /* ifdef INCLUDE_MGMTD_CMDDEFS_ONLY */ DEFPY_YANG(debug_staticd, debug_staticd_cmd, - "[no] debug static [{events$events|route$route|bfd$bfd}]", + "[no] debug static [{events$events|route$route|bfd$bfd|srv6$srv6}]", NO_STR DEBUG_STR STATICD_STR "Debug events\n" "Debug route\n" - "Debug bfd\n") + "Debug bfd\n" + "Debug srv6\n") { /* If no specific category, change all */ if (strmatch(argv[argc - 1]->text, "static")) - static_debug_set(vty->node, !no, true, true, true); + static_debug_set(vty->node, !no, true, true, true, true); else - static_debug_set(vty->node, !no, !!events, !!route, !!bfd); + static_debug_set(vty->node, !no, !!events, !!route, !!bfd, !!srv6); return CMD_SUCCESS; } @@ -1669,6 +1874,21 @@ void static_vty_init(void) install_element(VRF_NODE, &ipv6_route_address_interface_vrf_cmd); install_element(CONFIG_NODE, &ipv6_route_cmd); install_element(VRF_NODE, &ipv6_route_vrf_cmd); + + install_node(&sr_node); + install_node(&srv6_node); + install_node(&srv6_sids_node); + install_default(SEGMENT_ROUTING_NODE); + install_default(SRV6_NODE); + install_default(SRV6_SIDS_NODE); + + install_element(CONFIG_NODE, &static_segment_routing_cmd); + install_element(SEGMENT_ROUTING_NODE, &static_srv6_cmd); + install_element(SEGMENT_ROUTING_NODE, &no_static_srv6_cmd); + install_element(SRV6_NODE, &static_srv6_sids_cmd); + install_element(SRV6_SIDS_NODE, &srv6_sid_cmd); + install_element(SRV6_SIDS_NODE, &no_srv6_sid_cmd); + #endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */ #ifndef INCLUDE_MGMTD_CMDDEFS_ONLY diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index d76befc131..6da2dfec90 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -30,6 +30,9 @@ #include "static_nht.h" #include "static_vty.h" #include "static_debug.h" +#include "zclient.h" +#include "static_srv6.h" +#include "lib_errors.h" DEFINE_MTYPE_STATIC(STATIC, STATIC_NHT_DATA, "Static Nexthop tracking data"); PREDECL_HASH(static_nht_hash); @@ -113,6 +116,8 @@ static int static_ifp_up(struct interface *ifp) { static_ifindex_update(ifp, true); + static_ifp_srv6_sids_update(ifp, true); + return 0; } @@ -120,40 +125,44 @@ static int static_ifp_down(struct interface *ifp) { static_ifindex_update(ifp, false); + static_ifp_srv6_sids_update(ifp, false); + return 0; } static int route_notify_owner(ZAPI_CALLBACK_ARGS) { - struct prefix p; + struct prefix p, src_p, *src_pp; enum zapi_route_notify_owner note; uint32_t table_id; safi_t safi; - if (!zapi_route_notify_decode(zclient->ibuf, &p, &table_id, ¬e, NULL, - &safi)) + if (!zapi_route_notify_decode_srcdest(zclient->ibuf, &p, &src_p, &table_id, ¬e, NULL, + &safi)) return -1; + src_pp = src_p.prefixlen ? &src_p : NULL; + switch (note) { case ZAPI_ROUTE_FAIL_INSTALL: - static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED); + static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_NOT_INSTALLED); zlog_warn("%s: Route %pFX failed to install for table: %u", __func__, &p, table_id); break; case ZAPI_ROUTE_BETTER_ADMIN_WON: - static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED); + static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_NOT_INSTALLED); zlog_warn( "%s: Route %pFX over-ridden by better route for table: %u", __func__, &p, table_id); break; case ZAPI_ROUTE_INSTALLED: - static_nht_mark_state(&p, safi, vrf_id, STATIC_INSTALLED); + static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_INSTALLED); break; case ZAPI_ROUTE_REMOVED: - static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED); + static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_NOT_INSTALLED); break; case ZAPI_ROUTE_REMOVE_FAIL: - static_nht_mark_state(&p, safi, vrf_id, STATIC_INSTALLED); + static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_INSTALLED); zlog_warn("%s: Route %pFX failure to remove for table: %u", __func__, &p, table_id); break; @@ -219,8 +228,8 @@ static void static_zebra_nexthop_update(struct vrf *vrf, struct prefix *matched, nhtd->nh_num = nhr->nexthop_num; static_nht_reset_start(matched, afi, nhr->safi, nhtd->nh_vrf_id); - static_nht_update(NULL, matched, nhr->nexthop_num, afi, - nhr->safi, nhtd->nh_vrf_id); + static_nht_update(NULL, NULL, matched, nhr->nexthop_num, afi, nhr->safi, + nhtd->nh_vrf_id); } else zlog_err("No nhtd?"); } @@ -305,10 +314,13 @@ void static_zebra_nht_register(struct static_nexthop *nh, bool reg) { struct static_path *pn = nh->pn; struct route_node *rn = pn->rn; + const struct prefix *p, *src_p; struct static_route_info *si = static_route_info_from_rnode(rn); struct static_nht_data *nhtd, lookup = {}; uint32_t cmd; + srcdest_rnode_prefixes(rn, &p, &src_p); + if (!static_zebra_nht_get_prefix(nh, &lookup.nh)) return; lookup.nh_vrf_id = nh->nh_vrf_id; @@ -344,8 +356,8 @@ void static_zebra_nht_register(struct static_nexthop *nh, bool reg) if (nh->state == STATIC_NOT_INSTALLED || nh->state == STATIC_SENT_TO_ZEBRA) nh->state = STATIC_START; - static_nht_update(&rn->p, &nhtd->nh, nhtd->nh_num, afi, - si->safi, nh->nh_vrf_id); + static_nht_update(p, src_p, &nhtd->nh, nhtd->nh_num, afi, si->safi, + nh->nh_vrf_id); return; } @@ -530,10 +542,673 @@ extern void static_zebra_route_add(struct static_path *pn, bool install) zclient, &api); } +/** + * Send SRv6 SID to ZEBRA for installation or deletion. + * + * @param cmd ZEBRA_ROUTE_ADD or ZEBRA_ROUTE_DELETE + * @param sid SRv6 SID to install or delete + * @param prefixlen Prefix length + * @param oif Outgoing interface + * @param action SID action + * @param context SID context + */ +static void static_zebra_send_localsid(int cmd, const struct in6_addr *sid, uint16_t prefixlen, + ifindex_t oif, enum seg6local_action_t action, + const struct seg6local_context *context) +{ + struct prefix_ipv6 p = {}; + struct zapi_route api = {}; + struct zapi_nexthop *znh; + + if (cmd != ZEBRA_ROUTE_ADD && cmd != ZEBRA_ROUTE_DELETE) { + flog_warn(EC_LIB_DEVELOPMENT, "%s: wrong ZEBRA command", __func__); + return; + } + + if (prefixlen > IPV6_MAX_BITLEN) { + flog_warn(EC_LIB_DEVELOPMENT, "%s: wrong prefixlen %u", __func__, prefixlen); + return; + } + + DEBUGD(&static_dbg_srv6, "%s: |- %s SRv6 SID %pI6 behavior %s", __func__, + cmd == ZEBRA_ROUTE_ADD ? "Add" : "Delete", sid, seg6local_action2str(action)); + + p.family = AF_INET6; + p.prefixlen = prefixlen; + p.prefix = *sid; + + api.vrf_id = VRF_DEFAULT; + api.type = ZEBRA_ROUTE_STATIC; + api.instance = 0; + api.safi = SAFI_UNICAST; + memcpy(&api.prefix, &p, sizeof(p)); + + if (cmd == ZEBRA_ROUTE_DELETE) + return (void)zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); + + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + + znh = &api.nexthops[0]; + + memset(znh, 0, sizeof(*znh)); + + znh->type = NEXTHOP_TYPE_IFINDEX; + znh->ifindex = oif; + SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_SEG6LOCAL); + znh->seg6local_action = action; + memcpy(&znh->seg6local_ctx, context, sizeof(struct seg6local_context)); + + api.nexthop_num = 1; + + zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); +} + +/** + * Install SRv6 SID in the forwarding plane through Zebra. + * + * @param sid SRv6 SID + */ +void static_zebra_srv6_sid_install(struct static_srv6_sid *sid) +{ + enum seg6local_action_t action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; + struct seg6local_context ctx = {}; + struct interface *ifp = NULL; + struct vrf *vrf; + + if (!sid) + return; + + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) + return; + + if (!sid->locator) { + zlog_err("Failed to install SID %pFX: missing locator information", &sid->addr); + return; + } + + switch (sid->behavior) { + case SRV6_ENDPOINT_BEHAVIOR_END: + action = ZEBRA_SEG6_LOCAL_ACTION_END; + break; + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + action = ZEBRA_SEG6_LOCAL_ACTION_END; + SET_SRV6_FLV_OP(ctx.flv.flv_ops, ZEBRA_SEG6_LOCAL_FLV_OP_NEXT_CSID); + ctx.flv.lcblock_len = sid->locator->block_bits_length; + ctx.flv.lcnode_func_len = sid->locator->node_bits_length; + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + action = ZEBRA_SEG6_LOCAL_ACTION_END_DT6; + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) { + zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, + sid->attributes.vrf_name); + return; + } + ctx.table = vrf->data.l.table_id; + ifp = if_get_vrf_loopback(vrf->vrf_id); + if (!ifp) { + zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", + &sid->addr, sid->attributes.vrf_name); + return; + } + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + action = ZEBRA_SEG6_LOCAL_ACTION_END_DT4; + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) { + zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, + sid->attributes.vrf_name); + return; + } + ctx.table = vrf->data.l.table_id; + ifp = if_get_vrf_loopback(vrf->vrf_id); + if (!ifp) { + zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", + &sid->addr, sid->attributes.vrf_name); + return; + } + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + action = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) { + zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, + sid->attributes.vrf_name); + return; + } + ctx.table = vrf->data.l.table_id; + ifp = if_get_vrf_loopback(vrf->vrf_id); + if (!ifp) { + zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", + &sid->addr, sid->attributes.vrf_name); + return; + } + break; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + zlog_warn("unsupported behavior: %u", sid->behavior); + break; + } + + ctx.block_len = sid->locator->block_bits_length; + ctx.node_len = sid->locator->node_bits_length; + ctx.function_len = sid->locator->function_bits_length; + ctx.argument_len = sid->locator->argument_bits_length; + + /* Attach the SID to the SRv6 interface */ + if (!ifp) { + ifp = if_lookup_by_name(DEFAULT_SRV6_IFNAME, VRF_DEFAULT); + if (!ifp) { + zlog_warn("Failed to install SRv6 SID %pFX: %s interface not found", + &sid->addr, DEFAULT_SRV6_IFNAME); + return; + } + } + + /* Send the SID to zebra */ + static_zebra_send_localsid(ZEBRA_ROUTE_ADD, &sid->addr.prefix, sid->addr.prefixlen, + ifp->ifindex, action, &ctx); + + SET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); +} + +void static_zebra_srv6_sid_uninstall(struct static_srv6_sid *sid) +{ + enum seg6local_action_t action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; + struct interface *ifp = NULL; + struct seg6local_context ctx = {}; + struct vrf *vrf; + + if (!sid) + return; + + if (!CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) + return; + + if (!sid->locator) { + zlog_err("Failed to uninstall SID %pFX: missing locator information", &sid->addr); + return; + } + + switch (sid->behavior) { + case SRV6_ENDPOINT_BEHAVIOR_END: + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) { + zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, + sid->attributes.vrf_name); + return; + } + ifp = if_get_vrf_loopback(vrf->vrf_id); + if (!ifp) { + zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", + &sid->addr, sid->attributes.vrf_name); + return; + } + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) { + zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, + sid->attributes.vrf_name); + return; + } + ifp = if_get_vrf_loopback(vrf->vrf_id); + if (!ifp) { + zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", + &sid->addr, sid->attributes.vrf_name); + return; + } + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) { + zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, + sid->attributes.vrf_name); + return; + } + ifp = if_get_vrf_loopback(vrf->vrf_id); + if (!ifp) { + zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", + &sid->addr, sid->attributes.vrf_name); + return; + } + break; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + zlog_warn("unsupported behavior: %u", sid->behavior); + break; + } + + /* The SID is attached to the SRv6 interface */ + if (!ifp) { + ifp = if_lookup_by_name(DEFAULT_SRV6_IFNAME, VRF_DEFAULT); + if (!ifp) { + zlog_warn("%s interface not found: nothing to uninstall", + DEFAULT_SRV6_IFNAME); + return; + } + } + + ctx.block_len = sid->locator->block_bits_length; + ctx.node_len = sid->locator->node_bits_length; + ctx.function_len = sid->locator->function_bits_length; + ctx.argument_len = sid->locator->argument_bits_length; + + static_zebra_send_localsid(ZEBRA_ROUTE_DELETE, &sid->addr.prefix, sid->addr.prefixlen, + ifp->ifindex, action, &ctx); + + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); +} + +extern void static_zebra_request_srv6_sid(struct static_srv6_sid *sid) +{ + struct srv6_sid_ctx ctx = {}; + int ret = 0; + struct vrf *vrf; + + if (!sid) + return; + + /* convert `srv6_endpoint_behavior_codepoint` to `seg6local_action_t` */ + switch (sid->behavior) { + case SRV6_ENDPOINT_BEHAVIOR_END: + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END; + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT6; + /* process SRv6 SID attributes */ + /* generate table ID from the VRF name, if configured */ + if (sid->attributes.vrf_name[0] != '\0') { + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) + return; + ctx.vrf_id = vrf->vrf_id; + } + + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT4; + /* process SRv6 SID attributes */ + /* generate table ID from the VRF name, if configured */ + if (sid->attributes.vrf_name[0] != '\0') { + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) + return; + ctx.vrf_id = vrf->vrf_id; + } + + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; + /* process SRv6 SID attributes */ + /* generate table ID from the VRF name, if configured */ + if (sid->attributes.vrf_name[0] != '\0') { + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) + return; + ctx.vrf_id = vrf->vrf_id; + } + + break; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + zlog_warn("unsupported behavior: %u", sid->behavior); + return; + } + + /* Request SRv6 SID from SID Manager */ + ret = srv6_manager_get_sid(zclient, &ctx, &sid->addr.prefix, sid->locator->name, NULL); + if (ret < 0) + zlog_warn("%s: error getting SRv6 SID!", __func__); +} + +extern void static_zebra_release_srv6_sid(struct static_srv6_sid *sid) +{ + struct srv6_sid_ctx ctx = {}; + struct vrf *vrf; + int ret = 0; + + if (!sid || !CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID)) + return; + + /* convert `srv6_endpoint_behavior_codepoint` to `seg6local_action_t` */ + switch (sid->behavior) { + case SRV6_ENDPOINT_BEHAVIOR_END: + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END; + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT6; + /* process SRv6 SID attributes */ + /* generate table ID from the VRF name, if configured */ + if (sid->attributes.vrf_name[0] != '\0') { + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) + return; + ctx.vrf_id = vrf->vrf_id; + } + + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT4; + /* process SRv6 SID attributes */ + /* generate table ID from the VRF name, if configured */ + if (sid->attributes.vrf_name[0] != '\0') { + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) + return; + ctx.vrf_id = vrf->vrf_id; + } + + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; + /* process SRv6 SID attributes */ + /* generate table ID from the VRF name, if configured */ + if (sid->attributes.vrf_name[0] != '\0') { + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) + return; + ctx.vrf_id = vrf->vrf_id; + } + + break; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + zlog_warn("unsupported behavior: %u", sid->behavior); + return; + } + + /* remove the SRv6 SID from the zebra RIB */ + ret = srv6_manager_release_sid(zclient, &ctx); + if (ret == ZCLIENT_SEND_FAILURE) + flog_err(EC_LIB_ZAPI_SOCKET, "zclient_send_get_srv6_sid() delete failed: %s", + safe_strerror(errno)); +} + +/** + * Ask the SRv6 Manager (zebra) about a specific locator + * + * @param name Locator name + * @return 0 on success, -1 otherwise + */ +int static_zebra_srv6_manager_get_locator(const char *name) +{ + if (!name) + return -1; + + /* + * Send the Get Locator request to the SRv6 Manager and return the + * result + */ + return srv6_manager_get_locator(zclient, name); +} + +static void request_srv6_sids(struct static_srv6_locator *locator) +{ + struct static_srv6_sid *sid; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { + if (sid->locator == locator) + static_zebra_request_srv6_sid(sid); + } +} + +/** + * Internal function to process an SRv6 locator + * + * @param locator The locator to be processed + */ +static int static_zebra_process_srv6_locator_internal(struct srv6_locator *locator) +{ + struct static_srv6_locator *loc; + struct listnode *node; + struct static_srv6_sid *sid; + + if (!locator) + return -1; + + DEBUGD(&static_dbg_srv6, + "%s: Received SRv6 locator %s %pFX, loc-block-len=%u, loc-node-len=%u func-len=%u, arg-len=%u", + __func__, locator->name, &locator->prefix, locator->block_bits_length, + locator->node_bits_length, locator->function_bits_length, + locator->argument_bits_length); + + /* If we are already aware about the locator, nothing to do */ + loc = static_srv6_locator_lookup(locator->name); + if (loc) + return 0; + + loc = static_srv6_locator_alloc(locator->name); + + DEBUGD(&static_dbg_srv6, "%s: SRv6 locator (locator %s, prefix %pFX) set", __func__, + locator->name, &locator->prefix); + + /* Store the locator prefix */ + loc->prefix = locator->prefix; + loc->block_bits_length = locator->block_bits_length; + loc->node_bits_length = locator->node_bits_length; + loc->function_bits_length = locator->function_bits_length; + loc->argument_bits_length = locator->argument_bits_length; + loc->flags = locator->flags; + + listnode_add(srv6_locators, loc); + + for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { + if (strncmp(sid->locator_name, loc->name, sizeof(loc->name)) == 0) + sid->locator = loc; + } + + /* Request SIDs from the locator */ + request_srv6_sids(loc); + + return 0; +} + +/** + * Callback to process an SRv6 locator received from SRv6 Manager (zebra). + * + * @result 0 on success, -1 otherwise + */ +static int static_zebra_process_srv6_locator_add(ZAPI_CALLBACK_ARGS) +{ + struct srv6_locator loc = {}; + + if (!srv6_locators) + return -1; + + /* Decode the SRv6 locator */ + if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) + return -1; + + return static_zebra_process_srv6_locator_internal(&loc); +} + +/** + * Callback to process a notification from SRv6 Manager (zebra) of an SRv6 + * locator deleted. + * + * @result 0 on success, -1 otherwise + */ +static int static_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) +{ + struct srv6_locator loc = {}; + struct listnode *node2, *nnode2; + struct static_srv6_sid *sid; + struct static_srv6_locator *locator; + + if (!srv6_locators) + return -1; + + /* Decode the received zebra message */ + if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) + return -1; + + DEBUGD(&static_dbg_srv6, + "%s: SRv6 locator deleted in zebra: name %s, prefix %pFX, block_len %u, node_len %u, func_len %u, arg_len %u", + __func__, loc.name, &loc.prefix, loc.block_bits_length, loc.node_bits_length, + loc.function_bits_length, loc.argument_bits_length); + + locator = static_srv6_locator_lookup(loc.name); + if (!locator) + return 0; + + DEBUGD(&static_dbg_srv6, "%s: Deleting srv6 sids from locator %s", __func__, locator->name); + + /* Delete SRv6 SIDs */ + for (ALL_LIST_ELEMENTS(srv6_sids, node2, nnode2, sid)) { + if (sid->locator != locator) + continue; + + + DEBUGD(&static_dbg_srv6, "%s: Deleting SRv6 SID (locator %s, sid %pFX)", __func__, + locator->name, &sid->addr); + + /* + * Uninstall the SRv6 SID from the forwarding plane + * through Zebra + */ + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) { + static_zebra_srv6_sid_uninstall(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + } + } + + listnode_delete(srv6_locators, locator); + static_srv6_locator_free(locator); + + return 0; +} + +static int static_zebra_srv6_sid_notify(ZAPI_CALLBACK_ARGS) +{ + struct srv6_sid_ctx ctx; + struct in6_addr sid_addr; + enum zapi_srv6_sid_notify note; + uint32_t sid_func; + struct listnode *node; + char buf[256]; + struct static_srv6_sid *sid = NULL; + char *loc_name; + bool found = false; + + if (!srv6_locators) + return -1; + + /* Decode the received notification message */ + if (!zapi_srv6_sid_notify_decode(zclient->ibuf, &ctx, &sid_addr, &sid_func, NULL, ¬e, + &loc_name)) { + zlog_err("%s : error in msg decode", __func__); + return -1; + } + + DEBUGD(&static_dbg_srv6, + "%s: received SRv6 SID notify: ctx %s sid_value %pI6 sid_func %u note %s", __func__, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx), &sid_addr, sid_func, + zapi_srv6_sid_notify2str(note)); + + /* Handle notification */ + switch (note) { + case ZAPI_SRV6_SID_ALLOCATED: + + DEBUGD(&static_dbg_srv6, "%s: SRv6 SID %pI6 %s ALLOCATED", __func__, &sid_addr, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + + for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { + if (IPV6_ADDR_SAME(&sid->addr.prefix, &sid_addr)) { + found = true; + break; + } + } + + if (!found || !sid) { + zlog_err("SRv6 SID %pI6 %s: not found", &sid_addr, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + return 0; + } + + SET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); + + /* + * Install the new SRv6 End SID in the forwarding plane through + * Zebra + */ + static_zebra_srv6_sid_install(sid); + + SET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + + break; + case ZAPI_SRV6_SID_RELEASED: + + DEBUGD(&static_dbg_srv6, "%s: SRv6 SID %pI6 %s: RELEASED", __func__, &sid_addr, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + + for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { + if (IPV6_ADDR_SAME(&sid->addr.prefix, &sid_addr)) { + found = true; + break; + } + } + + if (!found || !sid) { + zlog_err("SRv6 SID %pI6 %s: not found", &sid_addr, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + return 0; + } + + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); + + break; + case ZAPI_SRV6_SID_FAIL_ALLOC: + zlog_err("SRv6 SID %pI6 %s: Failed to allocate", &sid_addr, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + + /* Error will be logged by zebra module */ + break; + case ZAPI_SRV6_SID_FAIL_RELEASE: + zlog_err("%s: SRv6 SID %pI6 %s failure to release", __func__, &sid_addr, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + + /* Error will be logged by zebra module */ + break; + } + + return 0; +} + static zclient_handler *const static_handlers[] = { [ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add, [ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete, [ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner, + [ZEBRA_SRV6_LOCATOR_ADD] = static_zebra_process_srv6_locator_add, + [ZEBRA_SRV6_LOCATOR_DELETE] = static_zebra_process_srv6_locator_delete, + [ZEBRA_SRV6_SID_NOTIFY] = static_zebra_srv6_sid_notify, }; void static_zebra_init(void) diff --git a/staticd/static_zebra.h b/staticd/static_zebra.h index c4f4ebdcbc..2a94c6dad9 100644 --- a/staticd/static_zebra.h +++ b/staticd/static_zebra.h @@ -7,6 +7,8 @@ #ifndef __STATIC_ZEBRA_H__ #define __STATIC_ZEBRA_H__ +#include "static_srv6.h" + #ifdef __cplusplus extern "C" { #endif @@ -22,6 +24,14 @@ extern void static_zebra_stop(void); extern void static_zebra_vrf_register(struct vrf *vrf); extern void static_zebra_vrf_unregister(struct vrf *vrf); +extern int static_zebra_srv6_manager_get_locator(const char *name); + +extern void static_zebra_request_srv6_sid(struct static_srv6_sid *sid); +extern void static_zebra_release_srv6_sid(struct static_srv6_sid *sid); + +extern void static_zebra_srv6_sid_install(struct static_srv6_sid *sid); +extern void static_zebra_srv6_sid_uninstall(struct static_srv6_sid *sid); + #ifdef __cplusplus } #endif diff --git a/staticd/subdir.am b/staticd/subdir.am index 07ebe3c02c..bdbacbdd68 100644 --- a/staticd/subdir.am +++ b/staticd/subdir.am @@ -19,6 +19,7 @@ staticd_libstatic_a_SOURCES = \ staticd/static_vty.c \ staticd/static_nb.c \ staticd/static_nb_config.c \ + staticd/static_srv6.c \ # end noinst_HEADERS += \ @@ -29,6 +30,7 @@ noinst_HEADERS += \ staticd/static_vty.h \ staticd/static_vrf.h \ staticd/static_nb.h \ + staticd/static_srv6.h \ # end clippy_scan += \ diff --git a/tests/topotests/bgp_aggregate_address_topo1/r1/bgp_192_168_0_1.json b/tests/topotests/bgp_aggregate_address_topo1/r1/bgp_192_168_0_1.json new file mode 100644 index 0000000000..8c0da8dc92 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_topo1/r1/bgp_192_168_0_1.json @@ -0,0 +1,41 @@ +{ + "prefix":"192.168.0.1/32", + "paths":[ + { + "aspath":{ + "string":"65001", + "segments":[ + { + "type":"as-sequence", + "list":[ + 65001 + ] + } + ], + "length":1 + }, + "suppressed":true, + "origin":"IGP", + "metric":10, + "valid":true, + "bestpath":{ + "overall":true, + "selectionReason":"First path received" + }, + "nexthops":[ + { + "ip":"10.0.0.2", + "afi":"ipv4", + "metric":0, + "accessible":true, + "used":true + } + ], + "peer":{ + "peerId":"10.0.0.2", + "routerId":"10.254.254.3", + "type":"external" + } + } + ] +} diff --git a/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py index 370d01e525..a0a1027c98 100644 --- a/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py +++ b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py @@ -13,6 +13,7 @@ Test BGP aggregate address features. """ +import json import os import sys import pytest @@ -265,6 +266,24 @@ match ip address acl-sup-three ) +def test_check_bgp_attribute(): + "Dump the suppressed attribute of the 192.168.0.1/32 prefix in r1." + tgen = get_topogen() + + logger.info("Test that the BGP path to 192.168.0.1 is as expected.") + expected = json.loads(open("{}/r1/bgp_192_168_0_1.json".format(CWD)).read()) + + test_func = functools.partial( + topotest.router_json_cmp, + tgen.gears["r1"], + "show bgp ipv4 192.168.0.1/32 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"r1" BGP 192.168.0.1 route output failed' + assert result is None, assertmsg + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/bgp_bfd_session/r1/frr.conf b/tests/topotests/bgp_bfd_session/r1/frr.conf index a1560b09fa..cea1ff8147 100644 --- a/tests/topotests/bgp_bfd_session/r1/frr.conf +++ b/tests/topotests/bgp_bfd_session/r1/frr.conf @@ -11,4 +11,16 @@ router bgp 65000 neighbor 192.168.1.3 bfd neighbor 192.168.1.3 ebgp-multihop 20 neighbor 192.168.1.3 update-source r1-eth0 + neighbor PG peer-group + neighbor PG remote-as auto + neighbor PG bfd + neighbor PG ebgp-multihop 15 + neighbor PG update-source 10.0.0.1 + neighbor 192.168.1.4 peer-group PG + neighbor PG2 peer-group + neighbor PG2 remote-as auto + neighbor PG2 bfd + neighbor PG2 ebgp-multihop 25 + neighbor 192.168.1.5 peer-group PG2 + neighbor 192.168.1.5 update-source 10.0.0.1 exit diff --git a/tests/topotests/bgp_bfd_session/test_bgp_bfd_session.py b/tests/topotests/bgp_bfd_session/test_bgp_bfd_session.py index adf557af7b..0dbb2f089b 100644 --- a/tests/topotests/bgp_bfd_session/test_bgp_bfd_session.py +++ b/tests/topotests/bgp_bfd_session/test_bgp_bfd_session.py @@ -85,7 +85,29 @@ def test_bgp_bfd_session(): "diagnostic": "ok", "remote-diagnostic": "ok", "type": "dynamic", - } + }, + { + "multihop": True, + "peer": "192.168.1.4", + "local": "10.0.0.1", + "vrf": "default", + "minimum-ttl": 241, + "status": "down", + "diagnostic": "ok", + "remote-diagnostic": "ok", + "type": "dynamic", + }, + { + "multihop": True, + "peer": "192.168.1.5", + "local": "10.0.0.1", + "vrf": "default", + "minimum-ttl": 231, + "status": "down", + "diagnostic": "ok", + "remote-diagnostic": "ok", + "type": "dynamic", + }, ] return topotest.json_cmp(output, expected) diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp_1.py b/tests/topotests/bgp_bmp/test_bgp_bmp_1.py index be3e07929a..1d7aa97473 100644 --- a/tests/topotests/bgp_bmp/test_bgp_bmp_1.py +++ b/tests/topotests/bgp_bmp/test_bgp_bmp_1.py @@ -78,7 +78,7 @@ def setup_module(mod): "tcpdump -nni r1-eth0 -s 0 -w {} &".format(pcap_file), stdout=None ) - for rname, router in tgen.routers().items(): + for _, (rname, router) in enumerate(tgen.routers().items(), 1): logger.info("Loading router %s" % rname) router.load_frr_config( os.path.join(CWD, "{}/frr.conf".format(rname)), diff --git a/tests/topotests/bgp_comm_list_match/r1/bgpd.conf b/tests/topotests/bgp_comm_list_match/r1/bgpd.conf index bac8412088..d7d58d22ae 100644 --- a/tests/topotests/bgp_comm_list_match/r1/bgpd.conf +++ b/tests/topotests/bgp_comm_list_match/r1/bgpd.conf @@ -12,6 +12,8 @@ router bgp 65001 ip prefix-list p1 seq 5 permit 172.16.255.1/32 ip prefix-list p3 seq 5 permit 172.16.255.3/32 ip prefix-list p4 seq 5 permit 172.16.255.4/32 +ip prefix-list p5 seq 5 permit 172.16.255.5/32 +ip prefix-list p6 seq 5 permit 172.16.255.6/32 ! route-map r2 permit 10 match ip address prefix-list p1 @@ -24,5 +26,13 @@ route-map r2 permit 30 set community 65001:10 65001:12 65001:13 exit route-map r2 permit 40 + match ip address prefix-list p5 + set community 65001:13 65001:14 +exit +route-map r2 permit 50 + match ip address prefix-list p6 + set community 65001:16 65001:17 65001:18 65001:19 +exit +route-map r2 permit 60 exit ! diff --git a/tests/topotests/bgp_comm_list_match/r1/zebra.conf b/tests/topotests/bgp_comm_list_match/r1/zebra.conf index 4219a7ca3a..1b19a4a12b 100644 --- a/tests/topotests/bgp_comm_list_match/r1/zebra.conf +++ b/tests/topotests/bgp_comm_list_match/r1/zebra.conf @@ -4,6 +4,8 @@ interface lo ip address 172.16.255.2/32 ip address 172.16.255.3/32 ip address 172.16.255.4/32 + ip address 172.16.255.5/32 + ip address 172.16.255.6/32 ! interface r1-eth0 ip address 192.168.0.1/24 diff --git a/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py b/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py index d0cab26e1a..c14ef6b8c3 100644 --- a/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py +++ b/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py @@ -133,6 +133,70 @@ def test_bgp_comm_list_match_any(): assert result is None, "Failed to filter BGP UPDATES with community-list on R3" +def test_bgp_comm_list_limit_match(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r3"] + router.vtysh_cmd( + """ + configure terminal + route-map r1 permit 20 + match community-limit 3 + """ + ) + + def _bgp_count(): + output = json.loads(router.vtysh_cmd("show bgp ipv4 json")) + expected = { + "vrfName": "default", + "routerId": "192.168.1.3", + "localAS": 65003, + "totalRoutes": 3, + "totalPaths": 3, + } + return topotest.json_cmp(output, expected) + + step("Check that 3 routes have been received on R3") + test_func = functools.partial(_bgp_count) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to check that 3 routes have been received on R3" + + +def test_bgp_comm_list_reset_limit_match(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r3"] + router.vtysh_cmd( + """ + configure terminal + route-map r1 permit 20 + no match community-limit + """ + ) + + def _bgp_count_two(): + output = json.loads(router.vtysh_cmd("show bgp ipv4 json")) + expected = { + "vrfName": "default", + "routerId": "192.168.1.3", + "localAS": 65003, + "totalRoutes": 4, + "totalPaths": 4, + } + return topotest.json_cmp(output, expected) + + step("Check that 4 routes have been received on R3") + test_func = functools.partial(_bgp_count_two) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to check that 4 routes have been received on R3" + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_dynamic_capability/r2/frr.conf b/tests/topotests/bgp_dynamic_capability/r2/frr.conf index 621e9381e3..cca07078ea 100644 --- a/tests/topotests/bgp_dynamic_capability/r2/frr.conf +++ b/tests/topotests/bgp_dynamic_capability/r2/frr.conf @@ -18,7 +18,6 @@ router bgp 65002 neighbor 192.168.1.1 timers connect 1 neighbor 192.168.1.1 capability dynamic neighbor 192.168.1.1 capability extended-nexthop - neighbor 192.168.1.1 addpath-rx-paths-limit 20 neighbor 2001:db8::1 remote-as external neighbor 2001:db8::1 timers 1 3 neighbor 2001:db8::1 timers connect 1 @@ -27,6 +26,9 @@ router bgp 65002 ! address-family ipv4 unicast redistribute connected + neighbor 192.168.1.1 addpath-tx-all-paths + neighbor 192.168.1.1 disable-addpath-rx + neighbor 192.168.1.1 addpath-rx-paths-limit 20 exit-address-family ! address-family ipv6 unicast diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_enhe.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_enhe.py index aa0508e88f..fd467b8c3b 100644 --- a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_enhe.py +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_enhe.py @@ -114,7 +114,7 @@ def test_bgp_dynamic_capability_enhe(): # Clear message stats to check if we receive a notification or not after we # change the role. - r1.vtysh_cmd("clear bgp 2001:db8::2 message-stats") + r2.vtysh_cmd("clear bgp 2001:db8::1 message-stats") r1.vtysh_cmd( """ configure terminal @@ -149,32 +149,6 @@ def test_bgp_dynamic_capability_enhe(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assert result is None, "Session was reset after setting ENHE capability" - def _bgp_check_if_session_not_reset(): - output = json.loads(r2.vtysh_cmd("show bgp neighbor 2001:db8::1 json")) - expected = { - "2001:db8::1": { - "bgpState": "Established", - "neighborCapabilities": { - "dynamic": "advertisedAndReceived", - "extendedNexthop": "advertisedAndReceived", - "extendedNexthopFamililesByPeer": { - "ipv4Unicast": "recieved", - }, - }, - "messageStats": { - "notificationsRecv": 0, - "capabilityRecv": 1, - }, - } - } - return topotest.json_cmp(output, expected) - - test_func = functools.partial( - _bgp_check_if_session_not_reset, - ) - _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) - assert result is None, "Session was reset after setting ENHE capability" - def _bgp_check_nexthop_enhe(): output = json.loads(r1.vtysh_cmd("show ip route 10.10.10.10/32 json")) expected = { diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_path_limit.py index 91df89b1b5..22e4fe687b 100644 --- a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_path_limit.py @@ -9,8 +9,6 @@ Test if Addpath/Paths-Limit capabilities are adjusted dynamically. T1: Enable Addpath/Paths-Limit capabilities and check if they are exchanged dynamically T2: Disable paths limit and check if it's exchanged dynamically -T3: Disable Addpath capability RX and check if it's exchanged dynamically -T4: Disable Addpath capability and check if it's exchanged dynamically """ import os @@ -65,12 +63,12 @@ def test_bgp_addpath_paths_limit(): "dynamic": "advertisedAndReceived", "addPath": { "ipv4Unicast": { - "txAdvertisedAndReceived": False, + "txAdvertisedAndReceived": True, "txAdvertised": True, - "txReceived": False, - "rxAdvertisedAndReceived": True, + "txReceived": True, + "rxAdvertisedAndReceived": False, "rxAdvertised": True, - "rxReceived": True, + "rxReceived": False, } }, "pathsLimit": { @@ -105,7 +103,6 @@ def test_bgp_addpath_paths_limit(): configure terminal router bgp address-family ipv4 unicast - neighbor 192.168.1.1 addpath-tx-all-paths neighbor 192.168.1.1 addpath-rx-paths-limit 21 """ ) @@ -122,9 +119,9 @@ def test_bgp_addpath_paths_limit(): "txAdvertisedAndReceived": True, "txAdvertised": True, "txReceived": True, - "rxAdvertisedAndReceived": True, + "rxAdvertisedAndReceived": False, "rxAdvertised": True, - "rxReceived": True, + "rxReceived": False, } }, "pathsLimit": { @@ -143,7 +140,7 @@ def test_bgp_addpath_paths_limit(): "messageStats": { "notificationsRecv": 0, "notificationsSent": 0, - "capabilityRecv": 2, + "capabilityRecv": 1, }, } } @@ -181,58 +178,6 @@ def test_bgp_addpath_paths_limit(): "txAdvertisedAndReceived": True, "txAdvertised": True, "txReceived": True, - "rxAdvertisedAndReceived": True, - "rxAdvertised": True, - "rxReceived": True, - } - }, - "pathsLimit": { - "ipv4Unicast": { - "advertisedAndReceived": True, - "advertisedPathsLimit": 10, - "receivedPathsLimit": 0, - } - }, - }, - "messageStats": { - "notificationsRecv": 0, - "notificationsSent": 0, - "capabilityRecv": 3, - }, - } - } - return topotest.json_cmp(output, expected) - - test_func = functools.partial( - _disable_paths_limit, - ) - _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) - assert result is None, "Something went wrong after disabling paths limit" - - ### - # T3: Disable Addpath capability RX and check if it's exchanged dynamically - ### - r2.vtysh_cmd( - """ - configure terminal - router bgp - address-family ipv4 unicast - neighbor 192.168.1.1 disable-addpath-rx - """ - ) - - def _disable_addpath_rx(): - output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) - expected = { - "192.168.1.2": { - "bgpState": "Established", - "neighborCapabilities": { - "dynamic": "advertisedAndReceived", - "addPath": { - "ipv4Unicast": { - "txAdvertisedAndReceived": True, - "txAdvertised": True, - "txReceived": True, "rxAdvertisedAndReceived": False, "rxAdvertised": True, "rxReceived": False, @@ -249,63 +194,17 @@ def test_bgp_addpath_paths_limit(): "messageStats": { "notificationsRecv": 0, "notificationsSent": 0, - "capabilityRecv": 4, - }, - } - } - return topotest.json_cmp(output, expected) - - test_func = functools.partial( - _disable_addpath_rx, - ) - _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) - assert result is None, "Something went wrong after disabling Addpath RX flags" - - ### - # T4: Disable Addpath capability and check if it's exchanged dynamically - ### - r1.vtysh_cmd( - """ - configure terminal - router bgp - address-family ipv4 unicast - no neighbor 192.168.1.2 addpath-tx-all-paths - """ - ) - - def _disable_addpath(): - output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) - expected = { - "192.168.1.2": { - "bgpState": "Established", - "neighborCapabilities": { - "dynamic": "advertisedAndReceived", - "addPath": { - "ipv4Unicast": { - "txAdvertisedAndReceived": False, - "txAdvertised": False, - "txReceived": True, - "rxAdvertisedAndReceived": False, - "rxAdvertised": True, - "rxReceived": False, - } - }, - }, - "messageStats": { - "notificationsRecv": 0, - "notificationsSent": 0, - "capabilitySent": 1, - "capabilityRecv": 4, + "capabilityRecv": 2, }, } } return topotest.json_cmp(output, expected) test_func = functools.partial( - _disable_addpath, + _disable_paths_limit, ) _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) - assert result is None, "Something went wrong when disabling Addpath capability" + assert result is None, "Something went wrong after disabling paths limit" if __name__ == "__main__": diff --git a/tests/topotests/bgp_evpn_route_map_match/r1/frr.conf b/tests/topotests/bgp_evpn_route_map_match/r1/frr.conf index 4347052c5e..2390733cc8 100644 --- a/tests/topotests/bgp_evpn_route_map_match/r1/frr.conf +++ b/tests/topotests/bgp_evpn_route_map_match/r1/frr.conf @@ -24,19 +24,16 @@ router bgp 65001 ! address-family l2vpn evpn neighbor 192.168.1.2 activate - neighbor 192.168.1.2 route-map r2 out + neighbor 192.168.1.2 route-map rt5 out advertise-all-vni advertise ipv4 unicast exit-address-family ! -route-map r2 deny 10 - match evpn route-type macip -! -route-map r2 deny 20 +route-map rt5 deny 20 match ip address prefix-list pl match evpn route-type prefix ! -route-map r2 permit 30 +route-map rt5 permit 30 ! ip prefix-list pl seq 5 permit 192.168.1.0/24 ip prefix-list pl seq 10 permit 10.10.10.1/32 diff --git a/tests/topotests/bgp_evpn_route_map_match/r2/frr.conf b/tests/topotests/bgp_evpn_route_map_match/r2/frr.conf index 9ed298d8fe..1c91a3e254 100644 --- a/tests/topotests/bgp_evpn_route_map_match/r2/frr.conf +++ b/tests/topotests/bgp_evpn_route_map_match/r2/frr.conf @@ -7,6 +7,7 @@ int lo int r2-eth0 ip address 192.168.1.2/24 ! +vni 10 router bgp 65002 no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_evpn_route_map_match/test_bgp_evpn_route_map_match.py b/tests/topotests/bgp_evpn_route_map_match/test_bgp_evpn_route_map_match.py index 36c79d6b2b..925ae1fce8 100644 --- a/tests/topotests/bgp_evpn_route_map_match/test_bgp_evpn_route_map_match.py +++ b/tests/topotests/bgp_evpn_route_map_match/test_bgp_evpn_route_map_match.py @@ -23,6 +23,7 @@ sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 from lib import topotest from lib.topogen import Topogen, get_topogen +from lib.topolog import logger def setup_module(mod): @@ -63,7 +64,7 @@ def teardown_module(mod): tgen.stop_topology() -def test_bgp_evpn_route_map_match_route_type(): +def test_bgp_evpn_route_map_match_route_type5(): tgen = get_topogen() if tgen.routers_have_failure(): @@ -84,16 +85,12 @@ def test_bgp_evpn_route_map_match_route_type(): "valid": True, } }, - "10.10.10.2:2": { - "[3]:[0]:[32]:[10.10.10.2]": { - "valid": True, - } - }, }, - "totalPrefixCounter": 2, + "totalPrefixCounter": 1, } return topotest.json_cmp(output, expected) + logger.info("Check route type-5 filtering") test_func = functools.partial( _bgp_converge, ) @@ -101,6 +98,97 @@ def test_bgp_evpn_route_map_match_route_type(): assert result is None, "Filtered EVPN routes should not be advertised" +def test_bgp_evpn_route_map_match_route_type2(): + tgen = get_topogen() + + # Change to L2VNI + for machine in [tgen.gears["r1"], tgen.gears["r2"]]: + machine.vtysh_cmd("configure terminal\nno vni 10") + + def _check_l2vni(): + for machine in [tgen.gears["r1"], tgen.gears["r2"]]: + output = json.loads(machine.vtysh_cmd("show evpn vni json")) + + expected = {"10": {"vni": 10, "type": "L2"}} + return topotest.json_cmp(output, expected) + + logger.info("Check L2VNI setup") + test_func = functools.partial(_check_l2vni) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "L2VNI setup failed." + + c2_mac = ( + tgen.gears["c2"] + .cmd("ip link show c2-eth0 | awk '/link\/ether/ {print $2}'") + .rstrip() + ) + tgen.gears["r1"].vtysh_cmd( + "\n".join( + [ + "configure", + "route-map rt2 deny 30", + "match mac address %s" % c2_mac, + "exit", + "router bgp 65001", + "address-family l2vpn evpn", + "neighbor 192.168.1.2 route-map rt2 in", + ] + ) + ) + + def _check_filter_mac(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd( + "show bgp l2vpn evpn neighbors 192.168.1.2 advertised-routes json" + ) + ) + + if ( + output["advertisedRoutes"] + .get("10.10.10.2:2", {}) + .get("[2]:[0]:[48]:[%s]" % c2_mac) + ): + return False + + return True + + logger.info("check mac filter in, on c2 interface: %s" % c2_mac) + test_func = functools.partial(_check_filter_mac) + _, result = topotest.run_and_expect(test_func, True, count=60, wait=1) + assert result is True, "%s is not filtered" % c2_mac + + tgen.gears["r1"].vtysh_cmd( + "\n".join( + [ + "configure", + "route-map rt2 deny 30", + "no match mac address %s" % c2_mac, + "match evpn route-type macip" "exit", + "router bgp 65001", + "address-family l2vpn evpn", + "neighbor 192.168.1.2 route-map rt2 out", + ] + ) + ) + + def _check_filter_type2(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd( + "show bgp l2vpn evpn neighbors 192.168.1.2 advertised-routes json" + ) + ) + + if output["totalPrefixCounter"] == 0: + return True + + return False + + logger.info("check route type-2 filter out") + test_func = functools.partial(_check_filter_type2) + _, result = topotest.run_and_expect(test_func, True, count=60, wait=1) + assert result is True, "EVPN routes type-2 are not filtered." + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes.json index 7532ce9331..cfab5726ed 100644 --- a/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes.json +++ b/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes.json @@ -2,8 +2,8 @@ "bgpLocalRouterId":"192.168.100.21", "defaultLocPrf":100, "localAS":65000, - "192.168.101.41:2":{ - "rd":"192.168.101.41:2", + "65000:201":{ + "rd":"65000:201", "[5]:[0]:[32]:[192.168.101.41]":{ "prefix":"[5]:[0]:[32]:[192.168.101.41]", "prefixLen":352, @@ -65,8 +65,8 @@ ] } }, - "192.168.102.21:2":{ - "rd":"192.168.102.21:2", + "65000:101":{ + "rd":"65000:101", "[5]:[0]:[32]:[192.168.102.21]":{ "prefix":"[5]:[0]:[32]:[192.168.102.21]", "prefixLen":352, diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes_all.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes_all.json index a14ba1291e..444c67e442 100644 --- a/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes_all.json +++ b/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes_all.json @@ -2,8 +2,8 @@ "bgpLocalRouterId":"192.168.100.21", "defaultLocPrf":100, "localAS":65000, - "192.168.101.41:2":{ - "rd":"192.168.101.41:2", + "65000:201":{ + "rd":"65000:201", "[5]:[0]:[32]:[192.168.101.41]":{ "prefix":"[5]:[0]:[32]:[192.168.101.41]", "prefixLen":352, @@ -125,8 +125,8 @@ ] } }, - "192.168.102.21:2":{ - "rd":"192.168.102.21:2", + "65000:101":{ + "rd":"65000:101", "[5]:[0]:[32]:[192.168.102.21]":{ "prefix":"[5]:[0]:[32]:[192.168.102.21]", "prefixLen":352, diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r1/frr.conf index c8c4faf222..e4a8059988 100644 --- a/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf +++ b/tests/topotests/bgp_evpn_rt5/r1/frr.conf @@ -1,15 +1,36 @@ +! debug zebra vxlan +! debug zebra kernel +! debug zebra dplane +! debug zebra rib ! debug bgp neighbor-events ! debug bgp updates ! debug bgp zebra +vrf r1-vrf-101 + vni 101 + exit-vrf +! +interface r1-eth0 + ip address 192.168.100.21/24 +! +interface loop101 vrf r1-vrf-101 + ip address 192.168.102.21/32 + ipv6 address fd00::1/128 +! router bgp 65000 bgp router-id 192.168.100.21 bgp log-neighbor-changes no bgp default ipv4-unicast + no bgp ebgp-requires-policy neighbor 192.168.100.41 remote-as 65000 neighbor 192.168.100.41 capability extended-nexthop + neighbor 192.168.100.61 remote-as 65500 + neighbor 192.168.100.61 capability extended-nexthop ! address-family l2vpn evpn neighbor 192.168.100.41 activate + neighbor 192.168.100.41 route-map rmap_r1 in + neighbor 192.168.100.61 activate + neighbor 192.168.100.61 route-map rmap_r3 in advertise-all-vni exit-address-family ! @@ -24,7 +45,17 @@ router bgp 65000 vrf r1-vrf-101 network fd00::1/128 exit-address-family address-family l2vpn evpn + rd 65000:101 + route-target both 65:101 advertise ipv4 unicast advertise ipv6 unicast exit-address-family ! +route-map rmap_r3 deny 1 + match evpn vni 102 +exit +route-map rmap_r1 permit 1 + match evpn vni 101 +exit + + diff --git a/tests/topotests/bgp_evpn_rt5/r1/zebra.conf b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf deleted file mode 100644 index c3d508c2b6..0000000000 --- a/tests/topotests/bgp_evpn_rt5/r1/zebra.conf +++ /dev/null @@ -1,23 +0,0 @@ -log stdout - -hostname r1 -password zebra - -! debug zebra vxlan -! debug zebra kernel -! debug zebra dplane -! debug zebra rib -log stdout -vrf r1-vrf-101 - vni 101 - exit-vrf -! -interface r1-eth0 - ip address 192.168.100.21/24 -! -interface loop101 vrf r1-vrf-101 - ip address 192.168.102.21/32 - ipv6 address fd00::1/128 -! - - diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgp_l2vpn_evpn_routes.json b/tests/topotests/bgp_evpn_rt5/r2/bgp_l2vpn_evpn_routes.json index 597bca5fd3..3a55a7a38d 100644 --- a/tests/topotests/bgp_evpn_rt5/r2/bgp_l2vpn_evpn_routes.json +++ b/tests/topotests/bgp_evpn_rt5/r2/bgp_l2vpn_evpn_routes.json @@ -2,8 +2,8 @@ "bgpLocalRouterId":"192.168.100.41", "defaultLocPrf":100, "localAS":65000, - "192.168.101.41:2":{ - "rd":"192.168.101.41:2", + "65000:201":{ + "rd":"65000:201", "[5]:[0]:[32]:[192.168.101.41]":{ "prefix":"[5]:[0]:[32]:[192.168.101.41]", "prefixLen":352, @@ -63,8 +63,8 @@ ] } }, - "192.168.102.21:2":{ - "rd":"192.168.102.21:2", + "65000:101":{ + "rd":"65000:101", "[5]:[0]:[32]:[192.168.102.21]":{ "prefix":"[5]:[0]:[32]:[192.168.102.21]", "prefixLen":352, diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r2/frr.conf index 4f1d8e4a37..0bb4b7cab5 100644 --- a/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf +++ b/tests/topotests/bgp_evpn_rt5/r2/frr.conf @@ -1,6 +1,19 @@ +! debug zebra vxlan ! debug bgp neighbor-events ! debug bgp updates ! debug bgp zebra + +vrf r2-vrf-101 + vni 101 + exit-vrf +! +interface loop101 vrf r2-vrf-101 + ip address 192.168.101.41/32 + ipv6 address fd00::2/128 +! +interface r2-eth0 + ip address 192.168.100.41/24 +! router bgp 65000 bgp router-id 192.168.100.41 bgp log-neighbor-changes @@ -27,6 +40,8 @@ router bgp 65000 vrf r2-vrf-101 network fd00::3/128 exit-address-family address-family l2vpn evpn + rd 65000:201 + route-target both 65:101 advertise ipv4 unicast route-map rmap4 advertise ipv6 unicast route-map rmap6 exit-address-family @@ -47,3 +62,4 @@ exit route-map rmap6 deny 2 match ipv6 address acl6_2 exit + diff --git a/tests/topotests/bgp_evpn_rt5/r2/zebra.conf b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf deleted file mode 100644 index 7db40cb59c..0000000000 --- a/tests/topotests/bgp_evpn_rt5/r2/zebra.conf +++ /dev/null @@ -1,19 +0,0 @@ -log stdout - -hostname r2 -password zebra - -! debug zebra vxlan - -vrf r2-vrf-101 - vni 101 - exit-vrf -! -interface loop101 vrf r2-vrf-101 - ip address 192.168.101.41/32 - ipv6 address fd00::2/128 -! -interface r2-eth0 - ip address 192.168.100.41/24 -! - diff --git a/tests/topotests/bgp_evpn_rt5/r3/frr.conf b/tests/topotests/bgp_evpn_rt5/r3/frr.conf new file mode 100644 index 0000000000..3f3851bd8c --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r3/frr.conf @@ -0,0 +1,46 @@ +! debug bgp neighbor-events +! debug bgp updates +! debug bgp zebra +vrf r3-vrf-102 + vni 102 + exit-vrf +! +interface r3-eth0 + ip address 192.168.100.61/24 +! +interface loop102 vrf r3-vrf-102 + ip address 192.168.102.61/32 + ipv6 address fd00:6::1/128 +! +router bgp 65500 + bgp router-id 192.168.100.61 + bgp log-neighbor-changes + no bgp default ipv4-unicast + no bgp ebgp-requires-policy + neighbor 192.168.100.21 remote-as 65000 + neighbor 192.168.100.21 capability extended-nexthop + ! + address-family l2vpn evpn + neighbor 192.168.100.21 activate + advertise-all-vni + exit-address-family +! +router bgp 65000 vrf r3-vrf-102 + bgp router-id 192.168.100.61 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 192.168.102.102/32 + exit-address-family + address-family ipv6 unicast + network fd00:102::1/128 + exit-address-family + address-family l2vpn evpn + rd 65000:302 + route-target both 65:101 + advertise ipv4 unicast + advertise ipv6 unicast + exit-address-family + ! + + diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py index c874cbed66..2b8355af09 100644 --- a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -42,10 +42,12 @@ def build_topo(tgen): tgen.add_router("r1") tgen.add_router("r2") + tgen.add_router("r3") switch = tgen.add_switch("s1") switch.add_link(tgen.gears["r1"]) switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) switch = tgen.add_switch("s2") switch.add_link(tgen.gears["r1"]) @@ -53,6 +55,9 @@ def build_topo(tgen): switch = tgen.add_switch("s3") switch.add_link(tgen.gears["r2"]) + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r3"]) + def setup_module(mod): "Sets up the pytest environment" @@ -71,16 +76,16 @@ def setup_module(mod): ) return pytest.skip("Skipping BGP EVPN RT5 NETNS Test. Kernel not supported") - # create VRF vrf-101 on R1 and R2 + # create VRF vrf-101 on R1, R2, R3 # create loop101 cmds_vrflite = [ - "ip link add {}-vrf-101 type vrf table 101", - "ip ru add oif {}-vrf-101 table 101", - "ip ru add iif {}-vrf-101 table 101", - "ip link set dev {}-vrf-101 up", - "ip link add loop101 type dummy", - "ip link set dev loop101 master {}-vrf-101", - "ip link set dev loop101 up", + "ip link add {0}-vrf-{1} type vrf table {1}", + "ip ru add oif {0}-vrf-{1} table {1}", + "ip ru add iif {0}-vrf-{1} table {1}", + "ip link set dev {0}-vrf-{1} up", + "ip link add loop{1} type dummy", + "ip link set dev loop{1} master {0}-vrf-{1}", + "ip link set dev loop{1} up", ] cmds_r2 = [ # config routing 101 @@ -92,6 +97,15 @@ def setup_module(mod): "ip link set vxlan-101 up type bridge_slave learning off flood off mcast_flood off", ] + cmds_r3 = [ # config routing 102 + "ip link add name bridge-102 up type bridge stp_state 0", + "ip link set bridge-102 master {}-vrf-102", + "ip link set dev bridge-102 up", + "ip link add name vxlan-102 type vxlan id 102 dstport 4789 dev r3-eth0 local 192.168.100.61", + "ip link set dev vxlan-102 master bridge-102", + "ip link set vxlan-102 up type bridge_slave learning off flood off mcast_flood off", + ] + # cmds_r1_netns_method3 = [ # "ip link add name vxlan-{1} type vxlan id {1} dstport 4789 dev {0}-eth0 local 192.168.100.21", # "ip link set dev vxlan-{1} netns {0}-vrf-{1}", @@ -111,8 +125,8 @@ def setup_module(mod): router = tgen.gears["r2"] for cmd in cmds_vrflite: - logger.info("cmd to r2: " + cmd.format("r2")) - output = router.cmd_raises(cmd.format("r2")) + logger.info("cmd to r2: " + cmd.format("r2", 101)) + output = router.cmd_raises(cmd.format("r2", 101)) logger.info("result: " + output) for cmd in cmds_r2: @@ -120,6 +134,17 @@ def setup_module(mod): output = router.cmd_raises(cmd.format("r2")) logger.info("result: " + output) + router = tgen.gears["r3"] + for cmd in cmds_vrflite: + logger.info("cmd to r3: " + cmd.format("r3", 102)) + output = router.cmd_raises(cmd.format("r3", 102)) + logger.info("result: " + output) + + for cmd in cmds_r3: + logger.info("cmd to r3: " + cmd.format("r3")) + output = router.cmd_raises(cmd.format("r3")) + logger.info("result: " + output) + tgen.net["r1"].cmd_raises( "ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev r1-eth0 local 192.168.100.21" ) @@ -134,19 +159,13 @@ def setup_module(mod): tgen.net["r1"].cmd_raises("ip -n r1-vrf-101 link set bridge-101 up") tgen.net["r1"].cmd_raises("ip -n r1-vrf-101 link set vxlan-101 up") - for rname, router in router_list.items(): + for rname, router in tgen.routers().items(): + logger.info("Loading router %s" % rname) if rname == "r1": router.use_netns_vrf() - router.load_config( - TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) - ) + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) else: - 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_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) # Initialize all routers. tgen.start_router() diff --git a/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf b/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf index 724cbf84ab..a26efb4c4f 100644 --- a/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf +++ b/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf @@ -4,3 +4,6 @@ router bgp 65001 neighbor fe80:1::2 remote-as external neighbor fe80:1::2 timers 3 10 neighbor fe80:1::2 interface r1-eth0 + neighbor fe80:1::3 remote-as external + neighbor fe80:1::3 timers 3 10 + neighbor fe80:1::3 interface r1-eth1 diff --git a/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf b/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf index 4e93d4f4e5..f1bbff2e44 100644 --- a/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf +++ b/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf @@ -2,3 +2,6 @@ interface r1-eth0 ipv6 address fe80:1::1/64 ! +interface r1-eth1 + ipv6 address fe80:1::1/64 +! diff --git a/tests/topotests/bgp_ipv6_ll_peering/r3/bgpd.conf b/tests/topotests/bgp_ipv6_ll_peering/r3/bgpd.conf new file mode 100644 index 0000000000..f1684880b3 --- /dev/null +++ b/tests/topotests/bgp_ipv6_ll_peering/r3/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor fe80:1::1 remote-as external + neighbor fe80:1::1 timers 3 10 + neighbor fe80:1::1 interface r3-eth0 diff --git a/tests/topotests/bgp_ipv6_ll_peering/r3/zebra.conf b/tests/topotests/bgp_ipv6_ll_peering/r3/zebra.conf new file mode 100644 index 0000000000..71053cd2c3 --- /dev/null +++ b/tests/topotests/bgp_ipv6_ll_peering/r3/zebra.conf @@ -0,0 +1,4 @@ +! +interface r3-eth0 + ipv6 address fe80:1::3/64 +! diff --git a/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py b/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py index aaa68ea340..fbd4097605 100644 --- a/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py +++ b/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py @@ -27,13 +27,17 @@ pytestmark = [pytest.mark.bgpd] def build_topo(tgen): - for routern in range(1, 3): + for routern in range(1, 4): tgen.add_router("r{}".format(routern)) switch = tgen.add_switch("s1") switch.add_link(tgen.gears["r1"]) switch.add_link(tgen.gears["r2"]) + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + def setup_module(mod): tgen = Topogen(build_topo, mod.__name__) @@ -64,6 +68,7 @@ def test_bgp_ipv6_link_local_peering(): pytest.skip(tgen.errors) r1 = tgen.gears["r1"] + r3 = tgen.gears["r3"] def _bgp_converge(): output = json.loads(r1.vtysh_cmd("show bgp summary json")) @@ -82,6 +87,28 @@ def test_bgp_ipv6_link_local_peering(): _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert result is None, "Failed to see BGP convergence on R2" + def _bgp_router_id_missing(): + output = json.loads(r3.vtysh_cmd("show bgp summary failed json")) + expected = { + "ipv4Unicast": { + "routerId": "0.0.0.0", + "as": 65003, + "peers": { + "fe80:1::1": { + "connectionsEstablished": 0, + "connectionsDropped": 0, + "peerUptime": "never", + "lastResetDueTo": "Router ID is missing", + } + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_router_id_missing) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "r3 should stay down due to missing router ID" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/bgp_ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/bgp_ipv4_routes.json new file mode 100644 index 0000000000..affe5cf8df --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/bgp_ipv4_routes.json @@ -0,0 +1,35 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "10.254.254.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { + "10.254.254.2/32": [{ + "valid": true, + "bestpath": true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.254.254.2", + "prefixLen":32, + "network":"10.254.254.2/32", + "metric":0, + "weight":0, + "path":"102", + "origin":"incomplete", + "nexthops":[{ + "ip":"2001:db8:1::2", + "hostname":"r2", + "afi":"ipv6", + "scope":"global" + },{ + "interface":"r1-eth0", + "hostname":"r2", + "afi":"ipv6", + "scope":"link-local", + "used":true + }]}] + }, + "totalRoutes": 2, + "totalPaths": 2 +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/bgp_ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/bgp_ipv6_routes.json new file mode 100644 index 0000000000..bccfb45771 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/bgp_ipv6_routes.json @@ -0,0 +1,35 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "10.254.254.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { + "2001:db8:1::/64": [{ + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"2001:db8:1::", + "prefixLen":64, + "network":"2001:db8:1::/64", + "metric":0, + "weight":0, + "path":"102", + "origin":"incomplete", + "nexthops":[{ + "ip":"2001:db8:1::2", + "hostname":"r2", + "afi":"ipv6", + "scope":"global" + },{ + "interface":"r1-eth0", + "hostname":"r2", + "afi":"ipv6", + "scope":"link-local", + "used":true + }]}] + }, + "totalRoutes": 1, + "totalPaths": 1 +} diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py index 045ac91fc7..5992c30116 100644 --- a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py @@ -75,6 +75,45 @@ def teardown_module(_mod): def test_protocols_convergence(): """ + Assert that BGP protocol has converged + by checking the incoming BGP updates have been received. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check BGP IPv4 routing table. + logger.info("Checking BGP IPv4 routes for convergence") + router = tgen.gears["r1"] + + json_file = "{}/{}/bgp_ipv4_routes.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv4 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check BGP IPv6 routing table. + json_file = "{}/{}/bgp_ipv6_routes.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv6 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_route_convergence(): + """ Assert that all protocols have converged statuses as they depend on it. """ diff --git a/tests/topotests/bgp_oad/r3/frr.conf b/tests/topotests/bgp_oad/r3/frr.conf index 02dd5adfe1..164267d74d 100644 --- a/tests/topotests/bgp_oad/r3/frr.conf +++ b/tests/topotests/bgp_oad/r3/frr.conf @@ -7,12 +7,14 @@ int r3-eth0 ! router bgp 65003 no bgp ebgp-requires-policy + no bgp network import-check neighbor 192.168.2.2 remote-as external neighbor 192.168.2.2 timers 1 3 neighbor 192.168.2.2 timers connect 1 neighbor 192.168.2.2 oad ! address-family ipv4 unicast + network 10.10.10.20/32 route-map static redistribute connected route-map connected exit-address-family ! @@ -20,3 +22,7 @@ route-map connected permit 10 set local-preference 123 set metric 123 ! +route-map static permit 10 + set extcommunity bandwidth 100 non-transitive +exit +! diff --git a/tests/topotests/bgp_oad/test_bgp_oad.py b/tests/topotests/bgp_oad/test_bgp_oad.py index b2ea7e0f19..b397bc6372 100644 --- a/tests/topotests/bgp_oad/test_bgp_oad.py +++ b/tests/topotests/bgp_oad/test_bgp_oad.py @@ -56,6 +56,7 @@ def test_bgp_oad(): r2 = tgen.gears["r2"] r3 = tgen.gears["r3"] r4 = tgen.gears["r4"] + r5 = tgen.gears["r5"] def _bgp_converge(): output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 10.10.10.10/32 json")) @@ -121,6 +122,38 @@ def test_bgp_oad(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assert result is None, "10.10.10.1/32 should not be advertised to r4 (not OAD peer)" + def _bgp_check_non_transitive_extended_community( + router, arg={"string": "LB:65003:12500000 (100.000 Mbps)"} + ): + output = json.loads( + router.vtysh_cmd("show bgp ipv4 unicast 10.10.10.20/32 json") + ) + expected = { + "paths": [ + { + "extendedCommunity": arg, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_non_transitive_extended_community, + r4, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "10.10.10.20/32 should be received at r4 with non-transitive extended community" + + test_func = functools.partial( + _bgp_check_non_transitive_extended_community, r5, None + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "10.10.10.20/32 should NOT be received at r5 with non-transitive extended community" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_peer_solo/r1/frr.conf b/tests/topotests/bgp_peer_solo/r1/frr.conf new file mode 100644 index 0000000000..6ef3688b83 --- /dev/null +++ b/tests/topotests/bgp_peer_solo/r1/frr.conf @@ -0,0 +1,10 @@ +! +int r1-eth0 + ip address 10.255.0.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 10.255.0.2 timers 1 3 + neighbor 10.255.0.2 timers connect 1 + neighbor 10.255.0.2 remote-as external +! diff --git a/tests/topotests/bgp_peer_solo/r2/frr.conf b/tests/topotests/bgp_peer_solo/r2/frr.conf new file mode 100644 index 0000000000..c58e327418 --- /dev/null +++ b/tests/topotests/bgp_peer_solo/r2/frr.conf @@ -0,0 +1,14 @@ +! +int r2-eth0 + ip address 10.255.0.2/24 +! +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.255.0.1 remote-as external + neighbor 10.255.0.1 timers 1 3 + neighbor 10.255.0.1 timers connect 1 + address-family ipv4 unicast + network 10.0.0.1/32 + exit-address-family +! diff --git a/tests/topotests/bgp_peer_solo/test_bgp_peer_solo.py b/tests/topotests/bgp_peer_solo/test_bgp_peer_solo.py new file mode 100644 index 0000000000..6bd2430aea --- /dev/null +++ b/tests/topotests/bgp_peer_solo/test_bgp_peer_solo.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +import os +import re +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +from lib import topotest +from lib.topogen import Topogen, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_peer_solo(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast summary json")) + print("output=", output) + expected = { + "peers": { + "10.255.0.2": { + "remoteAs": 65002, + "state": "Established", + "peerState": "OK", + }, + }, + "totalPeers": 1, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge initial state" + + def _bgp_advertised(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 neighbors 10.255.0.2 advertised-routes json")) + print("output adv=", output) + expected = { + "advertisedRoutes": { + "10.0.0.1/32": {}, + }, + "totalPrefixCounter": 1, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_advertised, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "Should contain an advertised route" + + # + # Apply solo option + # + r1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + neighbor 10.255.0.2 solo + """ + ) + + def _bgp_no_advertised(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 neighbors 10.255.0.2 advertised-routes json")) + expected = { + "totalPrefixCounter": 0, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_no_advertised, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "Shouldn't contain advertised routes" + + # + # Unset solo option + # + r1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + no neighbor 10.255.0.2 solo + """ + ) + + test_func = functools.partial( + _bgp_advertised, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "Should contain an advertised route" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_rpki_valid.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_rpki_valid.json index 016c019d10..966519bfd9 100644 --- a/tests/topotests/bgp_rpki_topo1/r2/bgp_rpki_valid.json +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_rpki_valid.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 3, "routerId": "192.0.2.2", "defaultLocPrf": 100, "localAS": 65002, @@ -11,7 +10,6 @@ "origin": "IGP", "metric": 0, "valid": true, - "version": 2, "rpkiValidationState": "valid", "bestpath": { "overall": true, @@ -40,7 +38,6 @@ "origin": "IGP", "metric": 0, "valid": true, - "version": 3, "rpkiValidationState": "valid", "bestpath": { "overall": true, diff --git a/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py b/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py index cf590ad01d..5c0b909517 100755 --- a/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py +++ b/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py @@ -159,6 +159,36 @@ def test_sid_reachable_again_bgp_update(): check_ping("c11", "192.168.2.1", True, 10, 1) +def test_sid_unreachable_no_router(): + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + no router bgp 65002 vrf vrf10 + """ + ) + check_ping("c11", "192.168.2.1", False, 10, 1) + + +def test_sid_reachable_again_no_router(): + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 65002 vrf vrf10 + bgp router-id 192.0.2.2 + ! + address-family ipv4 unicast + redistribute connected + sid vpn export 1 + rd vpn export 65002:10 + rt vpn both 0:10 + import vpn + export vpn + exit-address-family + """ + ) + check_ping("c11", "192.168.2.1", True, 10, 1) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_table_direct_topo1/__init__.py b/tests/topotests/bgp_table_direct_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/__init__.py diff --git a/tests/topotests/bgp_table_direct_topo1/r1/frr.conf b/tests/topotests/bgp_table_direct_topo1/r1/frr.conf new file mode 100644 index 0000000000..c45e3456a4 --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/r1/frr.conf @@ -0,0 +1,31 @@ +log commands +! +debug bgp zebra +debug zebra events +! +ip route 10.254.254.1/32 lo table 2000 +ip route 10.254.254.2/32 lo table 2000 +ip route 10.254.254.3/32 lo table 2000 +! +interface r1-eth0 + ip address 192.168.10.1/24 +! +interface r1-eth1 vrf blue + ip address 192.168.20.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.10.2 remote-as external + address-family ipv4 unicast + redistribute table-direct 2000 + exit-address-family +! +router bgp 65001 vrf blue + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.20.2 remote-as external + address-family ipv4 unicast + redistribute table-direct 2000 + exit-address-family +!
\ No newline at end of file diff --git a/tests/topotests/bgp_table_direct_topo1/r2/frr.conf b/tests/topotests/bgp_table_direct_topo1/r2/frr.conf new file mode 100644 index 0000000000..04787be0b3 --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/r2/frr.conf @@ -0,0 +1,10 @@ +log commands +! +interface r2-eth0 + ip address 192.168.10.2/24 +! +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.10.1 remote-as external +!
\ No newline at end of file diff --git a/tests/topotests/bgp_table_direct_topo1/r3/frr.conf b/tests/topotests/bgp_table_direct_topo1/r3/frr.conf new file mode 100644 index 0000000000..2530b28bfd --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/r3/frr.conf @@ -0,0 +1,10 @@ +log commands +! +interface r3-eth0 + ip address 192.168.20.2/24 +! +router bgp 65003 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.20.1 remote-as external +!
\ No newline at end of file diff --git a/tests/topotests/bgp_table_direct_topo1/test_bgp_table_direct_topo1.py b/tests/topotests/bgp_table_direct_topo1/test_bgp_table_direct_topo1.py new file mode 100644 index 0000000000..70257be3e7 --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/test_bgp_table_direct_topo1.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_table_direct_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2025 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bgp_table_direct_topo1.py: Test the FRR PIM MSDP peer. +""" + +import os +import sys +import json +from functools import partial +import re +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest + +# Required to instantiate the topology builder class. +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +from lib.pim import McastTesterHelper + +pytestmark = [pytest.mark.bgpd, pytest.mark.pimd] + +app_helper = McastTesterHelper() + + +def build_topo(tgen): + """ + +----+ +----+ + | r1 | <-> | r2 | + +----+ +----+ + | + | +----+ + --------| r3 | + +----+ + """ + + # Create 3 routers + for routern in range(1, 4): + tgen.add_router(f"r{routern}") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for _, router in router_list.items(): + router.load_frr_config(os.path.join(CWD, f"{router.name}/frr.conf")) + + tgen.gears["r1"].run("ip link add blue type vrf table 10") + tgen.gears["r1"].run("ip link set blue up") + tgen.gears["r1"].run("ip link set r1-eth1 master blue") + + # Initialize all routers. + tgen.start_router() + + app_helper.init(tgen) + + +def teardown_module(): + "Teardown the pytest environment" + tgen = get_topogen() + app_helper.cleanup() + tgen.stop_topology() + + +def expect_bgp_route(router, iptype, route, missing=False): + "Wait until route is present on RIB for protocol." + if missing: + logger.info("waiting route {} go missing in {}".format(route, router)) + else: + logger.info("waiting route {} in {}".format(route, router)) + + tgen = get_topogen() + expected_output = {route: [{"protocol": "bgp"}]} + wait_time = 130 + if missing: + expected_output = {route: None} + wait_time = 5 + + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show {} route json".format(iptype), + expected_output + ) + + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = f'"{router}" convergence failure' + assert result is None, assertmsg + + +def test_bgp_convergence(): + "Wait for BGP protocol convergence" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + # Wait for R2 + expect_bgp_route("r2", "ip", "10.254.254.1/32") + expect_bgp_route("r2", "ip", "10.254.254.2/32") + expect_bgp_route("r2", "ip", "10.254.254.3/32") + + # Wait for R3 + expect_bgp_route("r3", "ip", "10.254.254.1/32") + expect_bgp_route("r3", "ip", "10.254.254.2/32") + expect_bgp_route("r3", "ip", "10.254.254.3/32") + + +def test_route_change_convergence(): + "Change routes in table 2000 to test zebra redistribution." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r1"].vtysh_cmd(""" + configure terminal + no ip route 10.254.254.2/32 lo table 2000 + ip route 10.254.254.10/32 lo table 2000 + """) + + # Check R2 + expect_bgp_route("r2", "ip", "10.254.254.2/32", missing=True) + expect_bgp_route("r2", "ip", "10.254.254.10/32") + + # Check R3 + expect_bgp_route("r3", "ip", "10.254.254.2/32", missing=True) + expect_bgp_route("r3", "ip", "10.254.254.10/32") + + +def test_configuration_removal_convergence(): + "Remove table direct configuration and check if routes went missing." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r1"].vtysh_cmd(""" + configure terminal + router bgp 65001 + address-family ipv4 unicast + no redistribute table-direct 2000 + exit-address-family + exit + + router bgp 65001 vrf blue + address-family ipv4 unicast + no redistribute table-direct 2000 + exit-address-family + exit + """) + + # Check R2 + expect_bgp_route("r2", "ip", "10.254.254.1/32", missing=True) + expect_bgp_route("r2", "ip", "10.254.254.3/32", missing=True) + expect_bgp_route("r2", "ip", "10.254.254.10/32", missing=True) + + # Check R3 + expect_bgp_route("r3", "ip", "10.254.254.1/32", missing=True) + expect_bgp_route("r3", "ip", "10.254.254.3/32", missing=True) + expect_bgp_route("r3", "ip", "10.254.254.10/32", missing=True) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_table_map/r1/frr.conf b/tests/topotests/bgp_table_map/r1/frr.conf new file mode 100644 index 0000000000..f74440c384 --- /dev/null +++ b/tests/topotests/bgp_table_map/r1/frr.conf @@ -0,0 +1,22 @@ +! +int r1-eth0 + ip address 10.255.0.1/24 +! +access-list AccList seq 5 permit 10.0.0.1/32 +! +route-map TableMap permit 10 + match ip address AccList +exit +! +router bgp 65001 + bgp router-id 10.255.0.1 + no bgp ebgp-requires-policy + neighbor 10.255.0.2 remote-as external + neighbor 10.255.0.2 timers 1 3 + neighbor 10.255.0.2 timers connect 1 + ! + address-family ipv4 unicast + table-map TableMap + exit-address-family +exit +! diff --git a/tests/topotests/bgp_table_map/r2/frr.conf b/tests/topotests/bgp_table_map/r2/frr.conf new file mode 100644 index 0000000000..4523fe49ea --- /dev/null +++ b/tests/topotests/bgp_table_map/r2/frr.conf @@ -0,0 +1,18 @@ +! +int r2-eth0 + ip address 10.255.0.2/24 +! +router bgp 65002 + bgp router-id 10.255.0.2 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.255.0.1 remote-as external + neighbor 10.255.0.1 timers 1 3 + neighbor 10.255.0.1 timers connect 1 + ! + address-family ipv4 unicast + network 10.0.0.1/32 + network 10.0.0.2/32 + exit-address-family +exit +! diff --git a/tests/topotests/bgp_table_map/test_bgp_table_map.py b/tests/topotests/bgp_table_map/test_bgp_table_map.py new file mode 100644 index 0000000000..b10680f741 --- /dev/null +++ b/tests/topotests/bgp_table_map/test_bgp_table_map.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +import functools, json, os, pytest, re, sys + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +from lib import topotest +from lib.topogen import Topogen, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config( + os.path.join(CWD, "{}/frr.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_table_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads( + r1.vtysh_cmd( "show bgp ipv4 unicast summary json") + ) + expected = { + "peers": { + "10.255.0.2": { + "remoteAs": 65002, + "state": "Established", + "peerState": "OK", + }, + }, + "totalPeers": 1, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge initial state" + + def _bgp_with_table_map(): + output = json.loads(r1.vtysh_cmd("show ip fib json")) + expected = { + "10.0.0.1/32": [], + "10.0.0.2/32": None, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_with_table_map, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "Should contain only one of two shared networks" + + # + # Unset table-map + # + r1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + no table-map TableMap + """ + ) + + def _bgp_without_table_map(): + output = json.loads(r1.vtysh_cmd("show ip fib json")) + expected = { + "10.0.0.1/32": [], + "10.0.0.2/32": [], + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_without_table_map, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "Shouldn't contain both shared routes" + + # + # Reset table-map + # + r1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + table-map TableMap + """ + ) + + test_func = functools.partial( + _bgp_with_table_map, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "Should contain only one of two shared networks" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_advertised_10_125_0_2.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_advertised_10_125_0_2.json new file mode 100644 index 0000000000..7891982653 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_advertised_10_125_0_2.json @@ -0,0 +1,105 @@ +{ + "bgpLocalRouterId":"192.0.2.1", + "defaultLocPrf":100, + "localAS":65500, + "advertisedRoutes":{ + "192.0.2.1:1":{ + "rd":"192.0.2.1:1", + "10.101.0.0/24":{ + "prefix":"10.101.0.0/24", + "advertisedTo":{ + "10.125.0.2":{ + "hostname":"r2" + } + }, + "paths":[{ + "aspath":{ + "string":"Local", + "segments":[], + "length":0 + }, + "nhVrfName":"vrf1", + "announceNexthopSelf":true, + "origin":"incomplete", + "metric":0, + "locPrf":100, + "weight":32768, + "valid":true, + "sourced":true, + "local":true, + "bestpath":{ + "overall":true, + "selectionReason":"First path received" + }, + "extendedCommunity":{ + "string":"RT:192.0.2.1:100" + }, + "originatorId":"192.0.2.1", + "remoteLabel":101, + "nexthops":[{ + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "metric":0, + "accessible":true, + "used":true + }], + "peer":{ + "peerId":"0.0.0.0", + "routerId":"192.0.2.1" + } + }] + } + }, + "192.0.2.1:3":{ + "rd":"192.0.2.1:3", + "10.103.0.0/24":{ + "prefix":"10.103.0.0/24", + "advertisedTo":{ + "10.125.0.2":{ + "hostname":"r2" + } + }, + "paths":[{ + "aspath":{ + "string":"Local", + "segments":[], + "length":0 + }, + "nhVrfName":"vrf3", + "announceNexthopSelf":true, + "origin":"incomplete", + "metric":0, + "locPrf":100, + "weight":32768, + "valid":true, + "sourced":true, + "local":true, + "bestpath":{ + "overall":true, + "selectionReason":"First path received" + }, + "extendedCommunity":{ + "string":"RT:192.0.2.1:300" + }, + "originatorId":"192.0.2.1", + "remoteLabel":103, + "nexthops":[{ + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "metric":0, + "accessible":true, + "used":true + }], + "peer":{ + "peerId":"0.0.0.0", + "routerId":"192.0.2.1" + } + }] + } + } + }, + "totalPrefixCounter":2, + "filteredPrefixCounter":0 +} diff --git a/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py b/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py index ee84e375fb..ada37c28c1 100644 --- a/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py +++ b/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py @@ -218,6 +218,29 @@ def check_show_bgp_ipv4_vpn(rname, json_file): assert result is None, assertmsg +def check_show_bgp_ipv4_vpn_peer_advertised_routes(rname, peer, json_file): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears[rname] + + logger.info( + "Checking VPNv4 advertised routes for on {} for peer {}".format(rname, peer) + ) + + json_file = "{}/{}/{}".format(CWD, router.name, json_file) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv4 vpn neighbors {} advertised-routes detail json".format(peer), + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + def check_show_bgp_vrf_ipv4(rname, json_file): tgen = get_topogen() if tgen.routers_have_failure(): @@ -563,6 +586,21 @@ router bgp 65500 check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json") +def test_bgp_advertised_routes_step13(): + """ + Dump advertised routes from r1 to r2 + Check that the localpref attribute is set on the show command + """ + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + check_show_bgp_ipv4_vpn_peer_advertised_routes( + "r1", "10.125.0.2", "ipv4_vpn_routes_advertised_10_125_0_2.json" + ) + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/fpm_testing_topo1/r1/routes_summ.json b/tests/topotests/fpm_testing_topo1/r1/routes_summ.json index e9157bc664..12fe32cab3 100644 --- a/tests/topotests/fpm_testing_topo1/r1/routes_summ.json +++ b/tests/topotests/fpm_testing_topo1/r1/routes_summ.json @@ -3,21 +3,21 @@ { "fib":1, "rib":1, - "fibOffLoaded":0, + "fibOffLoaded":1, "fibTrapped":0, "type":"connected" }, { "fib":1, "rib":1, - "fibOffLoaded":0, + "fibOffLoaded":1, "fibTrapped":0, "type":"local" }, { "fib":10000, "rib":10000, - "fibOffLoaded":0, + "fibOffLoaded":10000, "fibTrapped":0, "type":"sharp" } diff --git a/tests/topotests/fpm_testing_topo1/r1/routes_summ_removed.json b/tests/topotests/fpm_testing_topo1/r1/routes_summ_removed.json index 8585b2bb6b..15d3f71077 100644 --- a/tests/topotests/fpm_testing_topo1/r1/routes_summ_removed.json +++ b/tests/topotests/fpm_testing_topo1/r1/routes_summ_removed.json @@ -3,14 +3,14 @@ { "fib":1, "rib":1, - "fibOffLoaded":0, + "fibOffLoaded":1, "fibTrapped":0, "type":"connected" }, { "fib":1, "rib":1, - "fibOffLoaded":0, + "fibOffLoaded":1, "fibTrapped":0, "type":"local" } diff --git a/tests/topotests/fpm_testing_topo1/test_fpm_topo1.py b/tests/topotests/fpm_testing_topo1/test_fpm_topo1.py index 66cefcc2a0..b3c375549a 100644 --- a/tests/topotests/fpm_testing_topo1/test_fpm_topo1.py +++ b/tests/topotests/fpm_testing_topo1/test_fpm_topo1.py @@ -57,7 +57,7 @@ def setup_module(module): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), - "-M dplane_fpm_nl", + "-M dplane_fpm_nl --asic-offload=notify_on_offload", ) router.load_config( TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) @@ -65,6 +65,7 @@ def setup_module(module): router.load_config( TopoRouter.RD_FPM_LISTENER, os.path.join(CWD, "{}/fpm_stub.conf".format(rname)), + "-r", ) tgen.start_router() @@ -111,7 +112,7 @@ def test_fpm_install_routes(): topotest.router_json_cmp, router, "show ip route summ json", expected ) - success, result = topotest.run_and_expect(test_func, None, 60, 1) + success, result = topotest.run_and_expect(test_func, None, 120, 1) assert success, "Unable to successfully install 10000 routes: {}".format(result) # Let's remove 10000 routes @@ -124,7 +125,7 @@ def test_fpm_install_routes(): topotest.router_json_cmp, router, "show ip route summ json", expected ) - success, result = topotest.run_and_expect(test_func, None, 60, 1) + success, result = topotest.run_and_expect(test_func, None, 120, 1) assert success, "Unable to remove 10000 routes: {}".format(result) diff --git a/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py b/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py index 9c1a23f54f..d17b4702f7 100644 --- a/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py +++ b/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py @@ -245,7 +245,7 @@ def check_ping6(name, dest_addr, expect_connected): if match not in output: return "ping fail" - match = "{} packet loss".format("0%" if expect_connected else "100%") + match = "{} packet loss".format(", 0%" if expect_connected else ", 100%") logger.info("[+] check {} {} {}".format(name, dest_addr, match)) tgen = get_topogen() func = functools.partial(_check, name, dest_addr, match) @@ -333,7 +333,7 @@ def test_ping_step1(): # Setup encap route on rt1, decap route on rt2 tgen.gears["rt1"].vtysh_cmd( - "sharp install seg6-routes fc00:0:9::1 nexthop-seg6 2001:db8:1::2 encap fc00:0:1:2:6:f00d:: 1" + "sharp install seg6-routes fc00:0:9::1 nexthop-seg6 2001:db8:1::2 encap fc00:0:2:6:f00d:: 1" ) tgen.gears["rt6"].vtysh_cmd( "sharp install seg6local-routes fc00:0:f00d:: nexthop-seg6local eth-dst End_DT6 254 1" @@ -443,7 +443,8 @@ def test_ping_step2(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - check_ping6("rt1", "fc00:0:9::1", False) + # ping should pass because route to fc00:0:2:6:f00d:: is still valid + check_ping6("rt1", "fc00:0:9::1", True) # @@ -643,7 +644,8 @@ def test_ping_step4(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - check_ping6("rt1", "fc00:0:9::1", False) + # ping should pass because route to fc00:0:2:6:f00d:: is still valid + check_ping6("rt1", "fc00:0:9::1", True) # @@ -838,7 +840,8 @@ def test_ping_step6(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - check_ping6("rt1", "fc00:0:9::1", False) + # ping should pass because route to fc00:0:2:6:f00d:: is still valid + check_ping6("rt1", "fc00:0:9::1", True) # @@ -1033,7 +1036,8 @@ def test_ping_step8(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - check_ping6("rt1", "fc00:0:9::1", False) + # ping should pass because route to fc00:0:2:6:f00d:: is still valid + check_ping6("rt1", "fc00:0:9::1", True) # diff --git a/tests/topotests/mgmt_notif/test_ds_notify.py b/tests/topotests/mgmt_notif/test_ds_notify.py new file mode 100644 index 0000000000..1759bf8df8 --- /dev/null +++ b/tests/topotests/mgmt_notif/test_ds_notify.py @@ -0,0 +1,238 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# +# January 14 2025, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2025, LabN Consulting, L.L.C. +# +""" +Test YANG Datastore Notifications +""" +import json +import logging +import os +import re +import time + +import pytest +from lib.topogen import Topogen +from lib.topotest import json_cmp +from munet.testing.util import waitline +from oper import check_kernel_32 + +pytestmark = [pytest.mark.ripd, pytest.mark.staticd, pytest.mark.mgmtd] + +CWD = os.path.dirname(os.path.realpath(__file__)) +FE_CLIENT = CWD + "/../lib/fe_client.py" + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = { + "s1": ("r1", "r2"), + } + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for _, router in router_list.items(): + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def get_op_and_json(output): + op = "" + path = "" + data = "" + for line in output.split("\n"): + if not line: + continue + if not op: + m = re.match("#OP=([A-Z]*): (.*)", line) + if m: + op = m.group(1) + path = m.group(2) + continue + data += line + "\n" + if not op: + assert False, f"No notifcation op present in:\n{output}" + return op, path, data + + +def test_frontend_datastore_notification(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + + check_kernel_32(r1, "11.11.11.11", 1, "") + + rc, _, _ = r1.cmd_status(FE_CLIENT + " --help") + + if rc: + pytest.skip("No protoc or present cannot run test") + + # Start our FE client in the background + p = r1.popen( + [FE_CLIENT, "--datastore", "--listen=/frr-interface:lib/interface/state"] + ) + assert waitline(p.stderr, "Connected", timeout=10) + + r1.cmd_raises("ip link set r1-eth0 mtu 1200") + + # {"frr-interface:lib":{"interface":[{"name":"r1-eth0","state":{"if-index":2,"mtu":1200,"mtu6":1200,"speed":10000,"metric":0,"phy-address":"ba:fd:de:b5:8b:90"}}]}} + + try: + # Wait for FE client to exit + output, error = p.communicate(timeout=10) + op, path, data = get_op_and_json(output) + + assert op == "REPLACE" + assert path.startswith("/frr-interface:lib/interface[name='r1-eth0']/state") + + jsout = json.loads(data) + expected = json.loads( + '{"frr-interface:lib":{"interface":[{"name":"r1-eth0","state":{"mtu":1200}}]}}' + ) + result = json_cmp(jsout, expected) + assert result is None + finally: + p.kill() + r1.cmd_raises("ip link set r1-eth0 mtu 1500") + + +def test_backend_datastore_update(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + + check_kernel_32(r1, "11.11.11.11", 1, "") + + be_client_path = "/usr/lib/frr/mgmtd_testc" + rc, _, _ = r1.cmd_status(be_client_path + " --help") + + if rc: + pytest.skip("No mgmtd_testc") + + # Start our BE client in the background + p = r1.popen( + [ + be_client_path, + "--timeout=20", + "--log=file:/dev/stderr", + "--datastore", + "--listen", + "/frr-interface:lib/interface", + ] + ) + assert waitline(p.stderr, "Got SUBSCR_REPLY success 1", timeout=10) + + r1.cmd_raises("ip link set r1-eth0 mtu 1200") + try: + expected = json.loads( + '{"frr-interface:lib":{"interface":[{"name":"r1-eth0","state":{"mtu":1200}}]}}' + ) + + output, error = p.communicate(timeout=10) + op, path, data = get_op_and_json(output) + jsout = json.loads(data) + result = json_cmp(jsout, expected) + assert result is None + finally: + p.kill() + r1.cmd_raises("ip link set r1-eth0 mtu 1500") + + +def test_backend_datastore_add_delete(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + + check_kernel_32(r1, "11.11.11.11", 1, "") + + be_client_path = "/usr/lib/frr/mgmtd_testc" + rc, _, _ = r1.cmd_status(be_client_path + " --help") + + if rc: + pytest.skip("No mgmtd_testc") + + # Start our BE client in the background + p = r1.popen( + [ + be_client_path, + "--timeout=20", + "--log=file:/dev/stderr", + "--notify-count=2", + "--datastore", + "--listen", + "/frr-interface:lib/interface", + ] + ) + assert waitline(p.stderr, "Got SUBSCR_REPLY success 1", timeout=10) + + r1.cmd_raises('vtysh -c "conf t" -c "int foobar"') + try: + assert waitline( + p.stdout, + re.escape('#OP=REPLACE: /frr-interface:lib/interface[name="foobar"]/state'), + timeout=2, + ) + + r1.cmd_raises('vtysh -c "conf t" -c "no int foobar"') + assert waitline( + p.stdout, + re.escape('#OP=DELETE: /frr-interface:lib/interface[name="foobar"]/state'), + timeout=2, + ) + finally: + p.kill() + r1.cmd_raises('vtysh -c "conf t" -c "no int foobar"') + + +def test_datastore_backend_filters(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + + check_kernel_32(r1, "11.11.11.11", 1, "") + + rc, _, _ = r1.cmd_status(FE_CLIENT + " --help") + if rc: + pytest.skip("No protoc or present cannot run test") + + # Start our FE client in the background + p = r1.popen( + [FE_CLIENT, "--datastore", "--listen=/frr-interface:lib/interface/state"] + ) + assert waitline(p.stderr, "Connected", timeout=10) + time.sleep(1) + + try: + output = r1.cmd_raises( + 'vtysh -c "show mgmt get-data /frr-backend:clients/client/state/notify-selectors"' + ) + jsout = json.loads(output) + + # + # Verify only zebra has the notify selector as it's the only provider currently + # + state = {"notify-selectors": ["/frr-interface:lib/interface/state"]} + expected = { + "frr-backend:clients": {"client": [{"name": "zebra", "state": state}]} + } + + result = json_cmp(jsout, expected, exact=True) + assert result is None + except Exception as error: + logging.error("got exception: %s", error) + raise + finally: + p.kill() diff --git a/tests/topotests/mgmt_notif/test_notif.py b/tests/topotests/mgmt_notif/test_notif.py index 526f051e6b..f3c7c8bc81 100644 --- a/tests/topotests/mgmt_notif/test_notif.py +++ b/tests/topotests/mgmt_notif/test_notif.py @@ -5,17 +5,13 @@ # # Copyright (c) 2024, LabN Consulting, L.L.C. # - """ -Test YANG Notifications +Test Traditional YANG Notifications """ import json -import logging import os -import re import pytest -from lib.micronet import Timeout, comm_error from lib.topogen import Topogen from lib.topotest import json_cmp from oper import check_kernel_32 @@ -45,99 +41,6 @@ def tgen(request): tgen.stop_topology() -def myreadline(f): - buf = "" - while True: - # logging.debug("READING 1 CHAR") - c = f.read(1) - if not c: - return buf if buf else None - buf += c - # logging.debug("READ CHAR: '%s'", c) - if c == "\n": - return buf - - -def _wait_output(f, regex, maxwait=120): - timeout = Timeout(maxwait) - while not timeout.is_expired(): - # line = p.stdout.readline() - line = myreadline(f) - if not line: - assert None, "EOF waiting for '{}'".format(regex) - line = line.rstrip() - if line: - logging.debug("GOT LINE: '%s'", line) - m = re.search(regex, line) - if m: - return m - assert None, "Failed to get output matching '{}' withint {} actual {}s".format( - regex, maxwait, timeout.elapsed() - ) - - -def get_op_and_json(output): - op = "" - path = "" - data = "" - for line in output.split("\n"): - if not line: - continue - if not op: - m = re.match("#OP=([A-Z]*): (.*)", line) - if m: - op = m.group(1) - path = m.group(2) - continue - data += line + "\n" - if not op: - assert False, f"No notifcation op present in:\n{output}" - return op, path, data - - -def test_frontend_datastore_notification(tgen): - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - r1 = tgen.gears["r1"].net - - check_kernel_32(r1, "11.11.11.11", 1, "") - - fe_client_path = CWD + "/../lib/fe_client.py" - rc, _, _ = r1.cmd_status(fe_client_path + " --help") - - if rc: - pytest.skip("No protoc or present cannot run test") - - # Start our FE client in the background - p = r1.popen( - [fe_client_path, "--datastore", "--listen=/frr-interface:lib/interface"] - ) - _wait_output(p.stderr, "Connected", maxwait=10) - - r1.cmd_raises("ip link set r1-eth0 mtu 1200") - - # {"frr-interface:lib":{"interface":[{"name":"r1-eth0","state":{"if-index":2,"mtu":1200,"mtu6":1200,"speed":10000,"metric":0,"phy-address":"ba:fd:de:b5:8b:90"}}]}} - - try: - # Wait for FE client to exit - output, error = p.communicate(timeout=10) - op, path, data = get_op_and_json(output) - - assert op == "REPLACE" - assert path.startswith("/frr-interface:lib/interface[name='r1-eth0']/state") - - jsout = json.loads(data) - expected = json.loads( - '{"frr-interface:lib":{"interface":[{"name":"r1-eth0","state":{"mtu":1200}}]}}' - ) - result = json_cmp(jsout, expected) - assert result is None - finally: - p.kill() - r1.cmd_raises("ip link set r1-eth0 mtu 1500") - - def test_frontend_notification(tgen): if tgen.routers_have_failure(): pytest.skip(tgen.errors) @@ -240,7 +143,7 @@ def test_frontend_all_notification(tgen): r1.cmd_raises("vtysh", stdin=conf) -def test_backend_notification(tgen): +def test_backend_yang_notification(tgen): if tgen.routers_have_failure(): pytest.skip(tgen.errors) diff --git a/tests/topotests/mgmt_tests/test_yang_mgmt.py b/tests/topotests/mgmt_tests/test_yang_mgmt.py index 52f6ba4db7..7b74eab6b7 100644 --- a/tests/topotests/mgmt_tests/test_yang_mgmt.py +++ b/tests/topotests/mgmt_tests/test_yang_mgmt.py @@ -181,7 +181,7 @@ def test_mgmt_commit_check(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", "mgmt commit check", ] } @@ -194,7 +194,7 @@ def test_mgmt_commit_check(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", "mgmt commit check", ] } @@ -245,7 +245,7 @@ def test_mgmt_commit_apply(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", "mgmt commit apply", ] } @@ -258,7 +258,7 @@ def test_mgmt_commit_apply(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", "mgmt commit apply", ] } @@ -298,7 +298,7 @@ def test_mgmt_commit_abort(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.3/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.3/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", "mgmt commit abort", ] } @@ -350,7 +350,7 @@ def test_mgmt_delete_config(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", "mgmt commit apply", ] } @@ -381,7 +381,7 @@ def test_mgmt_delete_config(request): raw_config = { "r1": { "raw_config": [ - "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][afi-safi='frr-routing:ipv4-unicast']", + "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']", "mgmt commit apply", ] } @@ -657,7 +657,7 @@ def test_mgmt_chaos_stop_start_frr(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", "mgmt commit apply", ] } @@ -689,7 +689,7 @@ def test_mgmt_chaos_stop_start_frr(request): raw_config = { "r1": { "raw_config": [ - "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']", + "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']", "mgmt commit apply", ] } @@ -733,7 +733,7 @@ def test_mgmt_chaos_kill_daemon(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", "mgmt commit apply", ] } diff --git a/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py index 8d91826022..a32b82c7f4 100755 --- a/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py +++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py @@ -638,12 +638,6 @@ def pre_config_for_source_dr_tests( "interfaceName": "r5-r4-eth1", "weight": 1, }, - { - "ip": "10.0.3.1", - "afi": "ipv4", - "interfaceName": "r5-r4-eth1", - "weight": 1, - }, ], } ] diff --git a/tests/topotests/ospf_prune_next_hop/r1/frr.conf b/tests/topotests/ospf_prune_next_hop/r1/frr.conf new file mode 100644 index 0000000000..130872e8d0 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r1/frr.conf @@ -0,0 +1,23 @@ +! +hostname r1 +ip forwarding +! +interface r1-eth0 + ip address 10.1.1.1/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r1-eth1 + ip address 10.1.2.1/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +! +router ospf + ospf router-id 1.1.1.1 + distance 20 + network 10.1.1.0/24 area 0 + network 10.1.2.0/24 area 0 diff --git a/tests/topotests/ospf_prune_next_hop/r2/frr.conf b/tests/topotests/ospf_prune_next_hop/r2/frr.conf new file mode 100644 index 0000000000..4268aea857 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r2/frr.conf @@ -0,0 +1,23 @@ +! +hostname r2 +ip forwarding +! +interface r2-eth0 + ip address 10.1.1.2/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r2-eth1 + ip address 10.1.2.1/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +! +router ospf + ospf router-id 2.2.2.2 + distance 20 + network 10.1.1.0/24 area 0 + network 10.1.2.0/24 area 0 diff --git a/tests/topotests/ospf_prune_next_hop/r3/frr.conf b/tests/topotests/ospf_prune_next_hop/r3/frr.conf new file mode 100644 index 0000000000..21d6506d7c --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r3/frr.conf @@ -0,0 +1,35 @@ +! +hostname r3 +ip forwarding +! +interface r3-eth0 + ip address 20.1.3.3/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r3-eth1 + ip address 10.1.3.3/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r3-eth2 + ip address 10.1.2.3/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +! +router ospf + ospf router-id 3.3.3.3 + distance 20 + network 10.1.2.0/24 area 0 + network 10.1.3.0/24 area 0 + network 20.1.3.0/24 area 1 + area 1 range 20.1.0.0/16 + redistribute static +! +! +ip route 100.100.100.100/32 Null0 diff --git a/tests/topotests/ospf_prune_next_hop/r4/frr.conf b/tests/topotests/ospf_prune_next_hop/r4/frr.conf new file mode 100644 index 0000000000..e66e93e20c --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r4/frr.conf @@ -0,0 +1,34 @@ +! +hostname r4 +ip forwarding +! +interface r4-eth0 + ip address 20.1.4.4/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r4-eth1 + ip address 10.1.3.4/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r4-eth2 + ip address 10.1.2.4/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +router ospf + ospf router-id 4.4.4.4 + distance 20 + network 10.1.2.0/24 area 0 + network 10.1.3.0/24 area 0 + network 20.1.4.0/24 area 1 + area 1 range 20.1.0.0/16 + redistribute static +! +! +ip route 100.100.100.100/32 Null0 diff --git a/tests/topotests/ospf_prune_next_hop/r5/frr.conf b/tests/topotests/ospf_prune_next_hop/r5/frr.conf new file mode 100644 index 0000000000..2d1dad9925 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r5/frr.conf @@ -0,0 +1,34 @@ +! +hostname r5 +ip forwarding +! +interface r5-eth0 + ip address 20.1.5.5/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r5-eth1 + ip address 10.1.3.5/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r5-eth2 + ip address 10.1.2.5/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +router ospf + ospf router-id 5.5.5.5 + distance 20 + network 10.1.2.0/24 area 0 + network 10.1.3.0/24 area 0 + network 20.1.5.0/24 area 1 + area 1 range 20.1.0.0/16 + redistribute static +! +! +ip route 100.100.100.100/32 Null0 diff --git a/tests/topotests/ospf_prune_next_hop/r6/frr.conf b/tests/topotests/ospf_prune_next_hop/r6/frr.conf new file mode 100644 index 0000000000..f343ee7c35 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r6/frr.conf @@ -0,0 +1,34 @@ +! +hostname r6 +ip forwarding +! +interface r6-eth0 + ip address 20.1.6.6/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r6-eth1 + ip address 10.1.3.6/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r6-eth2 + ip address 10.1.2.6/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +router ospf + ospf router-id 6.6.6.6 + distance 20 + network 10.1.2.0/24 area 0 + network 10.1.3.0/24 area 0 + network 20.1.6.0/24 area 1 + area 1 range 20.1.0.0/16 + redistribute static +! +! +ip route 100.100.100.100/32 Null0 diff --git a/tests/topotests/ospf_prune_next_hop/r7/frr.conf b/tests/topotests/ospf_prune_next_hop/r7/frr.conf new file mode 100644 index 0000000000..1eeb88c9d0 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r7/frr.conf @@ -0,0 +1,14 @@ +! +hostname r7 +ip forwarding +! +interface r7-eth0 + ip address 10.1.3.7/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +router ospf + ospf router-id 7.7.7.7 + distance 20 + network 10.1.3.0/24 area 0 diff --git a/tests/topotests/ospf_prune_next_hop/r8/frr.conf b/tests/topotests/ospf_prune_next_hop/r8/frr.conf new file mode 100644 index 0000000000..d8facbc01f --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r8/frr.conf @@ -0,0 +1,14 @@ +! +hostname r8 +ip forwarding +! +interface r8-eth0 + ip address 10.1.3.8/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +router ospf + ospf router-id 8.8.8.8 + distance 20 + network 10.1.3.0/24 area 0 diff --git a/tests/topotests/ospf_prune_next_hop/test_ospf_prune_next_hop.py b/tests/topotests/ospf_prune_next_hop/test_ospf_prune_next_hop.py new file mode 100644 index 0000000000..88aa6b2e36 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/test_ospf_prune_next_hop.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_prune_next_hop +# +# Copyright (c) 2025 LabN Consulting +# Acee Lindem +# + +import os +import sys +from functools import partial +import pytest + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger + +from lib.common_config import ( + step, +) + + +""" +test_ospf_metric_propagation.py: Test OSPF/BGP metric propagation +""" + +TOPOLOGY = """ + 20.1.3.0 20.1.4.0 20.1.5.0 20.1.6.0 + eth0 | .3 eth0 | .4 eth0 | .5 eth0 | .6 + +--+-+ +--+-+ +--+-+ +--+-+ +10.1 3.0 | R3 | | R4 | | R5 | | R6 | + +-----+ | | |eth1 | |eth1 | | 10.1.3.0/24 + | | | | +---- | |--- + -+---+ + | +--+-+ +--+-+ +--+-+ +--+-+ | + | eth2 | .3 eth2 | .4 eth2 | .5 eth2 | | +eth0| | | | | | eth0 + +--+--+ ++-------+ Switch Network +---------++ +--+---+ + | R7 | | 10.1.2.0/24 | | R8 | + +-----+ +------------------------------------+ +------+ + eth1 | .2 + +--+--+ + | R2 | + +--+--+ + eth0 | .2 + 10.1.1.0/24 | + eth0 | .1 + +--+--+ + | R1 | + +-----+ + +""" + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 8 routers + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r3") + tgen.add_router("r4") + tgen.add_router("r5") + tgen.add_router("r6") + tgen.add_router("r7") + tgen.add_router("r8") + + # Interconect router 1, 2 (0) + switch = tgen.add_switch("s1-1-2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # Add standalone networks to router 3 + switch = tgen.add_switch("s2-3") + switch.add_link(tgen.gears["r3"]) + + # Add standalone network to router 4 + switch = tgen.add_switch("s3-4") + switch.add_link(tgen.gears["r4"]) + + # Add standalone network to router 5 + switch = tgen.add_switch("s4-5") + switch.add_link(tgen.gears["r5"]) + + # Add standalone network to router 6 + switch = tgen.add_switch("s5-6") + switch.add_link(tgen.gears["r6"]) + + # Interconect routers 3, 4, 5, and 6 + switch = tgen.add_switch("s6-3") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r7"]) + switch = tgen.add_switch("s7-4") + switch.add_link(tgen.gears["r4"]) + switch = tgen.add_switch("s8-5") + switch.add_link(tgen.gears["r5"]) + switch = tgen.add_switch("s9-6") + switch.add_link(tgen.gears["r6"]) + switch.add_link(tgen.gears["r8"]) + + # Interconect routers 2, 3, 4, 5, and 6 + switch = tgen.add_switch("s10-lan") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["r5"]) + switch.add_link(tgen.gears["r6"]) + + +def setup_module(mod): + logger.info("OSPF Prune Next Hops:\n {}".format(TOPOLOGY)) + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + # Starting Routers + router_list = tgen.routers() + + for rname, router in router_list.items(): + logger.info("Loading router %s" % rname) + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_intra_area_route_prune(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("Skipped because of router(s) failure") + + step("Test OSPF intra-area route 10.1.3.0/24 duplicate nexthops already pruned") + # Verify OSPF route 10.1.3.0/24 nexthops pruned already. + r1 = tgen.gears["r1"] + input_dict = { + "10.1.3.0/24": { + "routeType": "N", + "transit": True, + "cost": 30, + "area": "0.0.0.0", + "nexthops": [ + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "8.8.8.8"} + ], + } + } + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf route detail json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "OSPF Intra-Area route 10.1.3.0/24 mismatch on router r1" + assert result is None, assertmsg + + step("Test IP route 10.1.3.0/24 installed") + input_dict = { + "10.1.3.0/24": [ + { + "prefix": "10.1.3.0/24", + "prefixLen": 24, + "protocol": "ospf", + "vrfName": "default", + "distance": 20, + "metric": 30, + "installed": True, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "ip": "10.1.1.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": True, + "weight": 1, + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 10.1.3.0/24 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "IP route 10.1.3.0/24 mismatch on router r1" + assert result is None, assertmsg + + +def test_inter_area_route_prune(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("Skipped because of router(s) failure") + + step("Test OSPF inter-area route 20.1.0.0/16 duplicate nexthops installed") + # Verify OSPF route 20.1.0.0/16 duplication nexthops + r1 = tgen.gears["r1"] + input_dict = { + "20.1.0.0/16": { + "routeType": "N IA", + "cost": 30, + "area": "0.0.0.0", + "nexthops": [ + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "3.3.3.3"}, + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "4.4.4.4"}, + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "5.5.5.5"}, + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "6.6.6.6"}, + ], + } + } + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf route detail json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "OSPF Inter-Area route 20.1.0.0/16 mismatch on router r1" + assert result is None, assertmsg + + step("Test IP route 10.1.3.0/24 installed with pruned next-hops") + input_dict = { + "20.1.0.0/16": [ + { + "prefix": "20.1.0.0/16", + "prefixLen": 16, + "protocol": "ospf", + "vrfName": "default", + "distance": 20, + "metric": 30, + "installed": True, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "ip": "10.1.1.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": True, + "weight": 1, + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 20.1.0.0/16 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "IP route 20.1.1.0/24 mismatch on router r1" + assert result is None, assertmsg + + +def test_as_external_route_prune(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("Skipped because of router(s) failure") + + step("Test OSPF AS external route 100.100.100.100 duplicate nexthops installed") + # Verify OSPF route 20.1.0.0/16 duplication nexthops + r1 = tgen.gears["r1"] + input_dict = { + "100.100.100.100/32": { + "routeType": "N E2", + "cost": 20, + "type2cost": 20, + "tag": 0, + "nexthops": [ + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "3.3.3.3"}, + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "4.4.4.4"}, + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "5.5.5.5"}, + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "6.6.6.6"}, + ], + } + } + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf route detail json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "OSPF AS external route 100.100.100.100/32 mismatch on router r1" + assert result is None, assertmsg + + step("Test IP route 100.100.100.100/32 installed with pruned next-hops") + input_dict = { + "100.100.100.100/32": [ + { + "prefix": "100.100.100.100/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfName": "default", + "distance": 20, + "metric": 20, + "installed": True, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "ip": "10.1.1.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": True, + "weight": 1, + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip route 100.100.100.100/32 json", + input_dict, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "IP route 100.100.100.100/32 mismatch on router r1" + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/sbfd_topo1/__init__.py b/tests/topotests/sbfd_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/sbfd_topo1/__init__.py diff --git a/tests/topotests/sbfd_topo1/r1/frr.conf b/tests/topotests/sbfd_topo1/r1/frr.conf new file mode 100644 index 0000000000..f865b81303 --- /dev/null +++ b/tests/topotests/sbfd_topo1/r1/frr.conf @@ -0,0 +1,8 @@ +ip forwarding +ipv6 forwarding +! + +interface r1-eth0 + ipv6 address 2001::10/64 +! +! diff --git a/tests/topotests/sbfd_topo1/r2/frr.conf b/tests/topotests/sbfd_topo1/r2/frr.conf new file mode 100644 index 0000000000..c9d166cabb --- /dev/null +++ b/tests/topotests/sbfd_topo1/r2/frr.conf @@ -0,0 +1,8 @@ +ip forwarding +ipv6 forwarding +! + +interface r2-eth0 + ipv6 address 2001::20/64 +! +! diff --git a/tests/topotests/sbfd_topo1/sbfd_topo1.dot b/tests/topotests/sbfd_topo1/sbfd_topo1.dot new file mode 100644 index 0000000000..437e8230ce --- /dev/null +++ b/tests/topotests/sbfd_topo1/sbfd_topo1.dot @@ -0,0 +1,45 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="template"; + + # Routers + r1 [ + shape=doubleoctagon, + label="A\nAS 100\n1.1.1.1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="B\nAS 200\n1.1.1.2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + s1 [ + shape=oval, + label="s1\n192.168.0.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + + # Connections + r1 -- s1 [label="A-eth0"]; + r2 -- s1 [label="B-eth0"]; + +} diff --git a/tests/topotests/sbfd_topo1/test_sbfd_topo1.py b/tests/topotests/sbfd_topo1/test_sbfd_topo1.py new file mode 100644 index 0000000000..274fbe6138 --- /dev/null +++ b/tests/topotests/sbfd_topo1/test_sbfd_topo1.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python + +# +# test_sbfd_topo1.py +# basic test cases for sbfd initiator and reflector +# +# Copyright (c) 2025 by Alibaba, Inc. +# +# 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. +# + +""" +<template>.py: Test <template>. +""" + +import os +import sys +import pytest +import json +import re +import time +import pdb +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import required_linux_kernel_version + +""" +test_sbfd_topo1.py: test simple sbfd with IPv6 encap. RT1 is sbfd Initiator, RT2 is sbfd Reflector + + +----+----+ +----+----+ + | | | | + | RT1 | 1 | RT2 | + | +--------+ | + | 2001::10| | 2001::20| + +----+----+ +----+----+ + +""" +pytestmark = [pytest.mark.bfdd] + +def show_bfd_check(router, status, type='echo', encap=None): + output = router.cmd("vtysh -c 'show bfd peers'") + if encap: + # check encap data if any + pattern1 = re.compile(r'encap-data {}'.format(encap)) + ret = pattern1.findall(output) + if len(ret) <= 0: + logger.info("encap-data not match") + return False + + # check status + pattern2 = re.compile(r'Status: {}'.format(status)) + ret = pattern2.findall(output) + if len(ret) <= 0: + logger.info("Status not match") + return False + + # check type + pattern3 = re.compile(r'Peer Type: {}'.format(type)) + ret = pattern3.findall(output) + if len(ret) <= 0: + logger.info("Peer Type not match") + return False + + logger.info("all check passed") + return True + +def build_topo(tgen): + "Test topology builder" + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # Example + # + # Create 2 routers + for routern in range(1, 3): + tgen.add_router('r{}'.format(routern)) + + # Create a switch with just one router connected to it to simulate a + # empty network. + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + + for rname, router in router_list.items(): + router.load_frr_config( + os.path.join(CWD, "{}/frr.conf".format(rname)), + [(TopoRouter.RD_ZEBRA, None), (TopoRouter.RD_BFD, None)]) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + # Verify that we are using the proper version and that the BFD + # daemon exists. + for router in router_list.values(): + # Check for Version + if router.has_version('<', '5.1'): + tgen.set_error('Unsupported FRR version') + break + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + # This function tears down the whole topology. + tgen.stop_topology() + + +# step 1 : config sbfd Initiator and reflector +def test_sbfd_config_check(): + "Assert that config sbfd and check sbfd status." + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.5") + if result is not True: + pytest.skip("Kernel requirements are not met") + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # config sbfd + r1 = tgen.net['r1'] + r1.cmd("ping -c 5 2001::20") + r1.cmd("vtysh -c 'config t' -c 'bfd' -c 'peer 2001::20 bfd-mode sbfd-init bfd-name 2-44 local-address 2001::10 remote-discr 1234'") + + r2 = tgen.net['r2'] + r2.cmd("vtysh -c 'config t' -c 'bfd' -c 'sbfd reflector source-address 2001::20 discriminator 1234'") + + check_func = partial( + show_bfd_check, r1, 'up', type='sbfd initiator' + ) + success, _ = topotest.run_and_expect(check_func, True, count=15, wait=1) + assert success is True, "sbfd not up in 15 seconds" + +# step 2: shutdown if and no shutdown if then check sbfd status +def test_sbfd_updown_interface(): + "Assert that updown interface then check sbfd status." + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.5") + if result is not True: + pytest.skip("Kernel requirements are not met") + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.net['r1'] + r2 = tgen.net['r2'] + + # shutdown interface + r2.cmd("vtysh -c 'config t' -c 'interface r2-eth0' -c 'shutdown'") + + check_func = partial( + show_bfd_check, r1, 'down', type='sbfd initiator' + ) + success, _ = topotest.run_and_expect(check_func, True, count=15, wait=1) + assert success is True, "sbfd not down in 15 seconds after shut" + + # up interface + r2.cmd("vtysh -c 'config t' -c 'interface r2-eth0' -c 'no shutdown'") + check_func = partial( + show_bfd_check, r1, 'up', type='sbfd initiator' + ) + success, _ = topotest.run_and_expect(check_func, True, count=15, wait=1) + assert success is True, "sbfd not up in 15 seconds after no shut" + +# step 3: change transmit-interval and check sbfd status according to the interval time +def test_sbfd_change_transmit_interval(): + "Assert that sbfd status changes align with transmit-interval." + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.5") + if result is not True: + pytest.skip("Kernel requirements are not met") + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.net['r1'] + r2 = tgen.net['r2'] + + r1.cmd("vtysh -c 'config t' -c 'bfd' -c 'peer 2001::20 bfd-mode sbfd-init bfd-name 2-44 local-address 2001::10 remote-discr 1234' -c 'transmit-interval 3000'") + #wait sometime for polling finish + time.sleep(1) + + # shutdown interface + r2.cmd("vtysh -c 'config t' -c 'interface r2-eth0' -c 'shutdown'") + + #wait enough time for timeout + check_func = partial( + show_bfd_check, r1, 'down', type='sbfd initiator' + ) + success, _ = topotest.run_and_expect(check_func, True, count=5, wait=3) + assert success is True, "sbfd not down as expected" + + r2.cmd("vtysh -c 'config t' -c 'interface r2-eth0' -c 'no shutdown'") + check_func = partial( + show_bfd_check, r1, 'up', type='sbfd initiator' + ) + success, _ = topotest.run_and_expect(check_func, True, count=15, wait=1) + assert success is True, "sbfd not up in 15 seconds after no shut" + + r1.cmd("vtysh -c 'config t' -c 'bfd' -c 'no peer 2001::20 bfd-mode sbfd-init bfd-name 2-44 local-address 2001::10 remote-discr 1234'") + success = show_bfd_check(r1, 'up', type='sbfd initiator') + assert success is False, "sbfd not deleted as unexpected" + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/srv6_static_route_ipv4/__init__.py b/tests/topotests/srv6_static_route_ipv4/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/srv6_static_route_ipv4/__init__.py diff --git a/tests/topotests/srv6_static_route_ipv4/expected_srv6_route.json b/tests/topotests/srv6_static_route_ipv4/expected_srv6_route.json new file mode 100644 index 0000000000..57f4c4488d --- /dev/null +++ b/tests/topotests/srv6_static_route_ipv4/expected_srv6_route.json @@ -0,0 +1,28 @@ +{ + "192.0.2.0/24": [ + { + "prefix": "192.0.2.0/24", + "prefixLen": 24, + "protocol": "static", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "nexthops": [ + { + "directlyConnected": true, + "active": true, + "weight": 1, + "seg6local": { + "action": "unspec" + }, + "seg6": [ + "fcbb:bbbb:1:2:3:4:5:6", + "fcbb:bbbb:7:8:fe00::" + ] + } + ] + } + ] +} diff --git a/tests/topotests/srv6_static_route_ipv4/r1/frr.conf b/tests/topotests/srv6_static_route_ipv4/r1/frr.conf new file mode 100644 index 0000000000..8ff23ec1b8 --- /dev/null +++ b/tests/topotests/srv6_static_route_ipv4/r1/frr.conf @@ -0,0 +1,7 @@ +hostname r1 +! +log stdout notifications +log commands +! +ipv6 route fcbb:bbbb:1::/48 sr0 +ip route 192.0.2.0/24 sr0 segments fcbb:bbbb:1:2:3:4:5:6/fcbb:bbbb:7:8:fe00:: diff --git a/tests/topotests/srv6_static_route_ipv4/r1/setup.sh b/tests/topotests/srv6_static_route_ipv4/r1/setup.sh new file mode 100644 index 0000000000..4b6cce89f8 --- /dev/null +++ b/tests/topotests/srv6_static_route_ipv4/r1/setup.sh @@ -0,0 +1,2 @@ +ip link add sr0 type dummy +ip link set sr0 up diff --git a/tests/topotests/srv6_static_route_ipv4/test_srv6_route.py b/tests/topotests/srv6_static_route_ipv4/test_srv6_route.py new file mode 100755 index 0000000000..b49a9cec89 --- /dev/null +++ b/tests/topotests/srv6_static_route_ipv4/test_srv6_route.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_srv6_static_route_ipv4.py +# +# Copyright 2025 +# Carmine Scarpitta <cscarpit.@cisco.com> +# + +""" +test_srv6_static_route_ipv4.py: +Test for SRv6 static route on zebra +""" + +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.topolog import logger + +pytestmark = [pytest.mark.staticd] + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def setup_module(mod): + tgen = Topogen({None: "r1"}, mod.__name__) + tgen.start_topology() + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) + router.load_frr_config("frr.conf") + tgen.start_router() + + +def teardown_module(): + tgen = get_topogen() + tgen.stop_topology() + + +def test_srv6_static_route(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + def _check_srv6_static_route(router, expected_route_file): + logger.info("checking zebra srv6 static route with multiple segs status") + output = json.loads(router.vtysh_cmd("show ip route static json")) + expected = open_json_file("{}/{}".format(CWD, expected_route_file)) + return topotest.json_cmp(output, expected) + + def check_srv6_static_route(router, expected_file): + func = functools.partial(_check_srv6_static_route, router, expected_file) + _, result = topotest.run_and_expect(func, None, count=15, wait=1) + assert result is None, "Failed" + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Test for srv6 route configuration") + check_srv6_static_route(router, "expected_srv6_route.json") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/static_simple/test_static_simple.py b/tests/topotests/static_simple/test_static_simple.py index bb3580a1d8..afde58fbf7 100644 --- a/tests/topotests/static_simple/test_static_simple.py +++ b/tests/topotests/static_simple/test_static_simple.py @@ -61,6 +61,15 @@ def get_ip_networks(super_prefix, count): return tuple(network.subnets(count_log2))[0:count] +def get_src_networks(src_prefix, count, default=""): + if src_prefix is not None: + for net in get_ip_networks(src_prefix, count): + yield " from {}".format(net) + else: + for i in range(0, count): + yield default + + def enable_debug(router): router.vtysh_cmd("debug northbound callbacks configuration") @@ -70,7 +79,7 @@ def disable_debug(router): @retry(retry_timeout=30, initial_wait=0.1) -def check_kernel(r1, super_prefix, count, add, is_blackhole, vrf, matchvia): +def check_kernel(r1, super_prefix, src_prefix, count, add, is_blackhole, vrf, matchvia): network = ipaddress.ip_network(super_prefix) vrfstr = f" vrf {vrf}" if vrf else "" if network.version == 6: @@ -79,26 +88,30 @@ def check_kernel(r1, super_prefix, count, add, is_blackhole, vrf, matchvia): kernel = r1.run(f"ip -4 route show{vrfstr}") logger.debug("checking kernel routing table%s:\n%s", vrfstr, kernel) - for _, net in enumerate(get_ip_networks(super_prefix, count)): + for net, srcnet in zip( + get_ip_networks(super_prefix, count), get_src_networks(src_prefix, count) + ): + netfull = str(net) + srcnet if not add: - assert str(net) not in kernel + assert netfull + " nhid" not in kernel + assert netfull + " via" not in kernel continue if is_blackhole: - route = f"blackhole {str(net)} proto (static|196) metric 20" + route = f"blackhole {netfull}(?: dev lo)? proto (static|196) metric 20" else: route = ( - f"{str(net)}(?: nhid [0-9]+)? {matchvia} " - "proto (static|196) metric 20" + f"{netfull}(?: nhid [0-9]+)? {matchvia} proto (static|196) metric 20" ) assert re.search(route, kernel), f"Failed to find \n'{route}'\n in \n'{kernel}'" -def do_config( +def do_config_inner( r1, count, add=True, do_ipv6=False, + do_sadr=False, via=None, vrf=None, use_cli=False, @@ -109,11 +122,18 @@ def do_config( # # Set the route details # - - if vrf: - super_prefix = "2002::/48" if do_ipv6 else "20.0.0.0/8" + src_prefs = [None, None] + if do_ipv6 and do_sadr: + # intentionally using overlapping prefix + super_prefs = ["2001::/48", "2002::/48"] + src_prefs = ["2001:db8:1111::/48", "2001:db8:2222::/48"] + elif do_ipv6: + super_prefs = ["2001::/48", "2002::/48"] else: - super_prefix = "2001::/48" if do_ipv6 else "10.0.0.0/8" + super_prefs = ["10.0.0.0/8", "20.0.0.0/8"] + + super_prefix = super_prefs[1 if vrf else 0] + src_prefix = src_prefs[1 if vrf else 0] matchvia = "" if via == "blackhole": @@ -144,11 +164,13 @@ def do_config( if vrf: f.write("vrf {}\n".format(vrf)) - for _, net in enumerate(get_ip_networks(super_prefix, count)): + for net, srcnet in zip( + get_ip_networks(super_prefix, count), get_src_networks(src_prefix, count) + ): if add: - f.write("ip route {} {}\n".format(net, via)) + f.write("ip route {}{} {}\n".format(net, srcnet, via)) else: - f.write("no ip route {} {}\n".format(net, via)) + f.write("no ip route {}{} {}\n".format(net, srcnet, via)) # # Load config file. @@ -165,7 +187,9 @@ def do_config( # # Verify the results are in the kernel # - check_kernel(r1, super_prefix, count, add, via == "blackhole", vrf, matchvia) + check_kernel( + r1, super_prefix, src_prefix, count, add, via == "blackhole", vrf, matchvia + ) optyped = "added" if add else "removed" logger.debug( @@ -175,6 +199,12 @@ def do_config( ) +def do_config(*args, **kwargs): + do_config_inner(*args, do_ipv6=False, do_sadr=False, **kwargs) + do_config_inner(*args, do_ipv6=True, do_sadr=False, **kwargs) + do_config_inner(*args, do_ipv6=True, do_sadr=True, **kwargs) + + def guts(tgen, vrf, use_cli): if tgen.routers_have_failure(): pytest.skip(tgen.errors) @@ -183,20 +213,20 @@ def guts(tgen, vrf, use_cli): count = 10 step(f"add {count} via gateway", reset=True) - do_config(r1, count, True, False, vrf=vrf, use_cli=use_cli) + do_config(r1, count, True, vrf=vrf, use_cli=use_cli) step(f"remove {count} via gateway") - do_config(r1, count, False, False, vrf=vrf, use_cli=use_cli) + do_config(r1, count, False, vrf=vrf, use_cli=use_cli) via = f"lo-{vrf}" if vrf else "lo" step("add via loopback") - do_config(r1, 1, True, False, via=via, vrf=vrf, use_cli=use_cli) + do_config(r1, 1, True, via=via, vrf=vrf, use_cli=use_cli) step("remove via loopback") - do_config(r1, 1, False, False, via=via, vrf=vrf, use_cli=use_cli) + do_config(r1, 1, False, via=via, vrf=vrf, use_cli=use_cli) step("add via blackhole") - do_config(r1, 1, True, False, via="blackhole", vrf=vrf, use_cli=use_cli) + do_config(r1, 1, True, via="blackhole", vrf=vrf, use_cli=use_cli) step("remove via blackhole") - do_config(r1, 1, False, False, via="blackhole", vrf=vrf, use_cli=use_cli) + do_config(r1, 1, False, via="blackhole", vrf=vrf, use_cli=use_cli) def test_static_cli(tgen): diff --git a/tests/topotests/static_srv6_sids/__init__.py b/tests/topotests/static_srv6_sids/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/static_srv6_sids/__init__.py diff --git a/tests/topotests/static_srv6_sids/expected_srv6_sids.json b/tests/topotests/static_srv6_sids/expected_srv6_sids.json new file mode 100644 index 0000000000..5799d97988 --- /dev/null +++ b/tests/topotests/static_srv6_sids/expected_srv6_sids.json @@ -0,0 +1,142 @@ +{ + "fcbb:bbbb:1::/48": [ + { + "prefix": "fcbb:bbbb:1::/48", + "prefixLen": 48, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "sr0", + "active": true, + "weight": 1, + "seg6local": { + "action": "End" + }, + "seg6localContext": { + + } + } + ] + } + ], + "fcbb:bbbb:1:fe10::/64": [ + { + "prefix": "fcbb:bbbb:1:fe10::/64", + "prefixLen": 64, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "Vrf10", + "active": true, + "weight": 1, + "seg6local": { + "action": "End.DT4" + }, + "seg6localContext": { + "table": 10 + } + } + ] + } + ], + "fcbb:bbbb:1:fe20::/64": [ + { + "prefix": "fcbb:bbbb:1:fe20::/64", + "prefixLen": 64, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "Vrf20", + "active": true, + "weight": 1, + "seg6local": { + "action": "End.DT6" + }, + "seg6localContext": { + "table": 20 + } + } + ] + } + ], + "fcbb:bbbb:1:fe30::/64": [ + { + "prefix": "fcbb:bbbb:1:fe30::/64", + "prefixLen": 64, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "Vrf30", + "active": true, + "weight": 1, + "seg6local": { + "action": "End.DT46" + }, + "seg6localContext": { + "table": 30 + } + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/static_srv6_sids/expected_srv6_sids_sid_delete_1.json b/tests/topotests/static_srv6_sids/expected_srv6_sids_sid_delete_1.json new file mode 100644 index 0000000000..e1a2a16afe --- /dev/null +++ b/tests/topotests/static_srv6_sids/expected_srv6_sids_sid_delete_1.json @@ -0,0 +1,107 @@ +{ + "fcbb:bbbb:1:fe10::/64": [ + { + "prefix": "fcbb:bbbb:1:fe10::/64", + "prefixLen": 64, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "Vrf10", + "active": true, + "weight": 1, + "seg6local": { + "action": "End.DT4" + }, + "seg6localContext": { + "table": 10 + } + } + ] + } + ], + "fcbb:bbbb:1:fe20::/64": [ + { + "prefix": "fcbb:bbbb:1:fe20::/64", + "prefixLen": 64, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "Vrf20", + "active": true, + "weight": 1, + "seg6local": { + "action": "End.DT6" + }, + "seg6localContext": { + "table": 20 + } + } + ] + } + ], + "fcbb:bbbb:1:fe30::/64": [ + { + "prefix": "fcbb:bbbb:1:fe30::/64", + "prefixLen": 64, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "Vrf30", + "active": true, + "weight": 1, + "seg6local": { + "action": "End.DT46" + }, + "seg6localContext": { + "table": 30 + } + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/static_srv6_sids/expected_srv6_sids_sid_delete_2.json b/tests/topotests/static_srv6_sids/expected_srv6_sids_sid_delete_2.json new file mode 100644 index 0000000000..b5801d354b --- /dev/null +++ b/tests/topotests/static_srv6_sids/expected_srv6_sids_sid_delete_2.json @@ -0,0 +1,72 @@ +{ + "fcbb:bbbb:1:fe10::/64": [ + { + "prefix": "fcbb:bbbb:1:fe10::/64", + "prefixLen": 64, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "Vrf10", + "active": true, + "weight": 1, + "seg6local": { + "action": "End.DT4" + }, + "seg6localContext": { + "table": 10 + } + } + ] + } + ], + "fcbb:bbbb:1:fe30::/64": [ + { + "prefix": "fcbb:bbbb:1:fe30::/64", + "prefixLen": 64, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "Vrf30", + "active": true, + "weight": 1, + "seg6local": { + "action": "End.DT46" + }, + "seg6localContext": { + "table": 30 + } + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/static_srv6_sids/r1/frr.conf b/tests/topotests/static_srv6_sids/r1/frr.conf new file mode 100644 index 0000000000..b4904d9ac2 --- /dev/null +++ b/tests/topotests/static_srv6_sids/r1/frr.conf @@ -0,0 +1,17 @@ +hostname r1 +! +segment-routing + srv6 + locators + locator MAIN + prefix fcbb:bbbb:1::/48 block-len 32 node-len 16 func-bits 16 + ! + ! + static-sids + sid fcbb:bbbb:1::/48 locator MAIN behavior uN + sid fcbb:bbbb:1:fe10::/64 locator MAIN behavior uDT4 vrf Vrf10 + sid fcbb:bbbb:1:fe20::/64 locator MAIN behavior uDT6 vrf Vrf20 + sid fcbb:bbbb:1:fe30::/64 locator MAIN behavior uDT46 vrf Vrf30 + ! + ! +!
\ No newline at end of file diff --git a/tests/topotests/static_srv6_sids/r1/setup.sh b/tests/topotests/static_srv6_sids/r1/setup.sh new file mode 100644 index 0000000000..040be73914 --- /dev/null +++ b/tests/topotests/static_srv6_sids/r1/setup.sh @@ -0,0 +1,13 @@ +ip link add sr0 type dummy +ip link set sr0 up + +ip link add Vrf10 type vrf table 10 +ip link set Vrf10 up + +ip link add Vrf20 type vrf table 20 +ip link set Vrf20 up + +ip link add Vrf30 type vrf table 30 +ip link set Vrf30 up + +sysctl -w net.vrf.strict_mode=1 diff --git a/tests/topotests/static_srv6_sids/test_static_srv6_sids.py b/tests/topotests/static_srv6_sids/test_static_srv6_sids.py new file mode 100755 index 0000000000..cdcc6fd29e --- /dev/null +++ b/tests/topotests/static_srv6_sids/test_static_srv6_sids.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_static_srv6_sids.py +# +# Copyright (c) 2025 by +# Alibaba Inc, Yuqing Zhao <galadriel.zyq@alibaba-inc.com> +# Lingyu Zhang <hanyu.zly@alibaba-inc.com> +# + +""" +test_static_srv6_sids.py: +Test for SRv6 static route on zebra +""" + +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.topolog import logger + +pytestmark = [pytest.mark.staticd] + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def setup_module(mod): + tgen = Topogen({None: "r1"}, mod.__name__) + tgen.start_topology() + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) + router.load_frr_config("frr.conf") + tgen.start_router() + + +def teardown_module(): + tgen = get_topogen() + tgen.stop_topology() + + +def test_srv6_static_sids(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + def _check_srv6_static_sids(router, expected_route_file): + logger.info("checking zebra srv6 static sids") + output = json.loads(router.vtysh_cmd("show ipv6 route static json")) + expected = open_json_file("{}/{}".format(CWD, expected_route_file)) + return topotest.json_cmp(output, expected) + + def check_srv6_static_sids(router, expected_file): + func = functools.partial(_check_srv6_static_sids, router, expected_file) + _, result = topotest.run_and_expect(func, None, count=15, wait=1) + assert result is None, "Failed" + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Test for srv6 sids configuration") + check_srv6_static_sids(router, "expected_srv6_sids.json") + + +def test_srv6_static_sids_sid_delete(): + """ + Remove the static SID and verify it gets removed + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + def _check_srv6_static_sids(router, expected_route_file): + logger.info("checking zebra srv6 static sids") + output = json.loads(router.vtysh_cmd("show ipv6 route static json")) + expected = open_json_file("{}/{}".format(CWD, expected_route_file)) + return topotest.json_cmp(output, expected) + + def check_srv6_static_sids(router, expected_file): + func = functools.partial(_check_srv6_static_sids, router, expected_file) + _, result = topotest.run_and_expect(func, None, count=15, wait=1) + assert result is None, "Failed" + + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + static-sids + no sid fcbb:bbbb:1::/48 + """ + ) + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Test for srv6 sids configuration") + check_srv6_static_sids(router, "expected_srv6_sids_sid_delete_1.json") + + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + static-sids + no sid fcbb:bbbb:1:fe20::/64 locator MAIN behavior uDT6 vrf Vrf20 + """ + ) + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Test for srv6 sids configuration") + check_srv6_static_sids(router, "expected_srv6_sids_sid_delete_2.json") + + +def test_srv6_static_sids_sid_readd(): + """ + Re-add the static SID and verify the routing table + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + def _check_srv6_static_sids(router, expected_route_file): + logger.info("checking zebra srv6 static sids") + output = json.loads(router.vtysh_cmd("show ipv6 route static json")) + expected = open_json_file("{}/{}".format(CWD, expected_route_file)) + return topotest.json_cmp(output, expected) + + def check_srv6_static_sids(router, expected_file): + func = functools.partial(_check_srv6_static_sids, router, expected_file) + _, result = topotest.run_and_expect(func, None, count=15, wait=1) + assert result is None, "Failed" + + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + static-sids + sid fcbb:bbbb:1::/48 locator MAIN behavior uN + sid fcbb:bbbb:1:fe20::/64 locator MAIN behavior uDT6 vrf Vrf20 + """ + ) + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Test for srv6 sids configuration") + check_srv6_static_sids(router, "expected_srv6_sids.json") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/v6_nexthop_group_recursive_resolution/r1/frr.conf b/tests/topotests/v6_nexthop_group_recursive_resolution/r1/frr.conf new file mode 100644 index 0000000000..f4da11af06 --- /dev/null +++ b/tests/topotests/v6_nexthop_group_recursive_resolution/r1/frr.conf @@ -0,0 +1,4 @@ +int r1-eth0 + ipv6 address fc00::1/64 + +ipv6 route 1::1/128 fc00::2 diff --git a/tests/topotests/v6_nexthop_group_recursive_resolution/test_v6_nexthop_group_recursive_resolution.py b/tests/topotests/v6_nexthop_group_recursive_resolution/test_v6_nexthop_group_recursive_resolution.py new file mode 100644 index 0000000000..587a951c85 --- /dev/null +++ b/tests/topotests/v6_nexthop_group_recursive_resolution/test_v6_nexthop_group_recursive_resolution.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2024 by Nvidia Corporation +# Donald Sharp +# + +""" +Check that the v6 nexthop recursive resolution works when it changes +""" + +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.staticd] + + +def build_topo(tgen): + + tgen.add_router("r1") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.items(): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)), + [(TopoRouter.RD_MGMTD, None), + (TopoRouter.RD_ZEBRA, None), + (TopoRouter.RD_STATIC, None), + (TopoRouter.RD_SHARP, None)]) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_recursive_v6_nexthop_generation(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Testing v6 nexthop resolution") + + #assert False + router = tgen.gears["r1"] + + def _v6_converge_1_1_initial(): + output = json.loads( + router.vtysh_cmd("show ipv6 route 1::1 json")) + + expected = { + "1::1/128":[ + { + "prefix":"1::1/128", + "prefixLen":128, + "protocol":"static", + "vrfName":"default", + "selected":True, + "destSelected":True, + "distance":1, + "metric":0, + "installed":True, + "table":254, + "nexthops":[ + { + "fib":True, + "ip":"fc00::2", + "afi":"ipv6", + "interfaceName":"r1-eth0", + "active":True, + "weight":1 + } + ] + } + ] + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_v6_converge_1_1_initial) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to install v6 1::1 route" + + router.vtysh_cmd("sharp install routes 2::2 nexthop 1::1 1") + router.vtysh_cmd("conf\nipv6 route 1::1/128 fc00::3\nno ipv6 route 1::1/128 fc00::2") + + def _v6_converge_1_1_post(): + output = json.loads( + router.vtysh_cmd("show ipv6 route 1::1 json")) + + expected = { + "1::1/128":[ + { + "prefix":"1::1/128", + "prefixLen":128, + "protocol":"static", + "vrfName":"default", + "selected":True, + "destSelected":True, + "distance":1, + "metric":0, + "installed":True, + "table":254, + "nexthops":[ + { + "fib":True, + "ip":"fc00::3", + "afi":"ipv6", + "interfaceName":"r1-eth0", + "active":True, + "weight":1 + } + ] + } + ] + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_v6_converge_1_1_post) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to change v6 1::1 route" + + router.vtysh_cmd("sharp install routes 2::2 nexthop 1::1 1") + + def _v6_change_2_2_post(): + output = json.loads( + router.vtysh_cmd("show ipv6 route 2::2 json")) + + expected = { + "2::2/128":[ + { + "prefix":"2::2/128", + "prefixLen":128, + "protocol":"sharp", + "vrfName":"default", + "selected":True, + "destSelected":True, + "distance":150, + "metric":0, + "installed":True, + "table":254, + "nexthops":[ + { + "fib":True, + "ip":"fc00::3", + "afi":"ipv6", + "interfaceName":"r1-eth0", + "active":True, + "weight":1 + } + ] + } + ] + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_v6_change_2_2_post) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see sharpd route correctly" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf index be831a1d34..252539e29b 100644 --- a/tools/etc/frr/support_bundle_commands.conf +++ b/tools/etc/frr/support_bundle_commands.conf @@ -79,6 +79,10 @@ show mpls table show mpls fec show mpls ldp show mpls pseudowires +show zebra dplane detailed +show zebra dplane provider +show fpm counters +show fpm status show memory show interface vrf all show vrf diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 3ea63ce2a3..f124cae713 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -237,6 +237,14 @@ def get_normalized_interface_vrf(line): return line +def get_normalized_ebgp_multihop_line(line): + obj = re.search(r"(.*)ebgp-multihop\s+255", line) + if obj: + line = obj.group(1) + "ebgp-multihop" + + return line + + # This dictionary contains a tree of all commands that we know start a # new multi-line context. All other commands are treated either as # commands inside a multi-line context or as single-line contexts. This @@ -382,6 +390,9 @@ class Config(object): if ":" in line: line = get_normalized_mac_ip_line(line) + if "ebgp-multihop" in line: + line = get_normalized_ebgp_multihop_line(line) + # vrf static routes can be added in two ways. The old way is: # # "ip route x.x.x.x/x y.y.y.y vrf <vrfname>" @@ -1757,12 +1768,13 @@ def compare_context_objects(newconf, running): delete_bgpd = True lines_to_del.append((running_ctx_keys, None)) - # We cannot do 'no interface' or 'no vrf' in FRR, and so deal with it - elif ( - running_ctx_keys[0].startswith("interface") - or running_ctx_keys[0].startswith("vrf") - or running_ctx_keys[0].startswith("router pim") - ): + elif running_ctx_keys[0].startswith("interface"): + lines_to_del.append((running_ctx_keys, None)) + + # We cannot do 'no vrf' in FRR, and so deal with it + elif running_ctx_keys[0].startswith("vrf") or running_ctx_keys[ + 0 + ].startswith("router pim"): for line in running_ctx.lines: lines_to_del.append((running_ctx_keys, line)) diff --git a/tools/gen_northbound_callbacks.c b/tools/gen_northbound_callbacks.c index 019404d7cc..87ba43eaa2 100644 --- a/tools/gen_northbound_callbacks.c +++ b/tools/gen_northbound_callbacks.c @@ -238,12 +238,10 @@ static int generate_prototypes(const struct lysc_node *snode, void *arg) generate_callback_name(snode, cb->operation, cb_name, sizeof(cb_name)); - if (cb->operation == NB_CB_GET_ELEM) { - if (f_new_cbs) - generate_prototype(&nb_oper_get, cb_name); - else - generate_prototype(cb, cb_name); - } + if (cb->operation == NB_CB_GET_ELEM && f_new_cbs) + generate_prototype(&nb_oper_get, cb_name); + else + generate_prototype(cb, cb_name); if (cb->need_config_write && need_config_write) { generate_config_write_cb_name(snode, cb_name, @@ -344,12 +342,10 @@ static int generate_callbacks(const struct lysc_node *snode, void *arg) generate_callback_name(snode, cb->operation, cb_name, sizeof(cb_name)); - if (cb->operation == NB_CB_GET_ELEM) { - if (f_new_cbs) - generate_callback(&nb_oper_get, cb_name); - else - generate_callback(cb, cb_name); - } + if (cb->operation == NB_CB_GET_ELEM && f_new_cbs) + generate_callback(&nb_oper_get, cb_name); + else + generate_callback(cb, cb_name); if (cb->need_config_write && need_config_write) { generate_config_write_cb_name(snode, cb_name, diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index c460dea70c..0559e89f92 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1312,6 +1312,13 @@ static struct cmd_node srv6_node = { .prompt = "%s(config-srv6)# ", }; +static struct cmd_node srv6_sids_node = { + .name = "srv6-sids", + .node = SRV6_SIDS_NODE, + .parent_node = SRV6_NODE, + .prompt = "%s(config-srv6-sids)# ", +}; + static struct cmd_node srv6_locs_node = { .name = "srv6-locators", .node = SRV6_LOCS_NODE, @@ -1685,7 +1692,7 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_end_all, vtysh_end_all_cmd, "end", return vtysh_end(); } -DEFUNSH(VTYSH_ZEBRA, srv6, srv6_cmd, +DEFUNSH(VTYSH_ZEBRA | VTYSH_MGMTD, srv6, srv6_cmd, "srv6", "Segment-Routing SRv6 configuration\n") { @@ -1693,6 +1700,14 @@ DEFUNSH(VTYSH_ZEBRA, srv6, srv6_cmd, return CMD_SUCCESS; } +DEFUNSH(VTYSH_MGMTD, srv6_sids, srv6_sids_cmd, + "static-sids", + "Segment-Routing SRv6 SIDs configuration\n") +{ + vty->node = SRV6_SIDS_NODE; + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_ZEBRA, srv6_locators, srv6_locators_cmd, "locators", "Segment-Routing SRv6 locators configuration\n") @@ -2216,7 +2231,7 @@ DEFUNSH(VTYSH_FABRICD, router_openfabric, router_openfabric_cmd, "router openfab } #endif /* HAVE_FABRICD */ -DEFUNSH(VTYSH_SR, segment_routing, segment_routing_cmd, +DEFUNSH(VTYSH_SR | VTYSH_MGMTD, segment_routing, segment_routing_cmd, "segment-routing", "Configure segment routing\n") { @@ -2385,6 +2400,79 @@ DEFUNSH(VTYSH_BFDD, bfd_peer_enter, bfd_peer_enter_cmd, return CMD_SUCCESS; } +DEFUNSH(VTYSH_BFDD, sbfd_echo_peer_enter, sbfd_echo_peer_enter_cmd, + "peer <A.B.C.D|X:X::X:X> bfd-mode sbfd-echo bfd-name BFDNAME [multihop$multihop] local-address <A.B.C.D|X:X::X:X> [vrf NAME] srv6-source-ipv6 X:X::X:X srv6-encap-data X:X::X:X...", + "Configure peer\n" + "IPv4 peer address\n" + "IPv6 peer address\n" + "Specify bfd session mode\n" + "Enable sbfd-echo mode\n" + "Specify bfd session name\n" + "bfd session name\n" + "Configure multihop\n" + "Configure local\n" + "IPv4 local address\n" + "IPv6 local address\n" + "Configure VRF\n" + "Configure VRF name\n" + "Configure source ipv6 address for srv6 encap\n" + "IPv6 local address\n" + "Configure sidlist data for srv6 encap\n" + "X:X::X:X IPv6 sid address\n") +{ + vty->node = BFD_PEER_NODE; + return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_BFDD, sbfd_init_peer_enter, sbfd_init_peer_enter_cmd, + "peer <A.B.C.D|X:X::X:X> bfd-mode sbfd-init bfd-name BFDNAME [multihop$multihop] local-address <A.B.C.D|X:X::X:X> [vrf NAME] remote-discr (1-4294967295) srv6-source-ipv6 X:X::X:X srv6-encap-data X:X::X:X...", + "Configure peer\n" + "IPv4 peer address\n" + "IPv6 peer address\n" + "Specify bfd session mode\n" + "Enable sbfd-init mode\n" + "Specify bfd session name\n" + "bfd session name\n" + "Configure multihop\n" + "Configure local\n" + "IPv4 local address\n" + "IPv6 local address\n" + "Configure VRF\n" + "Configure VRF name\n" + "Configure bfd session remote discriminator\n" + "Configure remote discriminator\n" + "Configure source ipv6 address for srv6 encap\n" + "IPv6 local address\n" + "Configure sidlist data for srv6 encap\n" + "X:X::X:X IPv6 sid address\n" + ) +{ + vty->node = BFD_PEER_NODE; + return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_BFDD, sbfd_init_peer_raw_enter, sbfd_init_peer_raw_enter_cmd, + "peer <A.B.C.D|X:X::X:X> bfd-mode sbfd-init bfd-name BFDNAME [multihop$multihop] local-address <A.B.C.D|X:X::X:X> [vrf NAME] remote-discr (1-4294967295)", + "Configure peer\n" + "IPv4 peer address\n" + "IPv6 peer address\n" + "Specify bfd session mode\n" + "Enable sbfd-init mode\n" + "Specify bfd session name\n" + "bfd session name\n" + "Configure multihop\n" + "Configure local\n" + "IPv4 local address\n" + "IPv6 local address\n" + "Configure VRF\n" + "Configure VRF name\n" + "Configure bfd session remote discriminator\n" + "Configure remote discriminator\n") +{ + vty->node = BFD_PEER_NODE; + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_BFDD, bfd_profile_enter, bfd_profile_enter_cmd, "profile BFDPROF", BFD_PROFILE_STR @@ -2535,7 +2623,7 @@ DEFUNSH(VTYSH_VRF, exit_vrf_config, exit_vrf_config_cmd, "exit-vrf", return CMD_SUCCESS; } -DEFUNSH(VTYSH_ZEBRA, exit_srv6_config, exit_srv6_config_cmd, "exit", +DEFUNSH(VTYSH_ZEBRA | VTYSH_MGMTD, exit_srv6_config, exit_srv6_config_cmd, "exit", "Exit from SRv6 configuration mode\n") { if (vty->node == SRV6_NODE) @@ -2551,6 +2639,14 @@ DEFUNSH(VTYSH_ZEBRA, exit_srv6_locs_config, exit_srv6_locs_config_cmd, "exit", return CMD_SUCCESS; } +DEFUNSH(VTYSH_MGMTD, exit_srv6_sids_config, exit_srv6_sids_config_cmd, "exit", + "Exit from SRv6-SIDs configuration mode\n") +{ + if (vty->node == SRV6_SIDS_NODE) + vty->node = SRV6_NODE; + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_ZEBRA, exit_srv6_loc_config, exit_srv6_loc_config_cmd, "exit", "Exit from SRv6-locators configuration mode\n") { @@ -2806,13 +2902,13 @@ DEFUNSH(VTYSH_KEYS, vtysh_quit_keys, vtysh_quit_keys_cmd, "quit", return vtysh_exit_keys(self, vty, argc, argv); } -DEFUNSH(VTYSH_SR, vtysh_exit_sr, vtysh_exit_sr_cmd, "exit", +DEFUNSH(VTYSH_SR | VTYSH_MGMTD, vtysh_exit_sr, vtysh_exit_sr_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); } -DEFUNSH(VTYSH_SR, vtysh_quit_sr, vtysh_quit_sr_cmd, "quit", +DEFUNSH(VTYSH_SR | VTYSH_MGMTD, vtysh_quit_sr, vtysh_quit_sr_cmd, "quit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); @@ -4999,6 +5095,7 @@ void vtysh_init_vty(void) install_node(&rmap_node); install_node(&vty_node); install_node(&srv6_node); + install_node(&srv6_sids_node); install_node(&srv6_locs_node); install_node(&srv6_loc_node); install_node(&srv6_encap_node); @@ -5272,6 +5369,9 @@ void vtysh_init_vty(void) install_element(BFD_NODE, &vtysh_end_all_cmd); install_element(BFD_NODE, &bfd_peer_enter_cmd); + install_element(BFD_NODE, &sbfd_init_peer_enter_cmd); + install_element(BFD_NODE, &sbfd_init_peer_raw_enter_cmd); + install_element(BFD_NODE, &sbfd_echo_peer_enter_cmd); install_element(BFD_PEER_NODE, &vtysh_exit_bfdd_cmd); install_element(BFD_PEER_NODE, &vtysh_quit_bfdd_cmd); install_element(BFD_PEER_NODE, &vtysh_end_all_cmd); @@ -5442,6 +5542,10 @@ void vtysh_init_vty(void) install_element(SRV6_NODE, &exit_srv6_config_cmd); install_element(SRV6_NODE, &vtysh_end_all_cmd); install_element(SRV6_NODE, &srv6_encap_cmd); + install_element(SRV6_NODE, &srv6_sids_cmd); + + install_element(SRV6_SIDS_NODE, &exit_srv6_sids_config_cmd); + install_element(SRV6_SIDS_NODE, &vtysh_end_all_cmd); install_element(SRV6_LOCS_NODE, &srv6_locator_cmd); install_element(SRV6_LOCS_NODE, &exit_srv6_locs_config_cmd); diff --git a/yang/frr-bfdd.yang b/yang/frr-bfdd.yang index c5c824f792..ceed6fa5a1 100644 --- a/yang/frr-bfdd.yang +++ b/yang/frr-bfdd.yang @@ -503,6 +503,136 @@ module frr-bfdd { config false; } } + + list sbfd-echo { + key "source-addr bfd-name vrf"; + description "support a special echo SBFD session in RFC7880, and enhanced with SRv6 encapsulation"; + + leaf source-addr { + type inet:ip-address; + description "Local IP address"; + } + + leaf dest-addr { + type inet:ip-address; + description "IP address of the peer"; + } + + leaf bfd-name { + type string; + default ""; + description "Bfd session name."; + } + + leaf vrf { + type frr-vrf:vrf-ref; + description "Virtual Routing Domain name"; + } + + leaf profile { + type profile-ref; + description "Override defaults with profile."; + } + + leaf-list srv6-encap-data { + type inet:ipv6-address; + max-elements 6; + + description "segment list ipv6 addresses for srv6 encapsulation"; + } + + leaf srv6-source-ipv6 { + type inet:ipv6-address; + description "source ipv6 address for srv6 encapsulation"; + } + + leaf bfd-mode { + type uint32; + description "Bfd session mode."; + } + + leaf multi-hop { + type boolean; + description "Use multi hop session instead of single hop."; + } + + uses session-common; + uses session-multi-hop; + uses session-echo; + + container stats { + uses session-states; + config false; + } + } + + list sbfd-init { + key "source-addr dest-addr bfd-name vrf"; + description "support SBFD session in RFC7880, and optional with SRv6 encapsulation"; + + leaf source-addr { + type inet:ip-address; + description "Local IP address"; + } + + leaf dest-addr { + type inet:ip-address; + description "IP address of the peer"; + } + + leaf bfd-name { + type string; + default ""; + description "Bfd session name."; + } + + leaf vrf { + type frr-vrf:vrf-ref; + description "Virtual Routing Domain name"; + } + + leaf profile { + type profile-ref; + description "Override defaults with profile."; + } + + leaf-list srv6-encap-data { + type inet:ipv6-address; + max-elements 6; + + description "segment list ipv6 addresses for srv6 encapsulation"; + } + + leaf srv6-source-ipv6 { + type inet:ip-address; + description "source ipv6 address for srv6 encapsulation"; + } + + leaf remote-discr { + type uint32; + default 0; + description + "Remote session identifier"; + } + + leaf bfd-mode { + type uint32; + description "Bfd session mode."; + } + + leaf multi-hop { + type boolean; + description "Use multi hop session instead of single hop."; + } + + uses session-common; + uses session-multi-hop; + + container stats { + uses session-states; + config false; + } + } } } } diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index 5f701d514c..efb0b2fa08 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -148,6 +148,12 @@ module frr-bgp-route-map { "Match BGP community list"; } + identity match-community-limit { + base frr-route-map:rmap-match-type; + description + "Match BGP community limit count"; + } + identity match-large-community { base frr-route-map:rmap-match-type; description @@ -802,6 +808,17 @@ identity set-extcommunity-color { } } + case community-limit { + when "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:match-community-limit')"; + description + "Match BGP updates when the list of communities count is less than the configured limit."; + leaf community-limit { + type uint16 { + range "1..1024"; + } + } + } + case comm-list-name { when "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:match-community') or " + "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:match-large-community') or " diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index a3e073f626..228faa4f10 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -403,7 +403,7 @@ module frr-isisd { "Limit backup computation up to the prefix priority."; } list tiebreaker { - key "index"; + key "index type"; unique "type"; description "Configure tiebreaker for multiple backups."; diff --git a/yang/frr-staticd.yang b/yang/frr-staticd.yang index 1e6c54c006..8d0e58c0a5 100644 --- a/yang/frr-staticd.yang +++ b/yang/frr-staticd.yang @@ -20,6 +20,10 @@ module frr-staticd { prefix frr-bfdd; } + import frr-vrf { + prefix frr-vrf; + } + organization "FRRouting"; contact @@ -92,6 +96,64 @@ module frr-staticd { } } + typedef srv6-behavior-codepoint { + description + "SRv6 Endpoint Behaviors Codepoints as per + https://www.iana.org/assignments/segment-routing/segment-routing.xhtml."; + type enumeration { + enum End { + value 1; + description + "This enum indicates End endpoint behavior."; + } + enum End.X { + value 5; + description + "This enum indicates End.X endpoint behavior."; + } + enum End.DT6 { + value 18; + description + "This enum indicates End.DT6 endpoint behavior."; + } + enum End.DT4 { + value 19; + description + "This enum indicates End.DT4 endpoint behavior."; + } + enum End.DT46 { + value 20; + description + "This enum indicates End.DT46 endpoint behavior."; + } + enum uN { + value 43; + description + "This enum indicates End with NEXT-CSID endpoint behavior."; + } + enum uA { + value 52; + description + "This enum indicates End.X with NEXT-CSID endpoint behavior."; + } + enum uDT6 { + value 62; + description + "This enum indicates End.DT6 with NEXT-CSID endpoint behavior."; + } + enum uDT4 { + value 63; + description + "This enum indicates End.DT4 with NEXT-CSID endpoint behavior."; + } + enum uDT46 { + value 64; + description + "This enum indicates End.DT46 with NEXT-CSID endpoint behavior."; + } + } + } + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol" { container staticd { when "../frr-rt:type = 'frr-staticd:staticd'" { @@ -103,7 +165,7 @@ module frr-staticd { "Support for a 'staticd' pseudo-protocol instance consists of a list of routes."; list route-list { - key "prefix afi-safi"; + key "prefix src-prefix afi-safi"; description "List of staticd IP routes."; leaf prefix { @@ -111,6 +173,11 @@ module frr-staticd { description "IP prefix."; } + leaf src-prefix { + type inet:ipv6-prefix; + description + "IPv6 source prefix for dst-src routes"; + } leaf afi-safi { type identityref { base frr-rt:afi-safi-type; @@ -118,6 +185,12 @@ module frr-staticd { description "AFI-SAFI type."; } + /* note dst-src routes are semantically invalid in MRIB */ + must "afi-safi = 'frr-rt:ipv6-unicast' + or afi-safi = 'frr-rt:ipv6-labeled-unicast' + or afi-safi = 'frr-rt:l3vpn-ipv6-unicast' + or src-prefix = '::/0' + "; uses staticd-prefix-attributes { augment "path-list/frr-nexthops/nexthop" { @@ -132,18 +205,45 @@ module frr-staticd { } } } + } - list src-list { - key "src-prefix"; - leaf src-prefix { - type inet:ipv6-prefix; + container segment-routing { + description + "Segment Routing configuration."; + container srv6 { + description + "Segment Routing over IPv6 (SRv6) configuration."; + container static-sids { description - "IPv6 source prefix"; + "This container lists the SRv6 Static SIDs instantiated on the local node."; + list sid { + description + "List of SRv6 Static SIDs."; + key "sid"; + leaf sid { + type inet:ipv6-prefix; + description + "Value of the SRv6 SID."; + } + leaf behavior { + type srv6-behavior-codepoint; + description + "Behavior bound to the SRv6 SID."; + } + leaf locator-name { + type string; + description + "SRv6 locator name."; + } + leaf vrf-name { + type frr-vrf:vrf-ref; + description + "The VRF name."; + } + } } - - uses staticd-prefix-attributes; } } } } -} +}
\ No newline at end of file diff --git a/yang/frr-test-module.yang b/yang/frr-test-module.yang index 90086d05a2..773a959553 100644 --- a/yang/frr-test-module.yang +++ b/yang/frr-test-module.yang @@ -7,13 +7,14 @@ module frr-test-module { import ietf-inet-types { prefix inet; } - import ietf-yang-types { - prefix yang; - } import frr-interface { prefix frr-interface; } + organization "placeholder for lint"; + + contact "placeholder for lint"; + description "FRRouting internal testing module. @@ -45,41 +46,56 @@ module frr-test-module { revision 2018-11-26 { description "Initial revision."; + reference "placeholder for lint"; } container frr-test-module { config false; + description "a container for test module data"; container vrfs { + description "a container of vrfs"; list vrf { key "name"; + description "a keyed vrf list object"; leaf name { type string; + description "name of vrf"; } container interfaces { + description "container of leaf-list interfaces"; leaf-list interface { type frr-interface:interface-ref; + description "leaf list interface object"; } leaf-list interface-new { type frr-interface:interface-ref; + description "second leaf list interface object"; } } container routes { + description "container of key-less route objects"; list route { + description "a key-less route object"; leaf prefix { type inet:ipv4-prefix; + description "prefix of the route object"; } leaf next-hop { type inet:ipv4-address; + description "nexthop of the route object"; } leaf interface { type frr-interface:interface-ref; + description "interface of the route object"; } leaf metric { type uint8; + description "metric of the route object"; } leaf active { type empty; + description "active status of the route object"; } } } @@ -87,16 +103,19 @@ module frr-test-module { input { leaf data { type string; + description "data input to ping action."; } } output { leaf vrf { type string; + description "vrf returned from ping action."; } // can't use the same name in input and output // because of a bug in libyang < 2.1.148 leaf data-out { type string; + description "data return from ping action."; } } } diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index b8dbabb60d..9f26852d1f 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -587,7 +587,6 @@ static void fpm_read(struct event *t) struct zebra_dplane_ctx *ctx; size_t available_bytes; size_t hdr_available_bytes; - int ival; /* Let's ignore the input at the moment. */ rv = stream_read_try(fnc->ibuf, fnc->socket, @@ -724,12 +723,18 @@ static void fpm_read(struct event *t) NULL); if (netlink_route_notify_read_ctx(hdr, 0, ctx) >= 0) { - /* In the FPM encoding, the vrfid is present */ - ival = dplane_ctx_get_table(ctx); - dplane_ctx_set_vrf(ctx, ival); - dplane_ctx_set_table(ctx, - ZEBRA_ROUTE_TABLE_UNKNOWN); - + /* + * Receiving back a netlink message from + * the fpm. Currently the netlink messages + * do not have a way to specify the vrf + * so it must be unknown. I'm looking + * at you sonic. If you are reading this + * and wondering why it's not working + * you must extend your patch to translate + * the tableid to the vrfid and set the + * tableid to 0 in order for this to work. + */ + dplane_ctx_set_vrf(ctx, VRF_UNKNOWN); dplane_provider_enqueue_to_zebra(ctx); } else { /* @@ -946,8 +951,6 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) nl_buf_len = 0; - frr_mutex_lock_autounlock(&fnc->obuf_mutex); - /* * If route replace is enabled then directly encode the install which * is going to use `NLM_F_REPLACE` (instead of delete/add operations). @@ -1100,6 +1103,8 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) /* We must know if someday a message goes beyond 65KiB. */ assert((nl_buf_len + FPM_HEADER_SIZE) <= UINT16_MAX); + frr_mutex_lock_autounlock(&fnc->obuf_mutex); + /* Check if we have enough buffer space. */ if (STREAM_WRITEABLE(fnc->obuf) < (nl_buf_len + FPM_HEADER_SIZE)) { atomic_fetch_add_explicit(&fnc->counters.buffer_full, 1, diff --git a/zebra/fpm_listener.c b/zebra/fpm_listener.c index 7d84c706d4..ed0842a3b1 100644 --- a/zebra/fpm_listener.c +++ b/zebra/fpm_listener.c @@ -756,8 +756,10 @@ static void fpm_serve(void) while (1) { hdr = read_fpm_msg(buf, sizeof(buf)); - if (!hdr) + if (!hdr) { + close(glob->sock); return; + } process_fpm_msg(hdr); } @@ -769,6 +771,8 @@ int main(int argc, char **argv) int r; bool fork_daemon = false; + setbuf(stdout, NULL); + memset(glob, 0, sizeof(*glob)); while ((r = getopt(argc, argv, "rdv")) != -1) { diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 66dc5b4b5f..9bf7e2cbb5 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -82,8 +82,8 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id) RNODE_FOREACH_RE (rn, newre) { if (CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED)) - zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, - client, rn, newre, false); + zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn, + newre, NULL); } route_unlock_node(rn); @@ -91,6 +91,24 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id) } /* Redistribute routes. */ +static void redistribute_table_direct(struct zserv *client, int type, const struct route_node *rn, + const struct route_entry *re) +{ + struct redist_table_direct *table; + struct redist_proto *red; + struct listnode *node; + afi_t afi = family2afi(rn->p.family); + + red = &client->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT]; + + for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) { + if (table->table_id != (int)re->table) + continue; + + zsend_redistribute_route(type, client, rn, re, &table->vrf_id); + } +} + static void zebra_redistribute(struct zserv *client, int type, unsigned short instance, struct zebra_vrf *zvrf, int afi) @@ -102,13 +120,9 @@ static void zebra_redistribute(struct zserv *client, int type, vrf_id_t vrf_id = zvrf_id(zvrf); if (type == ZEBRA_ROUTE_TABLE_DIRECT) { - if (vrf_id == VRF_DEFAULT) { - table = zebra_router_find_table(zvrf, instance, afi, - SAFI_UNICAST); - type = ZEBRA_ROUTE_ALL; - is_table_direct = true; - } else - return; + table = zebra_router_find_table(zvrf, instance, afi, SAFI_UNICAST); + type = ZEBRA_ROUTE_ALL; + is_table_direct = true; } else table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id); @@ -140,15 +154,20 @@ static void zebra_redistribute(struct zserv *client, int type, if (!zebra_check_addr(&rn->p)) continue; - zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, - client, rn, newre, is_table_direct); + if (is_table_direct) + redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_ADD, rn, + newre); + else + zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn, + newre, NULL); } } /* - * Function to return a valid table id value if table-direct is used - * return 0 otherwise - * This function can be called only if zebra_redistribute_check returns TRUE + * Checks if the route entry can be used as table-direct or not. + * `table-direct` routes always belong to `VRF_DEFAULT` and has an table + * ID different than the VRF it belongs (example main VRF table is 254, + * so in order to be `table-direct` the route's table ID must be != 254). */ static bool zebra_redistribute_is_table_direct(const struct route_entry *re) { @@ -177,15 +196,14 @@ static bool zebra_redistribute_check(const struct route_node *rn, afi = family2afi(rn->p.family); zvrf = zebra_vrf_lookup_by_id(re->vrf_id); - if (re->vrf_id == VRF_DEFAULT && zvrf->table_id != re->table) { + if (zvrf->table_id != re->table) { + /* + * Routes with table ID different from VRFs can be used as + * `table-direct` if enabled. + */ if (re->table && - redist_check_instance(&client->mi_redist - [afi][ZEBRA_ROUTE_TABLE_DIRECT], - re->table)) { - /* table-direct redistribution only for route entries which - * are on the default vrf, and that have table id different - * from the default table. - */ + redist_table_direct_has_id(&client->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT], + re->table)) { return true; } return false; @@ -227,7 +245,6 @@ void redistribute_update(const struct route_node *rn, { struct listnode *node, *nnode; struct zserv *client; - bool is_table_direct; if (IS_ZEBRA_DEBUG_RIB) zlog_debug( @@ -242,7 +259,6 @@ void redistribute_update(const struct route_node *rn, return; } - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { if (zebra_redistribute_check(rn, re, client)) { if (IS_ZEBRA_DEBUG_RIB) { @@ -253,15 +269,19 @@ void redistribute_update(const struct route_node *rn, re->vrf_id, re->table, re->type, re->distance, re->metric); } - is_table_direct = zebra_redistribute_is_table_direct(re); - zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, - client, rn, re, - is_table_direct); + if (zebra_redistribute_is_table_direct(re)) + redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_ADD, rn, + re); + else + zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn, + re, NULL); } else if (zebra_redistribute_check(rn, prev_re, client)) { - is_table_direct = zebra_redistribute_is_table_direct(prev_re); - zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, - client, rn, prev_re, - is_table_direct); + if (zebra_redistribute_is_table_direct(prev_re)) + redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn, + prev_re); + else + zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, client, rn, + prev_re, NULL); } } } @@ -281,7 +301,6 @@ void redistribute_delete(const struct route_node *rn, struct listnode *node, *nnode; struct zserv *client; vrf_id_t vrfid; - bool is_table_direct; if (old_re) vrfid = old_re->vrf_id; @@ -344,10 +363,12 @@ void redistribute_delete(const struct route_node *rn, * happy. */ assert(old_re); - is_table_direct = zebra_redistribute_is_table_direct(old_re); - zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, - client, rn, old_re, - is_table_direct); + if (zebra_redistribute_is_table_direct(old_re)) + redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn, + old_re); + else + zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, client, rn, + old_re, NULL); } } } @@ -383,8 +404,16 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS) } if (instance) { - if (!redist_check_instance(&client->mi_redist[afi][type], - instance)) { + if (type == ZEBRA_ROUTE_TABLE_DIRECT) { + struct redist_table_direct table = { + .vrf_id = zvrf->vrf->vrf_id, + .table_id = instance, + }; + if (!redist_lookup_table_direct(&client->mi_redist[afi][type], &table)) { + redist_add_table_direct(&client->mi_redist[afi][type], &table); + zebra_redistribute(client, type, instance, zvrf, afi); + } + } else if (!redist_check_instance(&client->mi_redist[afi][type], instance)) { redist_add_instance(&client->mi_redist[afi][type], instance); zebra_redistribute(client, type, instance, zvrf, afi); @@ -443,7 +472,13 @@ void zebra_redistribute_delete(ZAPI_HANDLER_ARGS) * themselves should keep track of the received routes from zebra and * withdraw them when necessary. */ - if (instance) + if (type == ZEBRA_ROUTE_TABLE_DIRECT) { + struct redist_table_direct table = { + .vrf_id = zvrf->vrf->vrf_id, + .table_id = instance, + }; + redist_del_table_direct(&client->mi_redist[afi][type], &table); + } else if (instance) redist_del_instance(&client->mi_redist[afi][type], instance); else vrf_bitmap_unset(&client->redist[afi][type], zvrf_id(zvrf)); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index b32882e858..9a60e32b65 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1295,6 +1295,12 @@ static int netlink_route_change_read_unicast_internal(struct nlmsghdr *h, flags, &p, (struct prefix_ipv6 *)&src_p, &nh, 0, table, metric, distance, true); + + if (nh.nh_label) + nexthop_del_labels(&nh); + + if (nh.nh_srv6) + nexthop_del_srv6_seg6(&nh); } else { /* XXX: need to compare the entire list of * nexthops here for NLM_F_APPEND stupidity */ @@ -2588,10 +2594,10 @@ ssize_t netlink_route_multipath_msg_encode(int cmd, struct zebra_dplane_ctx *ctx } } - if ((!fpm && kernel_nexthops_supported() - && (!proto_nexthops_only() - || is_proto_nhg(dplane_ctx_get_nhe_id(ctx), 0))) - || (fpm && force_nhg)) { + if ((!fpm && kernel_nexthops_supported() && + (!proto_nexthops_only() || is_proto_nhg(dplane_ctx_get_nhe_id(ctx), 0)) && + (!src_p || !src_p->prefixlen)) || + (fpm && force_nhg)) { /* Kernel supports nexthop objects */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("%s: %pFX nhg_id is %u", __func__, p, diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index ab55998af0..e9d554ba3d 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -509,9 +509,8 @@ int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp) return zserv_send_message(client, s); } -int zsend_redistribute_route(int cmd, struct zserv *client, - const struct route_node *rn, - const struct route_entry *re, bool is_table_direct) +int zsend_redistribute_route(int cmd, struct zserv *client, const struct route_node *rn, + const struct route_entry *re, vrf_id_t *to_vrf) { struct zapi_route api; struct zapi_nexthop *api_nh; @@ -527,9 +526,10 @@ int zsend_redistribute_route(int cmd, struct zserv *client, api.vrf_id = re->vrf_id; api.type = re->type; api.safi = SAFI_UNICAST; - if (is_table_direct) { + if (to_vrf != NULL) { api.instance = re->table; api.type = ZEBRA_ROUTE_TABLE_DIRECT; + api.vrf_id = *to_vrf; } else api.instance = re->instance; api.flags = re->flags; @@ -598,7 +598,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, /* Attributes. */ SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); - if (is_table_direct) + if (to_vrf != NULL) api.distance = ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT; else api.distance = re->distance; @@ -740,6 +740,10 @@ static int route_notify_internal(const struct route_node *rn, int type, struct zserv *client; struct stream *s; uint8_t blen; + const struct prefix *p, *src_p; + struct prefix src_dummy = {}; + + srcdest_rnode_prefixes(rn, &p, &src_p); client = zserv_find_client(type, instance); if (!client || !client->notify_owner) { @@ -771,9 +775,17 @@ static int route_notify_internal(const struct route_node *rn, int type, stream_putc(s, rn->p.family); - blen = prefix_blen(&rn->p); - stream_putc(s, rn->p.prefixlen); - stream_put(s, &rn->p.u.prefix, blen); + blen = prefix_blen(p); + stream_putc(s, p->prefixlen); + stream_put(s, &p->u.prefix, blen); + + if (!src_p) { + src_dummy.family = p->family; + src_p = &src_dummy; + } + blen = prefix_blen(src_p); + stream_putc(s, src_p->prefixlen); + stream_put(s, &src_p->u.prefix, blen); stream_putl(s, table_id); diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index a59ccc838b..29a5b69f18 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -51,10 +51,8 @@ extern void nbr_connected_delete_ipv6(struct interface *ifp, struct in6_addr *address); extern int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp); -extern int zsend_redistribute_route(int cmd, struct zserv *zclient, - const struct route_node *rn, - const struct route_entry *re, - bool is_table_direct); +extern int zsend_redistribute_route(int cmd, struct zserv *zclient, const struct route_node *rn, + const struct route_entry *re, vrf_id_t *to_vrf); extern int zsend_router_id_update(struct zserv *zclient, afi_t afi, struct prefix *p, vrf_id_t vrf_id); diff --git a/zebra/zebra_cli.c b/zebra/zebra_cli.c index ca53eb2eb3..bb79928326 100644 --- a/zebra/zebra_cli.c +++ b/zebra/zebra_cli.c @@ -1983,6 +1983,10 @@ static void lib_vrf_zebra_ipv6_router_id_cli_write(struct vty *vty, vty_out(vty, "ipv6 router-id %s\n", id); } +/* + * Both the v4 and v6 version of this command are now limiting the + * usage of System route types from being considered here at all + */ DEFPY_YANG (ip_protocol, ip_protocol_cmd, "[no] ip protocol " FRR_IP_PROTOCOL_MAP_STR_ZEBRA diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index b57c930154..9c252cc63c 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -7528,6 +7528,16 @@ static void dplane_thread_loop(struct event *event) if (!zdplane_info.dg_run) break; + /* + * The yield should only happen after a bit of work has been + * done but before we pull any new work off any provider + * queue to continue looping. This is a safe spot to + * do so. + */ + if (event_should_yield(event)) { + reschedule = true; + break; + } /* Locate next provider */ next_prov = dplane_prov_list_next(&zdplane_info.dg_providers, prov); @@ -7592,11 +7602,6 @@ static void dplane_thread_loop(struct event *event) zlog_debug("dplane dequeues %d completed work from provider %s", counter, dplane_provider_get_name(prov)); - if (event_should_yield(event)) { - reschedule = true; - break; - } - /* Locate next provider */ prov = next_prov; } @@ -7698,7 +7703,10 @@ static void zebra_dplane_init_internal(void) dplane_prov_list_init(&zdplane_info.dg_providers); - dplane_ctx_list_init(&zdplane_info.dg_update_list); + frr_with_mutex (&zdplane_info.dg_mutex) { + dplane_ctx_list_init(&zdplane_info.dg_update_list); + } + zns_info_list_init(&zdplane_info.dg_zns_list); zdplane_info.dg_updates_per_cycle = DPLANE_DEFAULT_NEW_WORK; diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 712b2534cc..f5141c8f23 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -572,8 +572,7 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) /* Nexthops should be in-order, so we simply compare them in-place */ for (nexthop1 = nhe1->nhg.nexthop, nexthop2 = nhe2->nhg.nexthop; nexthop1 && nexthop2; - nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) { - + nexthop1 = nexthop_next(nexthop1), nexthop2 = nexthop_next(nexthop2)) { if (!nhg_compare_nexthops(nexthop1, nexthop2)) return false; } @@ -608,8 +607,7 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) for (nexthop1 = nhe1->backup_info->nhe->nhg.nexthop, nexthop2 = nhe2->backup_info->nhe->nhg.nexthop; nexthop1 && nexthop2; - nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) { - + nexthop1 = nexthop_next(nexthop1), nexthop2 = nexthop_next(nexthop2)) { if (!nhg_compare_nexthops(nexthop1, nexthop2)) return false; } @@ -1762,7 +1760,8 @@ void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe) nhe->refcnt--; if (!zebra_router_in_shutdown() && nhe->refcnt <= 0 && - CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED) && + (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED) || + CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED)) && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_KEEP_AROUND)) { nhe->refcnt = 1; SET_FLAG(nhe->flags, NEXTHOP_GROUP_KEEP_AROUND); @@ -3382,7 +3381,17 @@ void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe, uint8_t type) void zebra_nhg_uninstall_kernel(struct nhg_hash_entry *nhe) { - if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)) { + /* + * Clearly if the nexthop group is installed we should + * remove it. Additionally If the nexthop is already + * QUEUED for installation, we should also just send + * a deletion down as well. We cannot necessarily pluck + * the installation out of the queue ( since it may have + * already been acted on, but not processed yet in the + * main pthread ). + */ + if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED) || + CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED)) { int ret = dplane_nexthop_delete(nhe); switch (ret) { diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 2881192eb7..a1c8cd3059 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1891,20 +1891,18 @@ struct route_node *rib_find_rn_from_ctx(const struct zebra_dplane_ctx *ctx) struct route_table *table = NULL; struct route_node *rn = NULL; const struct prefix *dest_pfx, *src_pfx; + uint32_t tableid = dplane_ctx_get_table(ctx); + vrf_id_t vrf_id = dplane_ctx_get_vrf(ctx); /* Locate rn and re(s) from ctx */ + table = zebra_vrf_lookup_table_with_table_id(dplane_ctx_get_afi(ctx), + dplane_ctx_get_safi(ctx), vrf_id, tableid); - table = zebra_vrf_lookup_table_with_table_id( - dplane_ctx_get_afi(ctx), dplane_ctx_get_safi(ctx), - dplane_ctx_get_vrf(ctx), dplane_ctx_get_table(ctx)); if (table == NULL) { if (IS_ZEBRA_DEBUG_DPLANE) { - zlog_debug( - "Failed to find route for ctx: no table for afi %d, safi %d, vrf %s(%u)", - dplane_ctx_get_afi(ctx), - dplane_ctx_get_safi(ctx), - vrf_id_to_name(dplane_ctx_get_vrf(ctx)), - dplane_ctx_get_vrf(ctx)); + zlog_debug("Failed to find route for ctx: no table for afi %d, safi %d, vrf %s(%u) table %u", + dplane_ctx_get_afi(ctx), dplane_ctx_get_safi(ctx), + vrf_id_to_name(vrf_id), vrf_id, tableid); } goto done; } @@ -2214,26 +2212,13 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) { struct route_node *rn = NULL; struct route_entry *re = NULL; - struct vrf *vrf; + struct vrf *vrf = vrf_lookup_by_id(dplane_ctx_get_vrf(ctx)); struct nexthop *nexthop; rib_dest_t *dest; bool fib_changed = false; bool debug_p = IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_RIB; int start_count, end_count; - vrf_id_t vrf_id; - int tableid; - - /* Locate vrf and route table - we must have one or the other */ - tableid = dplane_ctx_get_table(ctx); - vrf_id = dplane_ctx_get_vrf(ctx); - if (vrf_id == VRF_UNKNOWN) - vrf_id = zebra_vrf_lookup_by_table(tableid, - dplane_ctx_get_ns_id(ctx)); - else if (tableid == ZEBRA_ROUTE_TABLE_UNKNOWN) - tableid = zebra_vrf_lookup_tableid(vrf_id, - dplane_ctx_get_ns_id(ctx)); - - vrf = vrf_lookup_by_id(vrf_id); + uint32_t tableid = dplane_ctx_get_table(ctx); /* Locate rn and re(s) from ctx */ rn = rib_find_rn_from_ctx(ctx); @@ -4863,6 +4848,33 @@ void rib_close_table(struct route_table *table) } /* + * The context sent up from the dplane may be a context + * that has been generated by the zebra master pthread + * or it may be a context generated from a event in + * either the kernel dplane code or the fpm dplane + * code. In which case the tableid and vrfid may + * not be fully known and we have to figure it out + * when the context hits the master pthread. + * since this is the *starter* spot for that let + * us do a bit of work on each one to see if any + * massaging is needed + */ +static inline void zebra_rib_translate_ctx_from_dplane(struct zebra_dplane_ctx *ctx) +{ + uint32_t tableid = dplane_ctx_get_table(ctx); + vrf_id_t vrfid = dplane_ctx_get_vrf(ctx); + uint32_t nsid = dplane_ctx_get_ns_id(ctx); + enum dplane_op_e op = dplane_ctx_get_op(ctx); + + if (vrfid == VRF_UNKNOWN) + dplane_ctx_set_vrf(ctx, zebra_vrf_lookup_by_table(tableid, nsid)); + else if ((op == DPLANE_OP_ROUTE_INSTALL || op == DPLANE_OP_ROUTE_UPDATE || + op == DPLANE_OP_ROUTE_DELETE) && + tableid == ZEBRA_ROUTE_TABLE_UNKNOWN) + dplane_ctx_set_table(ctx, zebra_vrf_lookup_tableid(vrfid, nsid)); +} + +/* * Handle results from the dataplane system. Dequeue update context * structs, dispatch to appropriate internal handlers. */ @@ -4921,6 +4933,8 @@ static void rib_process_dplane_results(struct event *thread) } while (ctx) { + zebra_rib_translate_ctx_from_dplane(ctx); + #ifdef HAVE_SCRIPTING if (ret == 0) frrscript_call(fs, diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 73ffa09c16..2813f037a2 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -959,10 +959,11 @@ route_set_src(void *rule, const struct prefix *prefix, void *object) /* set src compilation. */ static void *route_set_src_compile(const char *arg) { - union g_addr src, *psrc; + union g_addr src = {}, *psrc; - if ((inet_pton(AF_INET6, arg, &src.ipv6) == 1) - || (inet_pton(AF_INET, arg, &src.ipv4) == 1)) { + /* IPv4 first, to ensure no garbage in the 12 unused bytes */ + if ((inet_pton(AF_INET, arg, &src.ipv4) == 1) || + (inet_pton(AF_INET6, arg, &src.ipv6) == 1)) { psrc = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(union g_addr)); *psrc = src; return psrc; diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index 624f60e815..6d228c5e24 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -1547,9 +1547,26 @@ static int get_srv6_sid_explicit(struct zebra_srv6_sid **sid, } if (ctx->behavior == ZEBRA_SEG6_LOCAL_ACTION_END) { - zlog_err("%s: invalid SM request arguments: explicit SID allocation not allowed for End/uN behavior", - __func__); - return -1; + zctx = zebra_srv6_sid_ctx_alloc(); + zctx->ctx = *ctx; + + *sid = zebra_srv6_sid_alloc(zctx, sid_value, locator, block, sid_func, + SRV6_SID_ALLOC_MODE_EXPLICIT); + if (!(*sid)) { + flog_err(EC_ZEBRA_SM_CANNOT_ASSIGN_SID, + "%s: failed to create SRv6 SID %s (%pI6)", __func__, + srv6_sid_ctx2str(buf, sizeof(buf), ctx), sid_value); + return -1; + } + (*sid)->ctx = zctx; + zctx->sid = *sid; + listnode_add(srv6->sids, zctx); + + if (IS_ZEBRA_DEBUG_SRV6) + zlog_debug("%s: allocated explicit SRv6 SID %pI6 for context %s", __func__, + &(*sid)->value, srv6_sid_ctx2str(buf, sizeof(buf), ctx)); + + return 1; } /* Allocate an explicit SID function for the SID */ diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 582d15627c..a1731712d3 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -1789,9 +1789,24 @@ DEFPY (show_route_detail, rib_dest_t *dest; bool network_found = false; bool show_ng = !!ng; + int idx = 0; + + /* + * Return error if V6 address/prefix is passed as an argument to + * "show ip route" cmd. + * + * When "show ip route <X:X::X:X|X:X::X:X/M>" is queried, + * argv[idx]->text will be set to "ipv6" but argv[idx]->arg will be set + * to "ip". + */ + if (argv_find(argv, argc, "ipv6", &idx) && !strcmp(argv[idx]->arg, "ip")) { + vty_out(vty, "%% Cannot specify IPv6 address/prefix for IPv4 table\n"); + return CMD_WARNING; + } if (address_str) prefix_str = address_str; + if (str2prefix(prefix_str, &p) < 0) { vty_out(vty, "%% Malformed address\n"); return CMD_WARNING; diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index c60eeab946..04585f6312 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -1584,7 +1584,7 @@ static void svd_nh_del_terminate(void *ptr) struct zebra_neigh *n = ptr; n->refcnt = 0; - svd_nh_del(n); + XFREE(MTYPE_L3NEIGH, n); } diff --git a/zebra/zserv.c b/zebra/zserv.c index 7ef3582329..6965c285cd 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -937,6 +937,10 @@ void zserv_close(void) /* * Open zebra's ZAPI listener socket. This is done early during startup, * before zebra is ready to listen and accept client connections. + * + * This function should only ever be called from the startup pthread + * from main.c. If it is called multiple times it will cause problems + * because it causes the zsock global variable to be setup. */ void zserv_open(const char *path) { diff --git a/zebra/zserv.h b/zebra/zserv.h index ce47ef19fa..1ff7ccd981 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -262,6 +262,9 @@ extern void zserv_close(void); * * path * where to place the Unix domain socket + * + * This function *should* only ever be called from + * main() and only every from 1 pthread. */ extern void zserv_open(const char *path); |
